diff --git a/python/langsmith_api/__init__.py b/python/langsmith_api/__init__.py new file mode 100644 index 000000000..cb42b500f --- /dev/null +++ b/python/langsmith_api/__init__.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import typing as _t + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given +from ._utils import file_from_path +from ._client import ( + Client, + Stream, + Timeout, + Langsmith, + Transport, + AsyncClient, + AsyncStream, + AsyncLangsmith, + RequestOptions, +) +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + ConflictError, + NotFoundError, + APIStatusError, + LangsmithError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "not_given", + "Omit", + "omit", + "LangsmithError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "Langsmith", + "AsyncLangsmith", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", +] + +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# langsmith_api._exceptions.NotFoundError -> langsmith_api.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "langsmith_api" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass diff --git a/python/langsmith_api/_base_client.py b/python/langsmith_api/_base_client.py new file mode 100644 index 000000000..a19ce50bb --- /dev/null +++ b/python/langsmith_api/_base_client.py @@ -0,0 +1,2131 @@ +from __future__ import annotations + +import sys +import json +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import warnings +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Literal, override, get_origin + +import anyio +import httpx +import distro +import pydantic +from httpx import URL +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + ResponseT, + AnyMapping, + PostParser, + BinaryTypes, + RequestFiles, + HttpxSendArgs, + RequestOptions, + AsyncBinaryTypes, + HttpxRequestFiles, + ModelBuilderProtocol, + not_given, +) +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V1, model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) +from ._constants import ( + DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, + DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, + RAW_RESPONSE_HEADER, + OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, +) +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder +from ._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) +from ._utils._json import openapi_dumps + +log: logging.Logger = logging.getLogger(__name__) + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG +else: + try: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necessary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + json: Body | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: ... + + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, + ) -> None: + self.url = url + self.json = json + self.params = params + + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" + return f"{self.__class__.__name__}(params={self.params})" + + +class BasePage(GenericModel, Generic[_T]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[_T] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: ... + + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] + ... + + def _params_from_url(self, url: URL) -> httpx.QueryParams: + # TODO: do we have to preprocess params here? + return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[_T], Generic[_T]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[_T], + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[_T]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[_T, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[_T], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[_T]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[_T], Generic[_T]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[_T], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[_T]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(URL(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + self._platform: Platform | None = None + + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `langsmith_api.DEFAULT_MAX_RETRIES`" + ) + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _make_status_error_from_response( + self, + response: httpx.Response, + ) -> APIStatusError: + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_mappings(self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + # headers are case-insensitive while dictionaries are not. + headers = httpx.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key + + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + + return headers + + def _prepare_url(self, url: str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx.Request: + if log.isEnabledFor(logging.DEBUG): + log.debug( + "Request options: %s", + model_dump( + options, + exclude_unset=True, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): + kwargs["content"] = json_data + elif not files: + # Don't set content when JSON is sent as multipart/form-data, + # since httpx's content param overrides other body arguments + kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=headers, + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + method=options.method, + url=prepared_url, + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + + return serialized + + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to + + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) + + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is object: + return cast(ResponseT, data) + + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self.auth_headers, + **self._custom_headers, + } + + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(self) -> URL: + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) + + def platform_headers(self) -> Dict[str, str]: + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) + + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # First, try the non-standard `retry-after-ms` header for milliseconds, + # which is more precise than integer-seconds `retry-after` + try: + retry_ms_header = response_headers.get("retry-after-ms", None) + return float(retry_ms_header) / 1000 + except (TypeError, ValueError): + pass + + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). + retry_header = response_headers.get("retry-after") + try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it + return float(retry_header) + except (TypeError, ValueError): + pass + + # Last, try parsing `retry-after` as a date. + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + return float(retry_date - time.time()) + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + + # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and 0 < retry_after <= 60: + return retry_after + + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) + + # Apply some jitter, plus-or-minus half a second. + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx.Response) -> bool: + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") + return True + if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") + return False + + # Retry on request timeouts. + if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on lock timeouts. + if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on rate limits. + if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry internal errors. + if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) + return True + + log.debug("Not retrying") + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + self.close() + except Exception: + pass + + +class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): + _client: httpx.Client + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + + super().__init__( + version=version, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + base_url=base_url, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or SyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + time.sleep(timeout) + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + + def _request_api_list( + self, + model: Type[object], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: BinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + +class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): + _client: httpx.AsyncClient + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + + super().__init__( + version=version, + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or AsyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return await self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + await anyio.sleep(timeout) + + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + + def _request_api_list( + self, + model: Type[_T], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[_T, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, + ) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + content: AsyncBinaryTypes | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/python/langsmith_api/_client.py b/python/langsmith_api/_client.py new file mode 100644 index 000000000..3d63d0b93 --- /dev/null +++ b/python/langsmith_api/_client.py @@ -0,0 +1,1032 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import TYPE_CHECKING, Any, Mapping +from typing_extensions import Self, override + +import httpx + +from . import _exceptions +from ._qs import Querystring +from ._types import ( + Omit, + Headers, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, + not_given, +) +from ._utils import ( + is_given, + is_mapping_t, + get_async_library, +) +from ._compat import cached_property +from ._version import __version__ +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._exceptions import APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) + +if TYPE_CHECKING: + from .resources import ( + info, + runs, + repos, + public, + commits, + datasets, + examples, + feedback, + sessions, + settings, + sandboxes, + evaluators, + workspaces, + annotation_queues, + ) + from .resources.info import InfoResource, AsyncInfoResource + from .resources.runs import RunsResource, AsyncRunsResource + from .resources.commits import CommitsResource, AsyncCommitsResource + from .resources.settings import SettingsResource, AsyncSettingsResource + from .resources.evaluators import EvaluatorsResource, AsyncEvaluatorsResource + from .resources.workspaces import WorkspacesResource, AsyncWorkspacesResource + from .resources.repos.repos import ReposResource, AsyncReposResource + from .resources.public.public import PublicResource, AsyncPublicResource + from .resources.datasets.datasets import DatasetsResource, AsyncDatasetsResource + from .resources.examples.examples import ExamplesResource, AsyncExamplesResource + from .resources.feedback.feedback import FeedbackResource, AsyncFeedbackResource + from .resources.sessions.sessions import SessionsResource, AsyncSessionsResource + from .resources.sandboxes.sandboxes import SandboxesResource, AsyncSandboxesResource + from .resources.annotation_queues.annotation_queues import AnnotationQueuesResource, AsyncAnnotationQueuesResource + +__all__ = [ + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "Langsmith", + "AsyncLangsmith", + "Client", + "AsyncClient", +] + + +class Langsmith(SyncAPIClient): + # client options + api_key: str | None + tenant_id: str | None + + def __init__( + self, + *, + api_key: str | None = None, + tenant_id: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous Langsmith client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `LANGSMITH_API_KEY` + - `tenant_id` from `LANGSMITH_TENANT_ID` + """ + if api_key is None: + api_key = os.environ.get("LANGSMITH_API_KEY") + self.api_key = api_key + + if tenant_id is None: + tenant_id = os.environ.get("LANGSMITH_TENANT_ID") + self.tenant_id = tenant_id + + if base_url is None: + base_url = os.environ.get("LANGCHAIN_BASE_URL") + if base_url is None: + base_url = f"https://api.smith.langchain.com/" + + custom_headers_env = os.environ.get("LANGCHAIN_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + @cached_property + def sessions(self) -> SessionsResource: + from .resources.sessions import SessionsResource + + return SessionsResource(self) + + @cached_property + def examples(self) -> ExamplesResource: + from .resources.examples import ExamplesResource + + return ExamplesResource(self) + + @cached_property + def datasets(self) -> DatasetsResource: + from .resources.datasets import DatasetsResource + + return DatasetsResource(self) + + @cached_property + def runs(self) -> RunsResource: + from .resources.runs import RunsResource + + return RunsResource(self) + + @cached_property + def evaluators(self) -> EvaluatorsResource: + from .resources.evaluators import EvaluatorsResource + + return EvaluatorsResource(self) + + @cached_property + def feedback(self) -> FeedbackResource: + from .resources.feedback import FeedbackResource + + return FeedbackResource(self) + + @cached_property + def public(self) -> PublicResource: + from .resources.public import PublicResource + + return PublicResource(self) + + @cached_property + def annotation_queues(self) -> AnnotationQueuesResource: + from .resources.annotation_queues import AnnotationQueuesResource + + return AnnotationQueuesResource(self) + + @cached_property + def info(self) -> InfoResource: + from .resources.info import InfoResource + + return InfoResource(self) + + @cached_property + def workspaces(self) -> WorkspacesResource: + from .resources.workspaces import WorkspacesResource + + return WorkspacesResource(self) + + @cached_property + def repos(self) -> ReposResource: + from .resources.repos import ReposResource + + return ReposResource(self) + + @cached_property + def commits(self) -> CommitsResource: + from .resources.commits import CommitsResource + + return CommitsResource(self) + + @cached_property + def settings(self) -> SettingsResource: + from .resources.settings import SettingsResource + + return SettingsResource(self) + + @cached_property + def sandboxes(self) -> SandboxesResource: + from .resources.sandboxes import SandboxesResource + + return SandboxesResource(self) + + @cached_property + def with_raw_response(self) -> LangsmithWithRawResponse: + return LangsmithWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LangsmithWithStreamedResponse: + return LangsmithWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + return {**self._api_key, **self._tenant_id} + + @property + def _api_key(self) -> dict[str, str]: + api_key = self.api_key + if api_key is None: + return {} + return {"X-API-Key": api_key} + + @property + def _tenant_id(self) -> dict[str, str]: + tenant_id = self.tenant_id + if tenant_id is None: + return {} + return {"X-Tenant-Id": tenant_id} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("X-API-Key") or isinstance(custom_headers.get("X-API-Key"), Omit): + return + + if headers.get("X-Tenant-Id") or isinstance(custom_headers.get("X-Tenant-Id"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or tenant_id to be set. Or for one of the `X-API-Key` or `X-Tenant-Id` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + api_key: str | None = None, + tenant_id: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + tenant_id=tenant_id or self.tenant_id, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncLangsmith(AsyncAPIClient): + # client options + api_key: str | None + tenant_id: str | None + + def __init__( + self, + *, + api_key: str | None = None, + tenant_id: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async AsyncLangsmith client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `LANGSMITH_API_KEY` + - `tenant_id` from `LANGSMITH_TENANT_ID` + """ + if api_key is None: + api_key = os.environ.get("LANGSMITH_API_KEY") + self.api_key = api_key + + if tenant_id is None: + tenant_id = os.environ.get("LANGSMITH_TENANT_ID") + self.tenant_id = tenant_id + + if base_url is None: + base_url = os.environ.get("LANGCHAIN_BASE_URL") + if base_url is None: + base_url = f"https://api.smith.langchain.com/" + + custom_headers_env = os.environ.get("LANGCHAIN_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + @cached_property + def sessions(self) -> AsyncSessionsResource: + from .resources.sessions import AsyncSessionsResource + + return AsyncSessionsResource(self) + + @cached_property + def examples(self) -> AsyncExamplesResource: + from .resources.examples import AsyncExamplesResource + + return AsyncExamplesResource(self) + + @cached_property + def datasets(self) -> AsyncDatasetsResource: + from .resources.datasets import AsyncDatasetsResource + + return AsyncDatasetsResource(self) + + @cached_property + def runs(self) -> AsyncRunsResource: + from .resources.runs import AsyncRunsResource + + return AsyncRunsResource(self) + + @cached_property + def evaluators(self) -> AsyncEvaluatorsResource: + from .resources.evaluators import AsyncEvaluatorsResource + + return AsyncEvaluatorsResource(self) + + @cached_property + def feedback(self) -> AsyncFeedbackResource: + from .resources.feedback import AsyncFeedbackResource + + return AsyncFeedbackResource(self) + + @cached_property + def public(self) -> AsyncPublicResource: + from .resources.public import AsyncPublicResource + + return AsyncPublicResource(self) + + @cached_property + def annotation_queues(self) -> AsyncAnnotationQueuesResource: + from .resources.annotation_queues import AsyncAnnotationQueuesResource + + return AsyncAnnotationQueuesResource(self) + + @cached_property + def info(self) -> AsyncInfoResource: + from .resources.info import AsyncInfoResource + + return AsyncInfoResource(self) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResource: + from .resources.workspaces import AsyncWorkspacesResource + + return AsyncWorkspacesResource(self) + + @cached_property + def repos(self) -> AsyncReposResource: + from .resources.repos import AsyncReposResource + + return AsyncReposResource(self) + + @cached_property + def commits(self) -> AsyncCommitsResource: + from .resources.commits import AsyncCommitsResource + + return AsyncCommitsResource(self) + + @cached_property + def settings(self) -> AsyncSettingsResource: + from .resources.settings import AsyncSettingsResource + + return AsyncSettingsResource(self) + + @cached_property + def sandboxes(self) -> AsyncSandboxesResource: + from .resources.sandboxes import AsyncSandboxesResource + + return AsyncSandboxesResource(self) + + @cached_property + def with_raw_response(self) -> AsyncLangsmithWithRawResponse: + return AsyncLangsmithWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLangsmithWithStreamedResponse: + return AsyncLangsmithWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + return {**self._api_key, **self._tenant_id} + + @property + def _api_key(self) -> dict[str, str]: + api_key = self.api_key + if api_key is None: + return {} + return {"X-API-Key": api_key} + + @property + def _tenant_id(self) -> dict[str, str]: + tenant_id = self.tenant_id + if tenant_id is None: + return {} + return {"X-Tenant-Id": tenant_id} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("X-API-Key") or isinstance(custom_headers.get("X-API-Key"), Omit): + return + + if headers.get("X-Tenant-Id") or isinstance(custom_headers.get("X-Tenant-Id"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or tenant_id to be set. Or for one of the `X-API-Key` or `X-Tenant-Id` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + api_key: str | None = None, + tenant_id: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + tenant_id=tenant_id or self.tenant_id, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class LangsmithWithRawResponse: + _client: Langsmith + + def __init__(self, client: Langsmith) -> None: + self._client = client + + @cached_property + def sessions(self) -> sessions.SessionsResourceWithRawResponse: + from .resources.sessions import SessionsResourceWithRawResponse + + return SessionsResourceWithRawResponse(self._client.sessions) + + @cached_property + def examples(self) -> examples.ExamplesResourceWithRawResponse: + from .resources.examples import ExamplesResourceWithRawResponse + + return ExamplesResourceWithRawResponse(self._client.examples) + + @cached_property + def datasets(self) -> datasets.DatasetsResourceWithRawResponse: + from .resources.datasets import DatasetsResourceWithRawResponse + + return DatasetsResourceWithRawResponse(self._client.datasets) + + @cached_property + def runs(self) -> runs.RunsResourceWithRawResponse: + from .resources.runs import RunsResourceWithRawResponse + + return RunsResourceWithRawResponse(self._client.runs) + + @cached_property + def evaluators(self) -> evaluators.EvaluatorsResourceWithRawResponse: + from .resources.evaluators import EvaluatorsResourceWithRawResponse + + return EvaluatorsResourceWithRawResponse(self._client.evaluators) + + @cached_property + def feedback(self) -> feedback.FeedbackResourceWithRawResponse: + from .resources.feedback import FeedbackResourceWithRawResponse + + return FeedbackResourceWithRawResponse(self._client.feedback) + + @cached_property + def public(self) -> public.PublicResourceWithRawResponse: + from .resources.public import PublicResourceWithRawResponse + + return PublicResourceWithRawResponse(self._client.public) + + @cached_property + def annotation_queues(self) -> annotation_queues.AnnotationQueuesResourceWithRawResponse: + from .resources.annotation_queues import AnnotationQueuesResourceWithRawResponse + + return AnnotationQueuesResourceWithRawResponse(self._client.annotation_queues) + + @cached_property + def info(self) -> info.InfoResourceWithRawResponse: + from .resources.info import InfoResourceWithRawResponse + + return InfoResourceWithRawResponse(self._client.info) + + @cached_property + def workspaces(self) -> workspaces.WorkspacesResourceWithRawResponse: + from .resources.workspaces import WorkspacesResourceWithRawResponse + + return WorkspacesResourceWithRawResponse(self._client.workspaces) + + @cached_property + def repos(self) -> repos.ReposResourceWithRawResponse: + from .resources.repos import ReposResourceWithRawResponse + + return ReposResourceWithRawResponse(self._client.repos) + + @cached_property + def commits(self) -> commits.CommitsResourceWithRawResponse: + from .resources.commits import CommitsResourceWithRawResponse + + return CommitsResourceWithRawResponse(self._client.commits) + + @cached_property + def settings(self) -> settings.SettingsResourceWithRawResponse: + from .resources.settings import SettingsResourceWithRawResponse + + return SettingsResourceWithRawResponse(self._client.settings) + + @cached_property + def sandboxes(self) -> sandboxes.SandboxesResourceWithRawResponse: + from .resources.sandboxes import SandboxesResourceWithRawResponse + + return SandboxesResourceWithRawResponse(self._client.sandboxes) + + +class AsyncLangsmithWithRawResponse: + _client: AsyncLangsmith + + def __init__(self, client: AsyncLangsmith) -> None: + self._client = client + + @cached_property + def sessions(self) -> sessions.AsyncSessionsResourceWithRawResponse: + from .resources.sessions import AsyncSessionsResourceWithRawResponse + + return AsyncSessionsResourceWithRawResponse(self._client.sessions) + + @cached_property + def examples(self) -> examples.AsyncExamplesResourceWithRawResponse: + from .resources.examples import AsyncExamplesResourceWithRawResponse + + return AsyncExamplesResourceWithRawResponse(self._client.examples) + + @cached_property + def datasets(self) -> datasets.AsyncDatasetsResourceWithRawResponse: + from .resources.datasets import AsyncDatasetsResourceWithRawResponse + + return AsyncDatasetsResourceWithRawResponse(self._client.datasets) + + @cached_property + def runs(self) -> runs.AsyncRunsResourceWithRawResponse: + from .resources.runs import AsyncRunsResourceWithRawResponse + + return AsyncRunsResourceWithRawResponse(self._client.runs) + + @cached_property + def evaluators(self) -> evaluators.AsyncEvaluatorsResourceWithRawResponse: + from .resources.evaluators import AsyncEvaluatorsResourceWithRawResponse + + return AsyncEvaluatorsResourceWithRawResponse(self._client.evaluators) + + @cached_property + def feedback(self) -> feedback.AsyncFeedbackResourceWithRawResponse: + from .resources.feedback import AsyncFeedbackResourceWithRawResponse + + return AsyncFeedbackResourceWithRawResponse(self._client.feedback) + + @cached_property + def public(self) -> public.AsyncPublicResourceWithRawResponse: + from .resources.public import AsyncPublicResourceWithRawResponse + + return AsyncPublicResourceWithRawResponse(self._client.public) + + @cached_property + def annotation_queues(self) -> annotation_queues.AsyncAnnotationQueuesResourceWithRawResponse: + from .resources.annotation_queues import AsyncAnnotationQueuesResourceWithRawResponse + + return AsyncAnnotationQueuesResourceWithRawResponse(self._client.annotation_queues) + + @cached_property + def info(self) -> info.AsyncInfoResourceWithRawResponse: + from .resources.info import AsyncInfoResourceWithRawResponse + + return AsyncInfoResourceWithRawResponse(self._client.info) + + @cached_property + def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithRawResponse: + from .resources.workspaces import AsyncWorkspacesResourceWithRawResponse + + return AsyncWorkspacesResourceWithRawResponse(self._client.workspaces) + + @cached_property + def repos(self) -> repos.AsyncReposResourceWithRawResponse: + from .resources.repos import AsyncReposResourceWithRawResponse + + return AsyncReposResourceWithRawResponse(self._client.repos) + + @cached_property + def commits(self) -> commits.AsyncCommitsResourceWithRawResponse: + from .resources.commits import AsyncCommitsResourceWithRawResponse + + return AsyncCommitsResourceWithRawResponse(self._client.commits) + + @cached_property + def settings(self) -> settings.AsyncSettingsResourceWithRawResponse: + from .resources.settings import AsyncSettingsResourceWithRawResponse + + return AsyncSettingsResourceWithRawResponse(self._client.settings) + + @cached_property + def sandboxes(self) -> sandboxes.AsyncSandboxesResourceWithRawResponse: + from .resources.sandboxes import AsyncSandboxesResourceWithRawResponse + + return AsyncSandboxesResourceWithRawResponse(self._client.sandboxes) + + +class LangsmithWithStreamedResponse: + _client: Langsmith + + def __init__(self, client: Langsmith) -> None: + self._client = client + + @cached_property + def sessions(self) -> sessions.SessionsResourceWithStreamingResponse: + from .resources.sessions import SessionsResourceWithStreamingResponse + + return SessionsResourceWithStreamingResponse(self._client.sessions) + + @cached_property + def examples(self) -> examples.ExamplesResourceWithStreamingResponse: + from .resources.examples import ExamplesResourceWithStreamingResponse + + return ExamplesResourceWithStreamingResponse(self._client.examples) + + @cached_property + def datasets(self) -> datasets.DatasetsResourceWithStreamingResponse: + from .resources.datasets import DatasetsResourceWithStreamingResponse + + return DatasetsResourceWithStreamingResponse(self._client.datasets) + + @cached_property + def runs(self) -> runs.RunsResourceWithStreamingResponse: + from .resources.runs import RunsResourceWithStreamingResponse + + return RunsResourceWithStreamingResponse(self._client.runs) + + @cached_property + def evaluators(self) -> evaluators.EvaluatorsResourceWithStreamingResponse: + from .resources.evaluators import EvaluatorsResourceWithStreamingResponse + + return EvaluatorsResourceWithStreamingResponse(self._client.evaluators) + + @cached_property + def feedback(self) -> feedback.FeedbackResourceWithStreamingResponse: + from .resources.feedback import FeedbackResourceWithStreamingResponse + + return FeedbackResourceWithStreamingResponse(self._client.feedback) + + @cached_property + def public(self) -> public.PublicResourceWithStreamingResponse: + from .resources.public import PublicResourceWithStreamingResponse + + return PublicResourceWithStreamingResponse(self._client.public) + + @cached_property + def annotation_queues(self) -> annotation_queues.AnnotationQueuesResourceWithStreamingResponse: + from .resources.annotation_queues import AnnotationQueuesResourceWithStreamingResponse + + return AnnotationQueuesResourceWithStreamingResponse(self._client.annotation_queues) + + @cached_property + def info(self) -> info.InfoResourceWithStreamingResponse: + from .resources.info import InfoResourceWithStreamingResponse + + return InfoResourceWithStreamingResponse(self._client.info) + + @cached_property + def workspaces(self) -> workspaces.WorkspacesResourceWithStreamingResponse: + from .resources.workspaces import WorkspacesResourceWithStreamingResponse + + return WorkspacesResourceWithStreamingResponse(self._client.workspaces) + + @cached_property + def repos(self) -> repos.ReposResourceWithStreamingResponse: + from .resources.repos import ReposResourceWithStreamingResponse + + return ReposResourceWithStreamingResponse(self._client.repos) + + @cached_property + def commits(self) -> commits.CommitsResourceWithStreamingResponse: + from .resources.commits import CommitsResourceWithStreamingResponse + + return CommitsResourceWithStreamingResponse(self._client.commits) + + @cached_property + def settings(self) -> settings.SettingsResourceWithStreamingResponse: + from .resources.settings import SettingsResourceWithStreamingResponse + + return SettingsResourceWithStreamingResponse(self._client.settings) + + @cached_property + def sandboxes(self) -> sandboxes.SandboxesResourceWithStreamingResponse: + from .resources.sandboxes import SandboxesResourceWithStreamingResponse + + return SandboxesResourceWithStreamingResponse(self._client.sandboxes) + + +class AsyncLangsmithWithStreamedResponse: + _client: AsyncLangsmith + + def __init__(self, client: AsyncLangsmith) -> None: + self._client = client + + @cached_property + def sessions(self) -> sessions.AsyncSessionsResourceWithStreamingResponse: + from .resources.sessions import AsyncSessionsResourceWithStreamingResponse + + return AsyncSessionsResourceWithStreamingResponse(self._client.sessions) + + @cached_property + def examples(self) -> examples.AsyncExamplesResourceWithStreamingResponse: + from .resources.examples import AsyncExamplesResourceWithStreamingResponse + + return AsyncExamplesResourceWithStreamingResponse(self._client.examples) + + @cached_property + def datasets(self) -> datasets.AsyncDatasetsResourceWithStreamingResponse: + from .resources.datasets import AsyncDatasetsResourceWithStreamingResponse + + return AsyncDatasetsResourceWithStreamingResponse(self._client.datasets) + + @cached_property + def runs(self) -> runs.AsyncRunsResourceWithStreamingResponse: + from .resources.runs import AsyncRunsResourceWithStreamingResponse + + return AsyncRunsResourceWithStreamingResponse(self._client.runs) + + @cached_property + def evaluators(self) -> evaluators.AsyncEvaluatorsResourceWithStreamingResponse: + from .resources.evaluators import AsyncEvaluatorsResourceWithStreamingResponse + + return AsyncEvaluatorsResourceWithStreamingResponse(self._client.evaluators) + + @cached_property + def feedback(self) -> feedback.AsyncFeedbackResourceWithStreamingResponse: + from .resources.feedback import AsyncFeedbackResourceWithStreamingResponse + + return AsyncFeedbackResourceWithStreamingResponse(self._client.feedback) + + @cached_property + def public(self) -> public.AsyncPublicResourceWithStreamingResponse: + from .resources.public import AsyncPublicResourceWithStreamingResponse + + return AsyncPublicResourceWithStreamingResponse(self._client.public) + + @cached_property + def annotation_queues(self) -> annotation_queues.AsyncAnnotationQueuesResourceWithStreamingResponse: + from .resources.annotation_queues import AsyncAnnotationQueuesResourceWithStreamingResponse + + return AsyncAnnotationQueuesResourceWithStreamingResponse(self._client.annotation_queues) + + @cached_property + def info(self) -> info.AsyncInfoResourceWithStreamingResponse: + from .resources.info import AsyncInfoResourceWithStreamingResponse + + return AsyncInfoResourceWithStreamingResponse(self._client.info) + + @cached_property + def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithStreamingResponse: + from .resources.workspaces import AsyncWorkspacesResourceWithStreamingResponse + + return AsyncWorkspacesResourceWithStreamingResponse(self._client.workspaces) + + @cached_property + def repos(self) -> repos.AsyncReposResourceWithStreamingResponse: + from .resources.repos import AsyncReposResourceWithStreamingResponse + + return AsyncReposResourceWithStreamingResponse(self._client.repos) + + @cached_property + def commits(self) -> commits.AsyncCommitsResourceWithStreamingResponse: + from .resources.commits import AsyncCommitsResourceWithStreamingResponse + + return AsyncCommitsResourceWithStreamingResponse(self._client.commits) + + @cached_property + def settings(self) -> settings.AsyncSettingsResourceWithStreamingResponse: + from .resources.settings import AsyncSettingsResourceWithStreamingResponse + + return AsyncSettingsResourceWithStreamingResponse(self._client.settings) + + @cached_property + def sandboxes(self) -> sandboxes.AsyncSandboxesResourceWithStreamingResponse: + from .resources.sandboxes import AsyncSandboxesResourceWithStreamingResponse + + return AsyncSandboxesResourceWithStreamingResponse(self._client.sandboxes) + + +Client = Langsmith + +AsyncClient = AsyncLangsmith diff --git a/python/langsmith_api/_compat.py b/python/langsmith_api/_compat.py new file mode 100644 index 000000000..e6690a4f2 --- /dev/null +++ b/python/langsmith_api/_compat.py @@ -0,0 +1,226 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal, TypedDict + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2, v3 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") + +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from ._utils import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + parse_date as parse_date, + is_typeddict as is_typeddict, + parse_datetime as parse_datetime, + is_literal_type as is_literal_type, + ) + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V1: + # TODO: provide an error message here? + ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V1: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V1: + return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) + + +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", + by_alias: bool | None = None, +) -> dict[str, Any]: + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=True if PYDANTIC_V1 else warnings, + **kwargs, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/python/langsmith_api/_constants.py b/python/langsmith_api/_constants.py new file mode 100644 index 000000000..ef07eae90 --- /dev/null +++ b/python/langsmith_api/_constants.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 1.5 minutes +DEFAULT_TIMEOUT = httpx.Timeout(timeout=90, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 16.0 diff --git a/python/langsmith_api/_exceptions.py b/python/langsmith_api/_exceptions.py new file mode 100644 index 000000000..e10fc2201 --- /dev/null +++ b/python/langsmith_api/_exceptions.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class LangsmithError(Exception): + pass + + +class APIError(LangsmithError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 + super().__init__(message) + self.request = request + self.message = message + self.body = body + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/python/langsmith_api/_files.py b/python/langsmith_api/_files.py new file mode 100644 index 000000000..591d7bb07 --- /dev/null +++ b/python/langsmith_api/_files.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t + +_T = TypeVar("_T") + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/stainless-sdks/langchain-python/tree/main#file-uploads" + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file + + +def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: + """Copy only the containers along the given paths. + + Used to guard against mutation by extract_files without copying the entire structure. + Only dicts and lists that lie on a path are copied; everything else + is returned by reference. + + For example, given paths=[["foo", "files", "file"]] and the structure: + { + "foo": { + "bar": {"baz": {}}, + "files": {"file": } + } + } + The root dict, "foo", and "files" are copied (they lie on the path). + "bar" and "baz" are returned by reference (off the path). + """ + return _deepcopy_with_paths(item, paths, 0) + + +def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: + if not paths: + return item + if is_mapping(item): + key_to_paths: dict[str, list[Sequence[str]]] = {} + for path in paths: + if index < len(path): + key_to_paths.setdefault(path[index], []).append(path) + + # if no path continues through this mapping, it won't be mutated and copying it is redundant + if not key_to_paths: + return item + + result = dict(item) + for key, subpaths in key_to_paths.items(): + if key in result: + result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) + return cast(_T, result) + if is_list(item): + array_paths = [path for path in paths if index < len(path) and path[index] == ""] + + # if no path expects a list here, nothing will be mutated inside it - return by reference + if not array_paths: + return cast(_T, item) + return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) + return item diff --git a/python/langsmith_api/_models.py b/python/langsmith_api/_models.py new file mode 100644 index 000000000..8c5ab2602 --- /dev/null +++ b/python/langsmith_api/_models.py @@ -0,0 +1,952 @@ +from __future__ import annotations + +import os +import inspect +import weakref +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) +from datetime import date, datetime +from typing_extensions import ( + List, + Unpack, + Literal, + ClassVar, + Protocol, + Required, + Annotated, + ParamSpec, + TypeAlias, + TypedDict, + TypeGuard, + final, + override, + runtime_checkable, +) + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) +from ._compat import ( + PYDANTIC_V1, + ConfigDict, + GenericModel as BaseGenericModel, + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V1: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = __cls.__new__(__cls) + fields_values: dict[str, object] = {} + + config = get_model_config(__cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(__cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + extra_field_type = _get_extra_fields_type(__cls) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: + _fields_set.add(key) + fields_values[key] = parsed + else: + _extra[key] = parsed + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V1: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if PYDANTIC_V1: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specific pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A dictionary representation of the model. + """ + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V1: + type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=cast("type[object]", original_type or type_), value=value) + except Exception: + pass + + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + _, items_type = get_args(type_) # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): + if isinstance(entry, str): + mapping[entry] = variant + else: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + DISCRIMINATOR_CACHE.setdefault(union, details) + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + + if schema["type"] != "model": + return None + + schema = cast("ModelSchema", schema) + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +# our use of subclassing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if not PYDANTIC_V1: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__="5").__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] + json_data: Body + extra_json: AnyMapping + follow_redirects: bool + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None + + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V1: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/python/langsmith_api/_qs.py b/python/langsmith_api/_qs.py new file mode 100644 index 000000000..4127c19c6 --- /dev/null +++ b/python/langsmith_api/_qs.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import get_args + +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given +from ._utils import flatten + +_T = TypeVar("_T") + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/python/langsmith_api/_resource.py b/python/langsmith_api/_resource.py new file mode 100644 index 000000000..9c6e46848 --- /dev/null +++ b/python/langsmith_api/_resource.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +import anyio + +if TYPE_CHECKING: + from ._client import Langsmith, AsyncLangsmith + + +class SyncAPIResource: + _client: Langsmith + + def __init__(self, client: Langsmith) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncLangsmith + + def __init__(self, client: AsyncLangsmith) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) diff --git a/python/langsmith_api/_response.py b/python/langsmith_api/_response.py new file mode 100644 index 000000000..6807bdc92 --- /dev/null +++ b/python/langsmith_api/_response.py @@ -0,0 +1,835 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, get_origin + +import anyio +import httpx +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._models import BaseModel, is_basemodel +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import LangsmithError, APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") + +log: logging.Logger = logging.getLogger(__name__) + + +class BaseAPIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _is_sse_stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._is_sse_stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + """Returns the httpx Request instance associated with the current response.""" + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx.URL: + """Returns the URL for which the request was made.""" + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + options=self._options, + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == bytes: + return cast(R, response.content) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError( + "Pydantic models must subclass our base model type, e.g. `from langsmith_api import BaseModel`" + ) + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if not content_type.endswith("json"): + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + +class APIResponse(BaseAPIResponse[R]): + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from langsmith_api import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except httpx.StreamConsumed as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + @overload + async def parse(self, *, to: type[_T]) -> _T: ... + + @overload + async def parse(self) -> R: ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from langsmith_api import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except httpx.StreamConsumed as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `langsmith_api._streaming` for reference", + ) + + +class StreamAlreadyConsumed(LangsmithError): + """ + Attempted to read or stream content, but the content has already + been streamed. + + This can happen if you use a method like `.iter_lines()` and then attempt + to read th entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/python/langsmith_api/_streaming.py b/python/langsmith_api/_streaming.py new file mode 100644 index 000000000..5ced19352 --- /dev/null +++ b/python/langsmith_api/_streaming.py @@ -0,0 +1,338 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx + +from ._utils import extract_type_var_from_base + +if TYPE_CHECKING: + from ._client import Langsmith, AsyncLangsmith + from ._models import FinalRequestOptions + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + _options: Optional[FinalRequestOptions] = None + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: Langsmith, + options: Optional[FinalRequestOptions] = None, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._options = options + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + _options: Optional[FinalRequestOptions] = None + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: AsyncLangsmith, + options: Optional[FinalRequestOptions] = None, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._options = options + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/python/langsmith_api/_types.py b/python/langsmith_api/_types.py new file mode 100644 index 000000000..beb06af33 --- /dev/null +++ b/python/langsmith_api/_types.py @@ -0,0 +1,273 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterable, + Iterator, + Optional, + Sequence, + AsyncIterable, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from langsmith_api import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + follow_redirects: bool + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. + + For example: + + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... + + + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +not_given = NotGiven() +# for backwards compatibility: +NOT_GIVEN = NotGiven() + + +class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +omit = Omit() + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/python/langsmith_api/_utils/__init__.py b/python/langsmith_api/_utils/__init__.py new file mode 100644 index 000000000..1c090e51f --- /dev/null +++ b/python/langsmith_api/_utils/__init__.py @@ -0,0 +1,64 @@ +from ._path import path_template as path_template +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + strip_not_given as strip_not_given, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_sequence_type as is_sequence_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/python/langsmith_api/_utils/_compat.py b/python/langsmith_api/_utils/_compat.py new file mode 100644 index 000000000..2c70b299c --- /dev/null +++ b/python/langsmith_api/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/python/langsmith_api/_utils/_datetime_parse.py b/python/langsmith_api/_utils/_datetime_parse.py new file mode 100644 index 000000000..7cb9d9e66 --- /dev/null +++ b/python/langsmith_api/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/python/langsmith_api/_utils/_json.py b/python/langsmith_api/_utils/_json.py new file mode 100644 index 000000000..60584214a --- /dev/null +++ b/python/langsmith_api/_utils/_json.py @@ -0,0 +1,35 @@ +import json +from typing import Any +from datetime import datetime +from typing_extensions import override + +import pydantic + +from .._compat import model_dump + + +def openapi_dumps(obj: Any) -> bytes: + """ + Serialize an object to UTF-8 encoded JSON bytes. + + Extends the standard json.dumps with support for additional types + commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. + """ + return json.dumps( + obj, + cls=_CustomEncoder, + # Uses the same defaults as httpx's JSON serialization + ensure_ascii=False, + separators=(",", ":"), + allow_nan=False, + ).encode() + + +class _CustomEncoder(json.JSONEncoder): + @override + def default(self, o: Any) -> Any: + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, pydantic.BaseModel): + return model_dump(o, exclude_unset=True, mode="json", by_alias=True) + return super().default(o) diff --git a/python/langsmith_api/_utils/_logs.py b/python/langsmith_api/_utils/_logs.py new file mode 100644 index 000000000..01f2d36b5 --- /dev/null +++ b/python/langsmith_api/_utils/_logs.py @@ -0,0 +1,25 @@ +import os +import logging + +logger: logging.Logger = logging.getLogger("langsmith_api") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - langsmith_api._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def setup_logging() -> None: + env = os.environ.get("LANGCHAIN_LOG") + if env == "debug": + _basic_config() + logger.setLevel(logging.DEBUG) + httpx_logger.setLevel(logging.DEBUG) + elif env == "info": + _basic_config() + logger.setLevel(logging.INFO) + httpx_logger.setLevel(logging.INFO) diff --git a/python/langsmith_api/_utils/_path.py b/python/langsmith_api/_utils/_path.py new file mode 100644 index 000000000..4d6e1e4cb --- /dev/null +++ b/python/langsmith_api/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/python/langsmith_api/_utils/_proxy.py b/python/langsmith_api/_utils/_proxy.py new file mode 100644 index 000000000..0f239a33c --- /dev/null +++ b/python/langsmith_api/_utils/_proxy.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and other methods. + """ + + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + + def __getattr__(self, attr: str) -> object: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) + + @override + def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) + + @override + def __dir__(self) -> Iterable[str]: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: # pyright: ignore + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ + + def __get_proxied__(self) -> T: + return self.__load__() + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: ... diff --git a/python/langsmith_api/_utils/_reflection.py b/python/langsmith_api/_utils/_reflection.py new file mode 100644 index 000000000..89aa712ac --- /dev/null +++ b/python/langsmith_api/_utils/_reflection.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/python/langsmith_api/_utils/_resources_proxy.py b/python/langsmith_api/_utils/_resources_proxy.py new file mode 100644 index 000000000..039fc99ea --- /dev/null +++ b/python/langsmith_api/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `langsmith_api.resources` module. + + This is used so that we can lazily import `langsmith_api.resources` only when + needed *and* so that users can just import `langsmith_api` and reference `langsmith_api.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("langsmith_api.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/python/langsmith_api/_utils/_streams.py b/python/langsmith_api/_utils/_streams.py new file mode 100644 index 000000000..f4a0208f0 --- /dev/null +++ b/python/langsmith_api/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/python/langsmith_api/_utils/_sync.py b/python/langsmith_api/_utils/_sync.py new file mode 100644 index 000000000..f6027c183 --- /dev/null +++ b/python/langsmith_api/_utils/_sync.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import asyncio +import functools +from typing import TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import sniffio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await asyncio.to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/python/langsmith_api/_utils/_transform.py b/python/langsmith_api/_utils/_transform.py new file mode 100644 index 000000000..520754920 --- /dev/null +++ b/python/langsmith_api/_utils/_transform.py @@ -0,0 +1,457 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_given, + lru_cache, + is_mapping, + is_iterable, + is_sequence, +) +from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict +from ._typing import ( + is_list_type, + is_union_type, + extract_type_arg, + is_iterable_type, + is_required_type, + is_sequence_type, + is_annotated_type, + strip_annotated_type, +) + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +@lru_cache(maxsize=8096) +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/python/langsmith_api/_utils/_typing.py b/python/langsmith_api/_utils/_typing.py new file mode 100644 index 000000000..193109f3a --- /dev/null +++ b/python/langsmith_api/_utils/_typing.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from ._utils import lru_cache +from .._types import InheritsGeneric +from ._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/python/langsmith_api/_utils/_utils.py b/python/langsmith_api/_utils/_utils.py new file mode 100644 index 000000000..199cd231f --- /dev/null +++ b/python/langsmith_api/_utils/_utils.py @@ -0,0 +1,433 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard, get_args + +import sniffio + +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) + return files + + +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, + array_format: ArrayFormat, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if not is_given(obj): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + array_format=array_format, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, + ) + for array_index, item in enumerate(obj) + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in its place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/python/langsmith_api/_version.py b/python/langsmith_api/_version.py new file mode 100644 index 000000000..0ee3d28e8 --- /dev/null +++ b/python/langsmith_api/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +__title__ = "langsmith_api" +__version__ = "0.0.1" diff --git a/python/langsmith_api/pagination.py b/python/langsmith_api/pagination.py new file mode 100644 index 000000000..794b2c290 --- /dev/null +++ b/python/langsmith_api/pagination.py @@ -0,0 +1,276 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Any, List, Type, Generic, Mapping, TypeVar, Optional, cast +from typing_extensions import override + +from httpx import Response + +from ._utils import is_mapping +from ._models import BaseModel +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = [ + "SyncOffsetPaginationTopLevelArray", + "AsyncOffsetPaginationTopLevelArray", + "SyncOffsetPaginationRepos", + "AsyncOffsetPaginationRepos", + "SyncOffsetPaginationCommits", + "AsyncOffsetPaginationCommits", + "SyncOffsetPaginationInsightsClusteringJobs", + "AsyncOffsetPaginationInsightsClusteringJobs", + "CursorPaginationCursors", + "SyncCursorPagination", + "AsyncCursorPagination", +] + +_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel) + +_T = TypeVar("_T") + + +class SyncOffsetPaginationTopLevelArray(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + @classmethod + def build(cls: Type[_BaseModelT], *, response: Response, data: object) -> _BaseModelT: # noqa: ARG003 + return cls.construct( + None, + **{ + **(cast(Mapping[str, Any], data) if is_mapping(data) else {"items": data}), + }, + ) + + +class AsyncOffsetPaginationTopLevelArray(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] + + @override + def _get_page_items(self) -> List[_T]: + items = self.items + if not items: + return [] + return items + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + @classmethod + def build(cls: Type[_BaseModelT], *, response: Response, data: object) -> _BaseModelT: # noqa: ARG003 + return cls.construct( + None, + **{ + **(cast(Mapping[str, Any], data) if is_mapping(data) else {"items": data}), + }, + ) + + +class SyncOffsetPaginationRepos(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + repos: List[_T] + total: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + repos = self.repos + if not repos: + return [] + return repos + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class AsyncOffsetPaginationRepos(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + repos: List[_T] + total: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + repos = self.repos + if not repos: + return [] + return repos + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class SyncOffsetPaginationCommits(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + commits: List[_T] + total: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + commits = self.commits + if not commits: + return [] + return commits + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class AsyncOffsetPaginationCommits(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + commits: List[_T] + total: Optional[int] = None + + @override + def _get_page_items(self) -> List[_T]: + commits = self.commits + if not commits: + return [] + return commits + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class SyncOffsetPaginationInsightsClusteringJobs(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + clustering_jobs: List[_T] + + @override + def _get_page_items(self) -> List[_T]: + clustering_jobs = self.clustering_jobs + if not clustering_jobs: + return [] + return clustering_jobs + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class AsyncOffsetPaginationInsightsClusteringJobs(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + clustering_jobs: List[_T] + + @override + def _get_page_items(self) -> List[_T]: + clustering_jobs = self.clustering_jobs + if not clustering_jobs: + return [] + return clustering_jobs + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("offset") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "offset" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + return PageInfo(params={"offset": current_count}) + + +class CursorPaginationCursors(BaseModel): + next: Optional[str] = None + + +class SyncCursorPagination(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + runs: List[_T] + cursors: Optional[CursorPaginationCursors] = None + + @override + def _get_page_items(self) -> List[_T]: + runs = self.runs + if not runs: + return [] + return runs + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = None + if self.cursors is not None: + if self.cursors.next is not None: + next = self.cursors.next + if not next: + return None + + return PageInfo(params={"cursor": next}) + + +class AsyncCursorPagination(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + runs: List[_T] + cursors: Optional[CursorPaginationCursors] = None + + @override + def _get_page_items(self) -> List[_T]: + runs = self.runs + if not runs: + return [] + return runs + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = None + if self.cursors is not None: + if self.cursors.next is not None: + next = self.cursors.next + if not next: + return None + + return PageInfo(params={"cursor": next}) diff --git a/python/langsmith_api/py.typed b/python/langsmith_api/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/python/langsmith_api/resources/__init__.py b/python/langsmith_api/resources/__init__.py new file mode 100644 index 000000000..455293538 --- /dev/null +++ b/python/langsmith_api/resources/__init__.py @@ -0,0 +1,201 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .info import ( + InfoResource, + AsyncInfoResource, + InfoResourceWithRawResponse, + AsyncInfoResourceWithRawResponse, + InfoResourceWithStreamingResponse, + AsyncInfoResourceWithStreamingResponse, +) +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from .repos import ( + ReposResource, + AsyncReposResource, + ReposResourceWithRawResponse, + AsyncReposResourceWithRawResponse, + ReposResourceWithStreamingResponse, + AsyncReposResourceWithStreamingResponse, +) +from .public import ( + PublicResource, + AsyncPublicResource, + PublicResourceWithRawResponse, + AsyncPublicResourceWithRawResponse, + PublicResourceWithStreamingResponse, + AsyncPublicResourceWithStreamingResponse, +) +from .commits import ( + CommitsResource, + AsyncCommitsResource, + CommitsResourceWithRawResponse, + AsyncCommitsResourceWithRawResponse, + CommitsResourceWithStreamingResponse, + AsyncCommitsResourceWithStreamingResponse, +) +from .datasets import ( + DatasetsResource, + AsyncDatasetsResource, + DatasetsResourceWithRawResponse, + AsyncDatasetsResourceWithRawResponse, + DatasetsResourceWithStreamingResponse, + AsyncDatasetsResourceWithStreamingResponse, +) +from .examples import ( + ExamplesResource, + AsyncExamplesResource, + ExamplesResourceWithRawResponse, + AsyncExamplesResourceWithRawResponse, + ExamplesResourceWithStreamingResponse, + AsyncExamplesResourceWithStreamingResponse, +) +from .feedback import ( + FeedbackResource, + AsyncFeedbackResource, + FeedbackResourceWithRawResponse, + AsyncFeedbackResourceWithRawResponse, + FeedbackResourceWithStreamingResponse, + AsyncFeedbackResourceWithStreamingResponse, +) +from .sessions import ( + SessionsResource, + AsyncSessionsResource, + SessionsResourceWithRawResponse, + AsyncSessionsResourceWithRawResponse, + SessionsResourceWithStreamingResponse, + AsyncSessionsResourceWithStreamingResponse, +) +from .settings import ( + SettingsResource, + AsyncSettingsResource, + SettingsResourceWithRawResponse, + AsyncSettingsResourceWithRawResponse, + SettingsResourceWithStreamingResponse, + AsyncSettingsResourceWithStreamingResponse, +) +from .sandboxes import ( + SandboxesResource, + AsyncSandboxesResource, + SandboxesResourceWithRawResponse, + AsyncSandboxesResourceWithRawResponse, + SandboxesResourceWithStreamingResponse, + AsyncSandboxesResourceWithStreamingResponse, +) +from .evaluators import ( + EvaluatorsResource, + AsyncEvaluatorsResource, + EvaluatorsResourceWithRawResponse, + AsyncEvaluatorsResourceWithRawResponse, + EvaluatorsResourceWithStreamingResponse, + AsyncEvaluatorsResourceWithStreamingResponse, +) +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) +from .annotation_queues import ( + AnnotationQueuesResource, + AsyncAnnotationQueuesResource, + AnnotationQueuesResourceWithRawResponse, + AsyncAnnotationQueuesResourceWithRawResponse, + AnnotationQueuesResourceWithStreamingResponse, + AsyncAnnotationQueuesResourceWithStreamingResponse, +) + +__all__ = [ + "SessionsResource", + "AsyncSessionsResource", + "SessionsResourceWithRawResponse", + "AsyncSessionsResourceWithRawResponse", + "SessionsResourceWithStreamingResponse", + "AsyncSessionsResourceWithStreamingResponse", + "ExamplesResource", + "AsyncExamplesResource", + "ExamplesResourceWithRawResponse", + "AsyncExamplesResourceWithRawResponse", + "ExamplesResourceWithStreamingResponse", + "AsyncExamplesResourceWithStreamingResponse", + "DatasetsResource", + "AsyncDatasetsResource", + "DatasetsResourceWithRawResponse", + "AsyncDatasetsResourceWithRawResponse", + "DatasetsResourceWithStreamingResponse", + "AsyncDatasetsResourceWithStreamingResponse", + "RunsResource", + "AsyncRunsResource", + "RunsResourceWithRawResponse", + "AsyncRunsResourceWithRawResponse", + "RunsResourceWithStreamingResponse", + "AsyncRunsResourceWithStreamingResponse", + "EvaluatorsResource", + "AsyncEvaluatorsResource", + "EvaluatorsResourceWithRawResponse", + "AsyncEvaluatorsResourceWithRawResponse", + "EvaluatorsResourceWithStreamingResponse", + "AsyncEvaluatorsResourceWithStreamingResponse", + "FeedbackResource", + "AsyncFeedbackResource", + "FeedbackResourceWithRawResponse", + "AsyncFeedbackResourceWithRawResponse", + "FeedbackResourceWithStreamingResponse", + "AsyncFeedbackResourceWithStreamingResponse", + "PublicResource", + "AsyncPublicResource", + "PublicResourceWithRawResponse", + "AsyncPublicResourceWithRawResponse", + "PublicResourceWithStreamingResponse", + "AsyncPublicResourceWithStreamingResponse", + "AnnotationQueuesResource", + "AsyncAnnotationQueuesResource", + "AnnotationQueuesResourceWithRawResponse", + "AsyncAnnotationQueuesResourceWithRawResponse", + "AnnotationQueuesResourceWithStreamingResponse", + "AsyncAnnotationQueuesResourceWithStreamingResponse", + "InfoResource", + "AsyncInfoResource", + "InfoResourceWithRawResponse", + "AsyncInfoResourceWithRawResponse", + "InfoResourceWithStreamingResponse", + "AsyncInfoResourceWithStreamingResponse", + "WorkspacesResource", + "AsyncWorkspacesResource", + "WorkspacesResourceWithRawResponse", + "AsyncWorkspacesResourceWithRawResponse", + "WorkspacesResourceWithStreamingResponse", + "AsyncWorkspacesResourceWithStreamingResponse", + "ReposResource", + "AsyncReposResource", + "ReposResourceWithRawResponse", + "AsyncReposResourceWithRawResponse", + "ReposResourceWithStreamingResponse", + "AsyncReposResourceWithStreamingResponse", + "CommitsResource", + "AsyncCommitsResource", + "CommitsResourceWithRawResponse", + "AsyncCommitsResourceWithRawResponse", + "CommitsResourceWithStreamingResponse", + "AsyncCommitsResourceWithStreamingResponse", + "SettingsResource", + "AsyncSettingsResource", + "SettingsResourceWithRawResponse", + "AsyncSettingsResourceWithRawResponse", + "SettingsResourceWithStreamingResponse", + "AsyncSettingsResourceWithStreamingResponse", + "SandboxesResource", + "AsyncSandboxesResource", + "SandboxesResourceWithRawResponse", + "AsyncSandboxesResourceWithRawResponse", + "SandboxesResourceWithStreamingResponse", + "AsyncSandboxesResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/annotation_queues/__init__.py b/python/langsmith_api/resources/annotation_queues/__init__.py new file mode 100644 index 000000000..96d3b7edf --- /dev/null +++ b/python/langsmith_api/resources/annotation_queues/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from .annotation_queues import ( + AnnotationQueuesResource, + AsyncAnnotationQueuesResource, + AnnotationQueuesResourceWithRawResponse, + AsyncAnnotationQueuesResourceWithRawResponse, + AnnotationQueuesResourceWithStreamingResponse, + AsyncAnnotationQueuesResourceWithStreamingResponse, +) + +__all__ = [ + "RunsResource", + "AsyncRunsResource", + "RunsResourceWithRawResponse", + "AsyncRunsResourceWithRawResponse", + "RunsResourceWithStreamingResponse", + "AsyncRunsResourceWithStreamingResponse", + "AnnotationQueuesResource", + "AsyncAnnotationQueuesResource", + "AnnotationQueuesResourceWithRawResponse", + "AsyncAnnotationQueuesResourceWithRawResponse", + "AnnotationQueuesResourceWithStreamingResponse", + "AsyncAnnotationQueuesResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/annotation_queues/annotation_queues.py b/python/langsmith_api/resources/annotation_queues/annotation_queues.py new file mode 100644 index 000000000..e19efd0c1 --- /dev/null +++ b/python/langsmith_api/resources/annotation_queues/annotation_queues.py @@ -0,0 +1,1443 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from ...types import ( + annotation_queue_export_params, + annotation_queue_update_params, + annotation_queue_populate_params, + annotation_queue_retrieve_run_params, + annotation_queue_retrieve_size_params, + annotation_queue_annotation_queues_params, + annotation_queue_create_run_status_params, + annotation_queue_retrieve_total_archived_params, + annotation_queue_retrieve_annotation_queues_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.annotation_queue_schema import AnnotationQueueSchema +from ...types.annotation_queue_size_schema import AnnotationQueueSizeSchema +from ...types.annotation_queue_retrieve_response import AnnotationQueueRetrieveResponse +from ...types.run_schema_with_annotation_queue_info import RunSchemaWithAnnotationQueueInfo +from ...types.annotation_queue_retrieve_queues_response import AnnotationQueueRetrieveQueuesResponse +from ...types.annotation_queue_rubric_item_schema_param import AnnotationQueueRubricItemSchemaParam +from ...types.annotation_queue_retrieve_annotation_queues_response import ( + AnnotationQueueRetrieveAnnotationQueuesResponse, +) + +__all__ = ["AnnotationQueuesResource", "AsyncAnnotationQueuesResource"] + + +class AnnotationQueuesResource(SyncAPIResource): + @cached_property + def runs(self) -> RunsResource: + return RunsResource(self._client) + + @cached_property + def with_raw_response(self) -> AnnotationQueuesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AnnotationQueuesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AnnotationQueuesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AnnotationQueuesResourceWithStreamingResponse(self) + + def retrieve( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueRetrieveResponse: + """ + Get Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueRetrieveResponse, + ) + + def update( + self, + queue_id: str, + *, + default_dataset: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + enable_reservations: bool | Omit = omit, + metadata: Optional[annotation_queue_update_params.Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + num_reviewers_per_item: Optional[annotation_queue_update_params.NumReviewersPerItem] | Omit = omit, + reservation_minutes: Optional[int] | Omit = omit, + reviewer_access_mode: Optional[Literal["any", "assigned"]] | Omit = omit, + rubric_instructions: Optional[str] | Omit = omit, + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._patch( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + body=maybe_transform( + { + "default_dataset": default_dataset, + "description": description, + "enable_reservations": enable_reservations, + "metadata": metadata, + "name": name, + "num_reviewers_per_item": num_reviewers_per_item, + "reservation_minutes": reservation_minutes, + "reviewer_access_mode": reviewer_access_mode, + "rubric_instructions": rubric_instructions, + "rubric_items": rubric_items, + }, + annotation_queue_update_params.AnnotationQueueUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._delete( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def annotation_queues( + self, + *, + name: str, + id: str | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + default_dataset: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + enable_reservations: Optional[bool] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + num_reviewers_per_item: Optional[int] | Omit = omit, + reservation_minutes: Optional[int] | Omit = omit, + reviewer_access_mode: str | Omit = omit, + rubric_instructions: Optional[str] | Omit = omit, + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] | Omit = omit, + session_ids: Optional[SequenceNotStr[str]] | Omit = omit, + updated_at: Union[str, datetime] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSchema: + """ + Create Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/annotation-queues", + body=maybe_transform( + { + "name": name, + "id": id, + "created_at": created_at, + "default_dataset": default_dataset, + "description": description, + "enable_reservations": enable_reservations, + "metadata": metadata, + "num_reviewers_per_item": num_reviewers_per_item, + "reservation_minutes": reservation_minutes, + "reviewer_access_mode": reviewer_access_mode, + "rubric_instructions": rubric_instructions, + "rubric_items": rubric_items, + "session_ids": session_ids, + "updated_at": updated_at, + }, + annotation_queue_annotation_queues_params.AnnotationQueueAnnotationQueuesParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueSchema, + ) + + def create_run_status( + self, + annotation_queue_run_id: str, + *, + override_added_at: Union[str, datetime, None] | Omit = omit, + status: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create Identity Annotation Queue Run Status + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not annotation_queue_run_id: + raise ValueError( + f"Expected a non-empty value for `annotation_queue_run_id` but received {annotation_queue_run_id!r}" + ) + return self._post( + path_template( + "/api/v1/annotation-queues/status/{annotation_queue_run_id}", + annotation_queue_run_id=annotation_queue_run_id, + ), + body=maybe_transform( + { + "override_added_at": override_added_at, + "status": status, + }, + annotation_queue_create_run_status_params.AnnotationQueueCreateRunStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def export( + self, + queue_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + include_annotator_detail: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Export Annotation Queue Archived Runs + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._post( + path_template("/api/v1/annotation-queues/{queue_id}/export", queue_id=queue_id), + body=maybe_transform( + { + "end_time": end_time, + "include_annotator_detail": include_annotator_detail, + "start_time": start_time, + }, + annotation_queue_export_params.AnnotationQueueExportParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def populate( + self, + *, + queue_id: str, + session_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Populate annotation queue with runs from an experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/annotation-queues/populate", + body=maybe_transform( + { + "queue_id": queue_id, + "session_ids": session_ids, + }, + annotation_queue_populate_params.AnnotationQueuePopulateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def retrieve_annotation_queues( + self, + *, + assigned_to_me: bool | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + ids: Optional[SequenceNotStr[str]] | Omit = omit, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + queue_type: Optional[Literal["single", "pairwise"]] | Omit = omit, + sort_by: Optional[str] | Omit = omit, + sort_by_desc: bool | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[AnnotationQueueRetrieveAnnotationQueuesResponse]: + """ + Get Annotation Queues + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/annotation-queues", + page=SyncOffsetPaginationTopLevelArray[AnnotationQueueRetrieveAnnotationQueuesResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "assigned_to_me": assigned_to_me, + "dataset_id": dataset_id, + "ids": ids, + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "queue_type": queue_type, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "tag_value_id": tag_value_id, + }, + annotation_queue_retrieve_annotation_queues_params.AnnotationQueueRetrieveAnnotationQueuesParams, + ), + ), + model=AnnotationQueueRetrieveAnnotationQueuesResponse, + ) + + def retrieve_queues( + self, + run_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueRetrieveQueuesResponse: + """ + Get Annotation Queues For Run + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{run_id}/queues", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueRetrieveQueuesResponse, + ) + + def retrieve_run( + self, + index: int, + *, + queue_id: str, + include_extra: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunSchemaWithAnnotationQueueInfo: + """ + Get a run from an annotation queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}/run/{index}", queue_id=queue_id, index=index), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_extra": include_extra}, + annotation_queue_retrieve_run_params.AnnotationQueueRetrieveRunParams, + ), + ), + cast_to=RunSchemaWithAnnotationQueueInfo, + ) + + def retrieve_size( + self, + queue_id: str, + *, + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Size From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}/size", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"status": status}, annotation_queue_retrieve_size_params.AnnotationQueueRetrieveSizeParams + ), + ), + cast_to=AnnotationQueueSizeSchema, + ) + + def retrieve_total_archived( + self, + queue_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Total Archived From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}/total_archived", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "end_time": end_time, + "start_time": start_time, + }, + annotation_queue_retrieve_total_archived_params.AnnotationQueueRetrieveTotalArchivedParams, + ), + ), + cast_to=AnnotationQueueSizeSchema, + ) + + def retrieve_total_size( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Total Size From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}/total_size", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueSizeSchema, + ) + + +class AsyncAnnotationQueuesResource(AsyncAPIResource): + @cached_property + def runs(self) -> AsyncRunsResource: + return AsyncRunsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAnnotationQueuesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncAnnotationQueuesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAnnotationQueuesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncAnnotationQueuesResourceWithStreamingResponse(self) + + async def retrieve( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueRetrieveResponse: + """ + Get Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueRetrieveResponse, + ) + + async def update( + self, + queue_id: str, + *, + default_dataset: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + enable_reservations: bool | Omit = omit, + metadata: Optional[annotation_queue_update_params.Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + num_reviewers_per_item: Optional[annotation_queue_update_params.NumReviewersPerItem] | Omit = omit, + reservation_minutes: Optional[int] | Omit = omit, + reviewer_access_mode: Optional[Literal["any", "assigned"]] | Omit = omit, + rubric_instructions: Optional[str] | Omit = omit, + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._patch( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + body=await async_maybe_transform( + { + "default_dataset": default_dataset, + "description": description, + "enable_reservations": enable_reservations, + "metadata": metadata, + "name": name, + "num_reviewers_per_item": num_reviewers_per_item, + "reservation_minutes": reservation_minutes, + "reviewer_access_mode": reviewer_access_mode, + "rubric_instructions": rubric_instructions, + "rubric_items": rubric_items, + }, + annotation_queue_update_params.AnnotationQueueUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._delete( + path_template("/api/v1/annotation-queues/{queue_id}", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def annotation_queues( + self, + *, + name: str, + id: str | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + default_dataset: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + enable_reservations: Optional[bool] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + num_reviewers_per_item: Optional[int] | Omit = omit, + reservation_minutes: Optional[int] | Omit = omit, + reviewer_access_mode: str | Omit = omit, + rubric_instructions: Optional[str] | Omit = omit, + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] | Omit = omit, + session_ids: Optional[SequenceNotStr[str]] | Omit = omit, + updated_at: Union[str, datetime] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSchema: + """ + Create Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/annotation-queues", + body=await async_maybe_transform( + { + "name": name, + "id": id, + "created_at": created_at, + "default_dataset": default_dataset, + "description": description, + "enable_reservations": enable_reservations, + "metadata": metadata, + "num_reviewers_per_item": num_reviewers_per_item, + "reservation_minutes": reservation_minutes, + "reviewer_access_mode": reviewer_access_mode, + "rubric_instructions": rubric_instructions, + "rubric_items": rubric_items, + "session_ids": session_ids, + "updated_at": updated_at, + }, + annotation_queue_annotation_queues_params.AnnotationQueueAnnotationQueuesParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueSchema, + ) + + async def create_run_status( + self, + annotation_queue_run_id: str, + *, + override_added_at: Union[str, datetime, None] | Omit = omit, + status: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create Identity Annotation Queue Run Status + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not annotation_queue_run_id: + raise ValueError( + f"Expected a non-empty value for `annotation_queue_run_id` but received {annotation_queue_run_id!r}" + ) + return await self._post( + path_template( + "/api/v1/annotation-queues/status/{annotation_queue_run_id}", + annotation_queue_run_id=annotation_queue_run_id, + ), + body=await async_maybe_transform( + { + "override_added_at": override_added_at, + "status": status, + }, + annotation_queue_create_run_status_params.AnnotationQueueCreateRunStatusParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def export( + self, + queue_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + include_annotator_detail: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Export Annotation Queue Archived Runs + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._post( + path_template("/api/v1/annotation-queues/{queue_id}/export", queue_id=queue_id), + body=await async_maybe_transform( + { + "end_time": end_time, + "include_annotator_detail": include_annotator_detail, + "start_time": start_time, + }, + annotation_queue_export_params.AnnotationQueueExportParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def populate( + self, + *, + queue_id: str, + session_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Populate annotation queue with runs from an experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/annotation-queues/populate", + body=await async_maybe_transform( + { + "queue_id": queue_id, + "session_ids": session_ids, + }, + annotation_queue_populate_params.AnnotationQueuePopulateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def retrieve_annotation_queues( + self, + *, + assigned_to_me: bool | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + ids: Optional[SequenceNotStr[str]] | Omit = omit, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + queue_type: Optional[Literal["single", "pairwise"]] | Omit = omit, + sort_by: Optional[str] | Omit = omit, + sort_by_desc: bool | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ + AnnotationQueueRetrieveAnnotationQueuesResponse, + AsyncOffsetPaginationTopLevelArray[AnnotationQueueRetrieveAnnotationQueuesResponse], + ]: + """ + Get Annotation Queues + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/annotation-queues", + page=AsyncOffsetPaginationTopLevelArray[AnnotationQueueRetrieveAnnotationQueuesResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "assigned_to_me": assigned_to_me, + "dataset_id": dataset_id, + "ids": ids, + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "queue_type": queue_type, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "tag_value_id": tag_value_id, + }, + annotation_queue_retrieve_annotation_queues_params.AnnotationQueueRetrieveAnnotationQueuesParams, + ), + ), + model=AnnotationQueueRetrieveAnnotationQueuesResponse, + ) + + async def retrieve_queues( + self, + run_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueRetrieveQueuesResponse: + """ + Get Annotation Queues For Run + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{run_id}/queues", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueRetrieveQueuesResponse, + ) + + async def retrieve_run( + self, + index: int, + *, + queue_id: str, + include_extra: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunSchemaWithAnnotationQueueInfo: + """ + Get a run from an annotation queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}/run/{index}", queue_id=queue_id, index=index), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_extra": include_extra}, + annotation_queue_retrieve_run_params.AnnotationQueueRetrieveRunParams, + ), + ), + cast_to=RunSchemaWithAnnotationQueueInfo, + ) + + async def retrieve_size( + self, + queue_id: str, + *, + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Size From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}/size", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"status": status}, annotation_queue_retrieve_size_params.AnnotationQueueRetrieveSizeParams + ), + ), + cast_to=AnnotationQueueSizeSchema, + ) + + async def retrieve_total_archived( + self, + queue_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Total Archived From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}/total_archived", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "end_time": end_time, + "start_time": start_time, + }, + annotation_queue_retrieve_total_archived_params.AnnotationQueueRetrieveTotalArchivedParams, + ), + ), + cast_to=AnnotationQueueSizeSchema, + ) + + async def retrieve_total_size( + self, + queue_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AnnotationQueueSizeSchema: + """ + Get Total Size From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}/total_size", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AnnotationQueueSizeSchema, + ) + + +class AnnotationQueuesResourceWithRawResponse: + def __init__(self, annotation_queues: AnnotationQueuesResource) -> None: + self._annotation_queues = annotation_queues + + self.retrieve = to_raw_response_wrapper( + annotation_queues.retrieve, + ) + self.update = to_raw_response_wrapper( + annotation_queues.update, + ) + self.delete = to_raw_response_wrapper( + annotation_queues.delete, + ) + self.annotation_queues = to_raw_response_wrapper( + annotation_queues.annotation_queues, + ) + self.create_run_status = to_raw_response_wrapper( + annotation_queues.create_run_status, + ) + self.export = to_raw_response_wrapper( + annotation_queues.export, + ) + self.populate = to_raw_response_wrapper( + annotation_queues.populate, + ) + self.retrieve_annotation_queues = to_raw_response_wrapper( + annotation_queues.retrieve_annotation_queues, + ) + self.retrieve_queues = to_raw_response_wrapper( + annotation_queues.retrieve_queues, + ) + self.retrieve_run = to_raw_response_wrapper( + annotation_queues.retrieve_run, + ) + self.retrieve_size = to_raw_response_wrapper( + annotation_queues.retrieve_size, + ) + self.retrieve_total_archived = to_raw_response_wrapper( + annotation_queues.retrieve_total_archived, + ) + self.retrieve_total_size = to_raw_response_wrapper( + annotation_queues.retrieve_total_size, + ) + + @cached_property + def runs(self) -> RunsResourceWithRawResponse: + return RunsResourceWithRawResponse(self._annotation_queues.runs) + + +class AsyncAnnotationQueuesResourceWithRawResponse: + def __init__(self, annotation_queues: AsyncAnnotationQueuesResource) -> None: + self._annotation_queues = annotation_queues + + self.retrieve = async_to_raw_response_wrapper( + annotation_queues.retrieve, + ) + self.update = async_to_raw_response_wrapper( + annotation_queues.update, + ) + self.delete = async_to_raw_response_wrapper( + annotation_queues.delete, + ) + self.annotation_queues = async_to_raw_response_wrapper( + annotation_queues.annotation_queues, + ) + self.create_run_status = async_to_raw_response_wrapper( + annotation_queues.create_run_status, + ) + self.export = async_to_raw_response_wrapper( + annotation_queues.export, + ) + self.populate = async_to_raw_response_wrapper( + annotation_queues.populate, + ) + self.retrieve_annotation_queues = async_to_raw_response_wrapper( + annotation_queues.retrieve_annotation_queues, + ) + self.retrieve_queues = async_to_raw_response_wrapper( + annotation_queues.retrieve_queues, + ) + self.retrieve_run = async_to_raw_response_wrapper( + annotation_queues.retrieve_run, + ) + self.retrieve_size = async_to_raw_response_wrapper( + annotation_queues.retrieve_size, + ) + self.retrieve_total_archived = async_to_raw_response_wrapper( + annotation_queues.retrieve_total_archived, + ) + self.retrieve_total_size = async_to_raw_response_wrapper( + annotation_queues.retrieve_total_size, + ) + + @cached_property + def runs(self) -> AsyncRunsResourceWithRawResponse: + return AsyncRunsResourceWithRawResponse(self._annotation_queues.runs) + + +class AnnotationQueuesResourceWithStreamingResponse: + def __init__(self, annotation_queues: AnnotationQueuesResource) -> None: + self._annotation_queues = annotation_queues + + self.retrieve = to_streamed_response_wrapper( + annotation_queues.retrieve, + ) + self.update = to_streamed_response_wrapper( + annotation_queues.update, + ) + self.delete = to_streamed_response_wrapper( + annotation_queues.delete, + ) + self.annotation_queues = to_streamed_response_wrapper( + annotation_queues.annotation_queues, + ) + self.create_run_status = to_streamed_response_wrapper( + annotation_queues.create_run_status, + ) + self.export = to_streamed_response_wrapper( + annotation_queues.export, + ) + self.populate = to_streamed_response_wrapper( + annotation_queues.populate, + ) + self.retrieve_annotation_queues = to_streamed_response_wrapper( + annotation_queues.retrieve_annotation_queues, + ) + self.retrieve_queues = to_streamed_response_wrapper( + annotation_queues.retrieve_queues, + ) + self.retrieve_run = to_streamed_response_wrapper( + annotation_queues.retrieve_run, + ) + self.retrieve_size = to_streamed_response_wrapper( + annotation_queues.retrieve_size, + ) + self.retrieve_total_archived = to_streamed_response_wrapper( + annotation_queues.retrieve_total_archived, + ) + self.retrieve_total_size = to_streamed_response_wrapper( + annotation_queues.retrieve_total_size, + ) + + @cached_property + def runs(self) -> RunsResourceWithStreamingResponse: + return RunsResourceWithStreamingResponse(self._annotation_queues.runs) + + +class AsyncAnnotationQueuesResourceWithStreamingResponse: + def __init__(self, annotation_queues: AsyncAnnotationQueuesResource) -> None: + self._annotation_queues = annotation_queues + + self.retrieve = async_to_streamed_response_wrapper( + annotation_queues.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + annotation_queues.update, + ) + self.delete = async_to_streamed_response_wrapper( + annotation_queues.delete, + ) + self.annotation_queues = async_to_streamed_response_wrapper( + annotation_queues.annotation_queues, + ) + self.create_run_status = async_to_streamed_response_wrapper( + annotation_queues.create_run_status, + ) + self.export = async_to_streamed_response_wrapper( + annotation_queues.export, + ) + self.populate = async_to_streamed_response_wrapper( + annotation_queues.populate, + ) + self.retrieve_annotation_queues = async_to_streamed_response_wrapper( + annotation_queues.retrieve_annotation_queues, + ) + self.retrieve_queues = async_to_streamed_response_wrapper( + annotation_queues.retrieve_queues, + ) + self.retrieve_run = async_to_streamed_response_wrapper( + annotation_queues.retrieve_run, + ) + self.retrieve_size = async_to_streamed_response_wrapper( + annotation_queues.retrieve_size, + ) + self.retrieve_total_archived = async_to_streamed_response_wrapper( + annotation_queues.retrieve_total_archived, + ) + self.retrieve_total_size = async_to_streamed_response_wrapper( + annotation_queues.retrieve_total_size, + ) + + @cached_property + def runs(self) -> AsyncRunsResourceWithStreamingResponse: + return AsyncRunsResourceWithStreamingResponse(self._annotation_queues.runs) diff --git a/python/langsmith_api/resources/annotation_queues/runs.py b/python/langsmith_api/resources/annotation_queues/runs.py new file mode 100644 index 000000000..d0341dee4 --- /dev/null +++ b/python/langsmith_api/resources/annotation_queues/runs.py @@ -0,0 +1,726 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, overload + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.annotation_queues import run_list_params, run_create_params, run_update_params, run_delete_all_params +from ...types.annotation_queues.run_list_response import RunListResponse +from ...types.annotation_queues.run_create_response import RunCreateResponse + +__all__ = ["RunsResource", "AsyncRunsResource"] + + +class RunsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return RunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return RunsResourceWithStreamingResponse(self) + + @overload + def create( + self, + queue_id: str, + *, + body: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + queue_id: str, + *, + body: Iterable[run_create_params.RunsAnnotationQueueRunAddSchemaArrayBody], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + queue_id: str, + *, + body: Iterable[run_create_params.Variant2Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["body"]) + def create( + self, + queue_id: str, + *, + body: SequenceNotStr[str] + | Iterable[run_create_params.RunsAnnotationQueueRunAddSchemaArrayBody] + | Iterable[run_create_params.Variant2Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._post( + path_template("/api/v1/annotation-queues/{queue_id}/runs", queue_id=queue_id), + body=maybe_transform(body, SequenceNotStr[str]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + def update( + self, + queue_run_id: str, + *, + queue_id: str, + added_at: Union[str, datetime, None] | Omit = omit, + last_reviewed_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update Run In Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + if not queue_run_id: + raise ValueError(f"Expected a non-empty value for `queue_run_id` but received {queue_run_id!r}") + return self._patch( + path_template( + "/api/v1/annotation-queues/{queue_id}/runs/{queue_run_id}", queue_id=queue_id, queue_run_id=queue_run_id + ), + body=maybe_transform( + { + "added_at": added_at, + "last_reviewed_time": last_reviewed_time, + }, + run_update_params.RunUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + queue_id: str, + *, + archived: Optional[bool] | Omit = omit, + include_stats: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunListResponse: + """ + Get Runs From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._get( + path_template("/api/v1/annotation-queues/{queue_id}/runs", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "archived": archived, + "include_stats": include_stats, + "limit": limit, + "offset": offset, + "status": status, + }, + run_list_params.RunListParams, + ), + ), + cast_to=RunListResponse, + ) + + def delete_all( + self, + queue_id: str, + *, + delete_all: bool | Omit = omit, + exclude_run_ids: Optional[SequenceNotStr[str]] | Omit = omit, + run_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Runs From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return self._post( + path_template("/api/v1/annotation-queues/{queue_id}/runs/delete", queue_id=queue_id), + body=maybe_transform( + { + "delete_all": delete_all, + "exclude_run_ids": exclude_run_ids, + "run_ids": run_ids, + }, + run_delete_all_params.RunDeleteAllParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete_queue( + self, + queue_run_id: str, + *, + queue_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Run From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + if not queue_run_id: + raise ValueError(f"Expected a non-empty value for `queue_run_id` but received {queue_run_id!r}") + return self._delete( + path_template( + "/api/v1/annotation-queues/{queue_id}/runs/{queue_run_id}", queue_id=queue_id, queue_run_id=queue_run_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncRunsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncRunsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + queue_id: str, + *, + body: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + queue_id: str, + *, + body: Iterable[run_create_params.RunsAnnotationQueueRunAddSchemaArrayBody], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + queue_id: str, + *, + body: Iterable[run_create_params.Variant2Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """ + Add Runs To Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["body"]) + async def create( + self, + queue_id: str, + *, + body: SequenceNotStr[str] + | Iterable[run_create_params.RunsAnnotationQueueRunAddSchemaArrayBody] + | Iterable[run_create_params.Variant2Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._post( + path_template("/api/v1/annotation-queues/{queue_id}/runs", queue_id=queue_id), + body=await async_maybe_transform(body, SequenceNotStr[str]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + async def update( + self, + queue_run_id: str, + *, + queue_id: str, + added_at: Union[str, datetime, None] | Omit = omit, + last_reviewed_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update Run In Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + if not queue_run_id: + raise ValueError(f"Expected a non-empty value for `queue_run_id` but received {queue_run_id!r}") + return await self._patch( + path_template( + "/api/v1/annotation-queues/{queue_id}/runs/{queue_run_id}", queue_id=queue_id, queue_run_id=queue_run_id + ), + body=await async_maybe_transform( + { + "added_at": added_at, + "last_reviewed_time": last_reviewed_time, + }, + run_update_params.RunUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list( + self, + queue_id: str, + *, + archived: Optional[bool] | Omit = omit, + include_stats: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunListResponse: + """ + Get Runs From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._get( + path_template("/api/v1/annotation-queues/{queue_id}/runs", queue_id=queue_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "archived": archived, + "include_stats": include_stats, + "limit": limit, + "offset": offset, + "status": status, + }, + run_list_params.RunListParams, + ), + ), + cast_to=RunListResponse, + ) + + async def delete_all( + self, + queue_id: str, + *, + delete_all: bool | Omit = omit, + exclude_run_ids: Optional[SequenceNotStr[str]] | Omit = omit, + run_ids: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Runs From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + return await self._post( + path_template("/api/v1/annotation-queues/{queue_id}/runs/delete", queue_id=queue_id), + body=await async_maybe_transform( + { + "delete_all": delete_all, + "exclude_run_ids": exclude_run_ids, + "run_ids": run_ids, + }, + run_delete_all_params.RunDeleteAllParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete_queue( + self, + queue_run_id: str, + *, + queue_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Run From Annotation Queue + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not queue_id: + raise ValueError(f"Expected a non-empty value for `queue_id` but received {queue_id!r}") + if not queue_run_id: + raise ValueError(f"Expected a non-empty value for `queue_run_id` but received {queue_run_id!r}") + return await self._delete( + path_template( + "/api/v1/annotation-queues/{queue_id}/runs/{queue_run_id}", queue_id=queue_id, queue_run_id=queue_run_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class RunsResourceWithRawResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_raw_response_wrapper( + runs.create, + ) + self.update = to_raw_response_wrapper( + runs.update, + ) + self.list = to_raw_response_wrapper( + runs.list, + ) + self.delete_all = to_raw_response_wrapper( + runs.delete_all, + ) + self.delete_queue = to_raw_response_wrapper( + runs.delete_queue, + ) + + +class AsyncRunsResourceWithRawResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_raw_response_wrapper( + runs.create, + ) + self.update = async_to_raw_response_wrapper( + runs.update, + ) + self.list = async_to_raw_response_wrapper( + runs.list, + ) + self.delete_all = async_to_raw_response_wrapper( + runs.delete_all, + ) + self.delete_queue = async_to_raw_response_wrapper( + runs.delete_queue, + ) + + +class RunsResourceWithStreamingResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.update = to_streamed_response_wrapper( + runs.update, + ) + self.list = to_streamed_response_wrapper( + runs.list, + ) + self.delete_all = to_streamed_response_wrapper( + runs.delete_all, + ) + self.delete_queue = to_streamed_response_wrapper( + runs.delete_queue, + ) + + +class AsyncRunsResourceWithStreamingResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_streamed_response_wrapper( + runs.create, + ) + self.update = async_to_streamed_response_wrapper( + runs.update, + ) + self.list = async_to_streamed_response_wrapper( + runs.list, + ) + self.delete_all = async_to_streamed_response_wrapper( + runs.delete_all, + ) + self.delete_queue = async_to_streamed_response_wrapper( + runs.delete_queue, + ) diff --git a/python/langsmith_api/resources/commits.py b/python/langsmith_api/resources/commits.py new file mode 100644 index 000000000..ed24a0e23 --- /dev/null +++ b/python/langsmith_api/resources/commits.py @@ -0,0 +1,502 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import commit_list_params, commit_create_params, commit_retrieve_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..pagination import SyncOffsetPaginationCommits, AsyncOffsetPaginationCommits +from .._base_client import AsyncPaginator, make_request_options +from ..types.commit_with_lookups import CommitWithLookups +from ..types.commit_create_response import CommitCreateResponse +from ..types.commit_retrieve_response import CommitRetrieveResponse + +__all__ = ["CommitsResource", "AsyncCommitsResource"] + + +class CommitsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CommitsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return CommitsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CommitsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return CommitsResourceWithStreamingResponse(self) + + def create( + self, + repo: str, + *, + owner: str, + description: str | Omit = omit, + manifest: object | Omit = omit, + parent_commit: str | Omit = omit, + skip_webhooks: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommitCreateResponse: + """Creates a new commit in a repository. + + Requires authentication and write access + to the repository. + + Args: + skip_webhooks: SkipWebhooks allows skipping webhook notifications. Can be true (boolean) to + skip all, or an array of webhook UUIDs to skip specific ones. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._post( + path_template("/commits/{owner}/{repo}", owner=owner, repo=repo), + body=maybe_transform( + { + "description": description, + "manifest": manifest, + "parent_commit": parent_commit, + "skip_webhooks": skip_webhooks, + }, + commit_create_params.CommitCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CommitCreateResponse, + ) + + def retrieve( + self, + commit: str, + *, + owner: str, + repo: str, + get_examples: bool | Omit = omit, + include: str | Omit = omit, + include_model: bool | Omit = omit, + is_view: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommitRetrieveResponse: + """Retrieves a specific commit by hash, tag, or "latest" for a repository. + + This + endpoint supports both authenticated and unauthenticated access. Authenticated + users can access private repos, while unauthenticated users can only access + public repos. Commit resolution logic: + + - "latest" or empty: Get the most recent commit + - Less than 8 characters: Only check for tags + - 8 or more characters: Prioritize commit hash over tag, check both + + Args: + include: Comma-separated list of optional fields: "model", "is_draft" + + include_model: Deprecated: use Include instead + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + if not commit: + raise ValueError(f"Expected a non-empty value for `commit` but received {commit!r}") + return self._get( + path_template("/commits/{owner}/{repo}/{commit}", owner=owner, repo=repo, commit=commit), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "get_examples": get_examples, + "include": include, + "include_model": include_model, + "is_view": is_view, + }, + commit_retrieve_params.CommitRetrieveParams, + ), + ), + cast_to=CommitRetrieveResponse, + ) + + def list( + self, + repo: str, + *, + owner: str, + include_stats: bool | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationCommits[CommitWithLookups]: + """Lists all commits for a repository with pagination support. + + This endpoint + supports both authenticated and unauthenticated access. Authenticated users can + access private repos, while unauthenticated users can only access public repos. + The include_stats parameter controls whether download and view statistics are + computed (defaults to true). + + Args: + include_stats: IncludeStats determines whether to compute num_downloads and num_views + + limit: Limit is the pagination limit + + offset: Offset is the pagination offset + + tag: Tag filters commits to only those with a specific tag (e.g. "production", + "staging") + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._get_api_list( + path_template("/commits/{owner}/{repo}", owner=owner, repo=repo), + page=SyncOffsetPaginationCommits[CommitWithLookups], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_stats": include_stats, + "limit": limit, + "offset": offset, + "tag": tag, + }, + commit_list_params.CommitListParams, + ), + ), + model=CommitWithLookups, + ) + + +class AsyncCommitsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCommitsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncCommitsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCommitsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncCommitsResourceWithStreamingResponse(self) + + async def create( + self, + repo: str, + *, + owner: str, + description: str | Omit = omit, + manifest: object | Omit = omit, + parent_commit: str | Omit = omit, + skip_webhooks: object | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommitCreateResponse: + """Creates a new commit in a repository. + + Requires authentication and write access + to the repository. + + Args: + skip_webhooks: SkipWebhooks allows skipping webhook notifications. Can be true (boolean) to + skip all, or an array of webhook UUIDs to skip specific ones. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._post( + path_template("/commits/{owner}/{repo}", owner=owner, repo=repo), + body=await async_maybe_transform( + { + "description": description, + "manifest": manifest, + "parent_commit": parent_commit, + "skip_webhooks": skip_webhooks, + }, + commit_create_params.CommitCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CommitCreateResponse, + ) + + async def retrieve( + self, + commit: str, + *, + owner: str, + repo: str, + get_examples: bool | Omit = omit, + include: str | Omit = omit, + include_model: bool | Omit = omit, + is_view: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommitRetrieveResponse: + """Retrieves a specific commit by hash, tag, or "latest" for a repository. + + This + endpoint supports both authenticated and unauthenticated access. Authenticated + users can access private repos, while unauthenticated users can only access + public repos. Commit resolution logic: + + - "latest" or empty: Get the most recent commit + - Less than 8 characters: Only check for tags + - 8 or more characters: Prioritize commit hash over tag, check both + + Args: + include: Comma-separated list of optional fields: "model", "is_draft" + + include_model: Deprecated: use Include instead + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + if not commit: + raise ValueError(f"Expected a non-empty value for `commit` but received {commit!r}") + return await self._get( + path_template("/commits/{owner}/{repo}/{commit}", owner=owner, repo=repo, commit=commit), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "get_examples": get_examples, + "include": include, + "include_model": include_model, + "is_view": is_view, + }, + commit_retrieve_params.CommitRetrieveParams, + ), + ), + cast_to=CommitRetrieveResponse, + ) + + def list( + self, + repo: str, + *, + owner: str, + include_stats: bool | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + tag: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[CommitWithLookups, AsyncOffsetPaginationCommits[CommitWithLookups]]: + """Lists all commits for a repository with pagination support. + + This endpoint + supports both authenticated and unauthenticated access. Authenticated users can + access private repos, while unauthenticated users can only access public repos. + The include_stats parameter controls whether download and view statistics are + computed (defaults to true). + + Args: + include_stats: IncludeStats determines whether to compute num_downloads and num_views + + limit: Limit is the pagination limit + + offset: Offset is the pagination offset + + tag: Tag filters commits to only those with a specific tag (e.g. "production", + "staging") + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._get_api_list( + path_template("/commits/{owner}/{repo}", owner=owner, repo=repo), + page=AsyncOffsetPaginationCommits[CommitWithLookups], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_stats": include_stats, + "limit": limit, + "offset": offset, + "tag": tag, + }, + commit_list_params.CommitListParams, + ), + ), + model=CommitWithLookups, + ) + + +class CommitsResourceWithRawResponse: + def __init__(self, commits: CommitsResource) -> None: + self._commits = commits + + self.create = to_raw_response_wrapper( + commits.create, + ) + self.retrieve = to_raw_response_wrapper( + commits.retrieve, + ) + self.list = to_raw_response_wrapper( + commits.list, + ) + + +class AsyncCommitsResourceWithRawResponse: + def __init__(self, commits: AsyncCommitsResource) -> None: + self._commits = commits + + self.create = async_to_raw_response_wrapper( + commits.create, + ) + self.retrieve = async_to_raw_response_wrapper( + commits.retrieve, + ) + self.list = async_to_raw_response_wrapper( + commits.list, + ) + + +class CommitsResourceWithStreamingResponse: + def __init__(self, commits: CommitsResource) -> None: + self._commits = commits + + self.create = to_streamed_response_wrapper( + commits.create, + ) + self.retrieve = to_streamed_response_wrapper( + commits.retrieve, + ) + self.list = to_streamed_response_wrapper( + commits.list, + ) + + +class AsyncCommitsResourceWithStreamingResponse: + def __init__(self, commits: AsyncCommitsResource) -> None: + self._commits = commits + + self.create = async_to_streamed_response_wrapper( + commits.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + commits.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + commits.list, + ) diff --git a/python/langsmith_api/resources/datasets/__init__.py b/python/langsmith_api/resources/datasets/__init__.py new file mode 100644 index 000000000..b38315241 --- /dev/null +++ b/python/langsmith_api/resources/datasets/__init__.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from .group import ( + GroupResource, + AsyncGroupResource, + GroupResourceWithRawResponse, + AsyncGroupResourceWithRawResponse, + GroupResourceWithStreamingResponse, + AsyncGroupResourceWithStreamingResponse, +) +from .share import ( + ShareResource, + AsyncShareResource, + ShareResourceWithRawResponse, + AsyncShareResourceWithRawResponse, + ShareResourceWithStreamingResponse, + AsyncShareResourceWithStreamingResponse, +) +from .splits import ( + SplitsResource, + AsyncSplitsResource, + SplitsResourceWithRawResponse, + AsyncSplitsResourceWithRawResponse, + SplitsResourceWithStreamingResponse, + AsyncSplitsResourceWithStreamingResponse, +) +from .datasets import ( + DatasetsResource, + AsyncDatasetsResource, + DatasetsResourceWithRawResponse, + AsyncDatasetsResourceWithRawResponse, + DatasetsResourceWithStreamingResponse, + AsyncDatasetsResourceWithStreamingResponse, +) +from .versions import ( + VersionsResource, + AsyncVersionsResource, + VersionsResourceWithRawResponse, + AsyncVersionsResourceWithRawResponse, + VersionsResourceWithStreamingResponse, + AsyncVersionsResourceWithStreamingResponse, +) +from .comparative import ( + ComparativeResource, + AsyncComparativeResource, + ComparativeResourceWithRawResponse, + AsyncComparativeResourceWithRawResponse, + ComparativeResourceWithStreamingResponse, + AsyncComparativeResourceWithStreamingResponse, +) +from .experiments import ( + ExperimentsResource, + AsyncExperimentsResource, + ExperimentsResourceWithRawResponse, + AsyncExperimentsResourceWithRawResponse, + ExperimentsResourceWithStreamingResponse, + AsyncExperimentsResourceWithStreamingResponse, +) +from .playground_experiment import ( + PlaygroundExperimentResource, + AsyncPlaygroundExperimentResource, + PlaygroundExperimentResourceWithRawResponse, + AsyncPlaygroundExperimentResourceWithRawResponse, + PlaygroundExperimentResourceWithStreamingResponse, + AsyncPlaygroundExperimentResourceWithStreamingResponse, +) + +__all__ = [ + "VersionsResource", + "AsyncVersionsResource", + "VersionsResourceWithRawResponse", + "AsyncVersionsResourceWithRawResponse", + "VersionsResourceWithStreamingResponse", + "AsyncVersionsResourceWithStreamingResponse", + "RunsResource", + "AsyncRunsResource", + "RunsResourceWithRawResponse", + "AsyncRunsResourceWithRawResponse", + "RunsResourceWithStreamingResponse", + "AsyncRunsResourceWithStreamingResponse", + "GroupResource", + "AsyncGroupResource", + "GroupResourceWithRawResponse", + "AsyncGroupResourceWithRawResponse", + "GroupResourceWithStreamingResponse", + "AsyncGroupResourceWithStreamingResponse", + "ExperimentsResource", + "AsyncExperimentsResource", + "ExperimentsResourceWithRawResponse", + "AsyncExperimentsResourceWithRawResponse", + "ExperimentsResourceWithStreamingResponse", + "AsyncExperimentsResourceWithStreamingResponse", + "ShareResource", + "AsyncShareResource", + "ShareResourceWithRawResponse", + "AsyncShareResourceWithRawResponse", + "ShareResourceWithStreamingResponse", + "AsyncShareResourceWithStreamingResponse", + "ComparativeResource", + "AsyncComparativeResource", + "ComparativeResourceWithRawResponse", + "AsyncComparativeResourceWithRawResponse", + "ComparativeResourceWithStreamingResponse", + "AsyncComparativeResourceWithStreamingResponse", + "SplitsResource", + "AsyncSplitsResource", + "SplitsResourceWithRawResponse", + "AsyncSplitsResourceWithRawResponse", + "SplitsResourceWithStreamingResponse", + "AsyncSplitsResourceWithStreamingResponse", + "PlaygroundExperimentResource", + "AsyncPlaygroundExperimentResource", + "PlaygroundExperimentResourceWithRawResponse", + "AsyncPlaygroundExperimentResourceWithRawResponse", + "PlaygroundExperimentResourceWithStreamingResponse", + "AsyncPlaygroundExperimentResourceWithStreamingResponse", + "DatasetsResource", + "AsyncDatasetsResource", + "DatasetsResourceWithRawResponse", + "AsyncDatasetsResourceWithRawResponse", + "DatasetsResourceWithStreamingResponse", + "AsyncDatasetsResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/datasets/comparative.py b/python/langsmith_api/resources/datasets/comparative.py new file mode 100644 index 000000000..6424ecc6f --- /dev/null +++ b/python/langsmith_api/resources/datasets/comparative.py @@ -0,0 +1,292 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import comparative_create_params +from ...types.datasets.comparative_create_response import ComparativeCreateResponse + +__all__ = ["ComparativeResource", "AsyncComparativeResource"] + + +class ComparativeResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ComparativeResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ComparativeResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ComparativeResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ComparativeResourceWithStreamingResponse(self) + + def create( + self, + *, + experiment_ids: SequenceNotStr[str], + id: str | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + description: Optional[str] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + modified_at: Union[str, datetime] | Omit = omit, + name: Optional[str] | Omit = omit, + reference_dataset_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ComparativeCreateResponse: + """ + Create a comparative experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/datasets/comparative", + body=maybe_transform( + { + "experiment_ids": experiment_ids, + "id": id, + "created_at": created_at, + "description": description, + "extra": extra, + "modified_at": modified_at, + "name": name, + "reference_dataset_id": reference_dataset_id, + }, + comparative_create_params.ComparativeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ComparativeCreateResponse, + ) + + def delete( + self, + comparative_experiment_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific comparative experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not comparative_experiment_id: + raise ValueError( + f"Expected a non-empty value for `comparative_experiment_id` but received {comparative_experiment_id!r}" + ) + return self._delete( + path_template( + "/api/v1/datasets/comparative/{comparative_experiment_id}", + comparative_experiment_id=comparative_experiment_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncComparativeResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncComparativeResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncComparativeResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncComparativeResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncComparativeResourceWithStreamingResponse(self) + + async def create( + self, + *, + experiment_ids: SequenceNotStr[str], + id: str | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + description: Optional[str] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + modified_at: Union[str, datetime] | Omit = omit, + name: Optional[str] | Omit = omit, + reference_dataset_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ComparativeCreateResponse: + """ + Create a comparative experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/datasets/comparative", + body=await async_maybe_transform( + { + "experiment_ids": experiment_ids, + "id": id, + "created_at": created_at, + "description": description, + "extra": extra, + "modified_at": modified_at, + "name": name, + "reference_dataset_id": reference_dataset_id, + }, + comparative_create_params.ComparativeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ComparativeCreateResponse, + ) + + async def delete( + self, + comparative_experiment_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific comparative experiment. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not comparative_experiment_id: + raise ValueError( + f"Expected a non-empty value for `comparative_experiment_id` but received {comparative_experiment_id!r}" + ) + return await self._delete( + path_template( + "/api/v1/datasets/comparative/{comparative_experiment_id}", + comparative_experiment_id=comparative_experiment_id, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ComparativeResourceWithRawResponse: + def __init__(self, comparative: ComparativeResource) -> None: + self._comparative = comparative + + self.create = to_raw_response_wrapper( + comparative.create, + ) + self.delete = to_raw_response_wrapper( + comparative.delete, + ) + + +class AsyncComparativeResourceWithRawResponse: + def __init__(self, comparative: AsyncComparativeResource) -> None: + self._comparative = comparative + + self.create = async_to_raw_response_wrapper( + comparative.create, + ) + self.delete = async_to_raw_response_wrapper( + comparative.delete, + ) + + +class ComparativeResourceWithStreamingResponse: + def __init__(self, comparative: ComparativeResource) -> None: + self._comparative = comparative + + self.create = to_streamed_response_wrapper( + comparative.create, + ) + self.delete = to_streamed_response_wrapper( + comparative.delete, + ) + + +class AsyncComparativeResourceWithStreamingResponse: + def __init__(self, comparative: AsyncComparativeResource) -> None: + self._comparative = comparative + + self.create = async_to_streamed_response_wrapper( + comparative.create, + ) + self.delete = async_to_streamed_response_wrapper( + comparative.delete, + ) diff --git a/python/langsmith_api/resources/datasets/datasets.py b/python/langsmith_api/resources/datasets/datasets.py new file mode 100644 index 000000000..1be21c428 --- /dev/null +++ b/python/langsmith_api/resources/datasets/datasets.py @@ -0,0 +1,1777 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Mapping, Iterable, Optional, cast +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from .runs import ( + RunsResource, + AsyncRunsResource, + RunsResourceWithRawResponse, + AsyncRunsResourceWithRawResponse, + RunsResourceWithStreamingResponse, + AsyncRunsResourceWithStreamingResponse, +) +from .group import ( + GroupResource, + AsyncGroupResource, + GroupResourceWithRawResponse, + AsyncGroupResourceWithRawResponse, + GroupResourceWithStreamingResponse, + AsyncGroupResourceWithStreamingResponse, +) +from .share import ( + ShareResource, + AsyncShareResource, + ShareResourceWithRawResponse, + AsyncShareResourceWithRawResponse, + ShareResourceWithStreamingResponse, + AsyncShareResourceWithStreamingResponse, +) +from .splits import ( + SplitsResource, + AsyncSplitsResource, + SplitsResourceWithRawResponse, + AsyncSplitsResourceWithRawResponse, + SplitsResourceWithStreamingResponse, + AsyncSplitsResourceWithStreamingResponse, +) +from ...types import ( + DataType, + SortByDatasetColumn, + dataset_list_params, + dataset_clone_params, + dataset_create_params, + dataset_update_params, + dataset_upload_params, + dataset_update_tags_params, + dataset_retrieve_csv_params, + dataset_retrieve_jsonl_params, + dataset_retrieve_openai_params, + dataset_retrieve_version_params, + dataset_retrieve_openai_ft_params, +) +from ..._files import deepcopy_with_paths +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from .versions import ( + VersionsResource, + AsyncVersionsResource, + VersionsResourceWithRawResponse, + AsyncVersionsResourceWithRawResponse, + VersionsResourceWithStreamingResponse, + AsyncVersionsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .comparative import ( + ComparativeResource, + AsyncComparativeResource, + ComparativeResourceWithRawResponse, + AsyncComparativeResourceWithRawResponse, + ComparativeResourceWithStreamingResponse, + AsyncComparativeResourceWithStreamingResponse, +) +from .experiments import ( + ExperimentsResource, + AsyncExperimentsResource, + ExperimentsResourceWithRawResponse, + AsyncExperimentsResourceWithRawResponse, + ExperimentsResourceWithStreamingResponse, + AsyncExperimentsResourceWithStreamingResponse, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.dataset import Dataset +from ...types.data_type import DataType +from .playground_experiment import ( + PlaygroundExperimentResource, + AsyncPlaygroundExperimentResource, + PlaygroundExperimentResourceWithRawResponse, + AsyncPlaygroundExperimentResourceWithRawResponse, + PlaygroundExperimentResourceWithStreamingResponse, + AsyncPlaygroundExperimentResourceWithStreamingResponse, +) +from ...types.dataset_version import DatasetVersion +from ...types.dataset_clone_response import DatasetCloneResponse +from ...types.sort_by_dataset_column import SortByDatasetColumn +from ...types.dataset_update_response import DatasetUpdateResponse +from ...types.dataset_transformation_param import DatasetTransformationParam + +__all__ = ["DatasetsResource", "AsyncDatasetsResource"] + + +class DatasetsResource(SyncAPIResource): + @cached_property + def versions(self) -> VersionsResource: + return VersionsResource(self._client) + + @cached_property + def runs(self) -> RunsResource: + return RunsResource(self._client) + + @cached_property + def group(self) -> GroupResource: + return GroupResource(self._client) + + @cached_property + def experiments(self) -> ExperimentsResource: + return ExperimentsResource(self._client) + + @cached_property + def share(self) -> ShareResource: + return ShareResource(self._client) + + @cached_property + def comparative(self) -> ComparativeResource: + return ComparativeResource(self._client) + + @cached_property + def splits(self) -> SplitsResource: + return SplitsResource(self._client) + + @cached_property + def playground_experiment(self) -> PlaygroundExperimentResource: + return PlaygroundExperimentResource(self._client) + + @cached_property + def with_raw_response(self) -> DatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return DatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return DatasetsResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str, + id: Optional[str] | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + data_type: DataType | Omit = omit, + description: Optional[str] | Omit = omit, + externally_managed: Optional[bool] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + inputs_schema_definition: Optional[Dict[str, object]] | Omit = omit, + outputs_schema_definition: Optional[Dict[str, object]] | Omit = omit, + transformations: Optional[Iterable[DatasetTransformationParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Create a new dataset. + + Args: + data_type: Enum for dataset data types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/datasets", + body=maybe_transform( + { + "name": name, + "id": id, + "created_at": created_at, + "data_type": data_type, + "description": description, + "externally_managed": externally_managed, + "extra": extra, + "inputs_schema_definition": inputs_schema_definition, + "outputs_schema_definition": outputs_schema_definition, + "transformations": transformations, + }, + dataset_create_params.DatasetCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + def retrieve( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Get a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + def update( + self, + dataset_id: str, + *, + baseline_experiment_id: Optional[dataset_update_params.BaselineExperimentID] | Omit = omit, + description: Optional[dataset_update_params.Description] | Omit = omit, + inputs_schema_definition: Optional[dataset_update_params.InputsSchemaDefinition] | Omit = omit, + metadata: Optional[dataset_update_params.Metadata] | Omit = omit, + name: Optional[dataset_update_params.Name] | Omit = omit, + outputs_schema_definition: Optional[dataset_update_params.OutputsSchemaDefinition] | Omit = omit, + patch_examples: Optional[Dict[str, dataset_update_params.PatchExamples]] | Omit = omit, + transformations: Optional[dataset_update_params.Transformations] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetUpdateResponse: + """ + Update a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._patch( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + body=maybe_transform( + { + "baseline_experiment_id": baseline_experiment_id, + "description": description, + "inputs_schema_definition": inputs_schema_definition, + "metadata": metadata, + "name": name, + "outputs_schema_definition": outputs_schema_definition, + "patch_examples": patch_examples, + "transformations": transformations, + }, + dataset_update_params.DatasetUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetUpdateResponse, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + datatype: Union[List[DataType], DataType, None] | Omit = omit, + exclude: Optional[List[Literal["example_count"]]] | Omit = omit, + exclude_corrections_datasets: bool | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByDatasetColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[Dataset]: + """ + Get all datasets by query params and owner. + + Args: + datatype: Enum for dataset data types. + + sort_by: Enum for available dataset columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/datasets", + page=SyncOffsetPaginationTopLevelArray[Dataset], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "datatype": datatype, + "exclude": exclude, + "exclude_corrections_datasets": exclude_corrections_datasets, + "limit": limit, + "metadata": metadata, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "tag_value_id": tag_value_id, + }, + dataset_list_params.DatasetListParams, + ), + ), + model=Dataset, + ) + + def delete( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._delete( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def clone( + self, + *, + source_dataset_id: str, + target_dataset_id: str, + as_of: Union[Union[str, datetime], str, None] | Omit = omit, + examples: SequenceNotStr[str] | Omit = omit, + split: Union[str, SequenceNotStr[str], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetCloneResponse: + """ + Clone a dataset. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/datasets/clone", + body=maybe_transform( + { + "source_dataset_id": source_dataset_id, + "target_dataset_id": target_dataset_id, + "as_of": as_of, + "examples": examples, + "split": split, + }, + dataset_clone_params.DatasetCloneParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetCloneResponse, + ) + + def retrieve_csv( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as CSV format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/csv", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"as_of": as_of}, dataset_retrieve_csv_params.DatasetRetrieveCsvParams), + ), + cast_to=object, + ) + + def retrieve_jsonl( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as CSV format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/jsonl", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"as_of": as_of}, dataset_retrieve_jsonl_params.DatasetRetrieveJSONLParams), + ), + cast_to=object, + ) + + def retrieve_openai( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as OpenAI Evals Jsonl format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/openai", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"as_of": as_of}, dataset_retrieve_openai_params.DatasetRetrieveOpenAIParams), + ), + cast_to=object, + ) + + def retrieve_openai_ft( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as OpenAI Jsonl format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/openai_ft", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"as_of": as_of}, dataset_retrieve_openai_ft_params.DatasetRetrieveOpenAIFtParams + ), + ), + cast_to=object, + ) + + def retrieve_version( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + tag: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetVersion: + """ + Get dataset version by as_of or exact tag. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/version", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "as_of": as_of, + "tag": tag, + }, + dataset_retrieve_version_params.DatasetRetrieveVersionParams, + ), + ), + cast_to=DatasetVersion, + ) + + def update_tags( + self, + dataset_id: str, + *, + as_of: Union[Union[str, datetime], str], + tag: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetVersion: + """ + Set a tag on a dataset version. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._put( + path_template("/api/v1/datasets/{dataset_id}/tags", dataset_id=dataset_id), + body=maybe_transform( + { + "as_of": as_of, + "tag": tag, + }, + dataset_update_tags_params.DatasetUpdateTagsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetVersion, + ) + + def upload( + self, + *, + file: FileTypes, + input_keys: SequenceNotStr[str], + data_type: DataType | Omit = omit, + description: Optional[str] | Omit = omit, + input_key_mappings: Optional[str] | Omit = omit, + inputs_schema_definition: Optional[str] | Omit = omit, + metadata_key_mappings: Optional[str] | Omit = omit, + metadata_keys: SequenceNotStr[str] | Omit = omit, + name: Optional[str] | Omit = omit, + output_key_mappings: Optional[str] | Omit = omit, + output_keys: SequenceNotStr[str] | Omit = omit, + outputs_schema_definition: Optional[str] | Omit = omit, + transformations: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Create a new dataset from a CSV or JSONL file. + + Args: + data_type: Enum for dataset data types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "input_keys": input_keys, + "data_type": data_type, + "description": description, + "input_key_mappings": input_key_mappings, + "inputs_schema_definition": inputs_schema_definition, + "metadata_key_mappings": metadata_key_mappings, + "metadata_keys": metadata_keys, + "name": name, + "output_key_mappings": output_key_mappings, + "output_keys": output_keys, + "outputs_schema_definition": outputs_schema_definition, + "transformations": transformations, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/api/v1/datasets/upload", + body=maybe_transform(body, dataset_upload_params.DatasetUploadParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + +class AsyncDatasetsResource(AsyncAPIResource): + @cached_property + def versions(self) -> AsyncVersionsResource: + return AsyncVersionsResource(self._client) + + @cached_property + def runs(self) -> AsyncRunsResource: + return AsyncRunsResource(self._client) + + @cached_property + def group(self) -> AsyncGroupResource: + return AsyncGroupResource(self._client) + + @cached_property + def experiments(self) -> AsyncExperimentsResource: + return AsyncExperimentsResource(self._client) + + @cached_property + def share(self) -> AsyncShareResource: + return AsyncShareResource(self._client) + + @cached_property + def comparative(self) -> AsyncComparativeResource: + return AsyncComparativeResource(self._client) + + @cached_property + def splits(self) -> AsyncSplitsResource: + return AsyncSplitsResource(self._client) + + @cached_property + def playground_experiment(self) -> AsyncPlaygroundExperimentResource: + return AsyncPlaygroundExperimentResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncDatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncDatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncDatasetsResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + id: Optional[str] | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + data_type: DataType | Omit = omit, + description: Optional[str] | Omit = omit, + externally_managed: Optional[bool] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + inputs_schema_definition: Optional[Dict[str, object]] | Omit = omit, + outputs_schema_definition: Optional[Dict[str, object]] | Omit = omit, + transformations: Optional[Iterable[DatasetTransformationParam]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Create a new dataset. + + Args: + data_type: Enum for dataset data types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/datasets", + body=await async_maybe_transform( + { + "name": name, + "id": id, + "created_at": created_at, + "data_type": data_type, + "description": description, + "externally_managed": externally_managed, + "extra": extra, + "inputs_schema_definition": inputs_schema_definition, + "outputs_schema_definition": outputs_schema_definition, + "transformations": transformations, + }, + dataset_create_params.DatasetCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + async def retrieve( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Get a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + async def update( + self, + dataset_id: str, + *, + baseline_experiment_id: Optional[dataset_update_params.BaselineExperimentID] | Omit = omit, + description: Optional[dataset_update_params.Description] | Omit = omit, + inputs_schema_definition: Optional[dataset_update_params.InputsSchemaDefinition] | Omit = omit, + metadata: Optional[dataset_update_params.Metadata] | Omit = omit, + name: Optional[dataset_update_params.Name] | Omit = omit, + outputs_schema_definition: Optional[dataset_update_params.OutputsSchemaDefinition] | Omit = omit, + patch_examples: Optional[Dict[str, dataset_update_params.PatchExamples]] | Omit = omit, + transformations: Optional[dataset_update_params.Transformations] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetUpdateResponse: + """ + Update a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._patch( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "baseline_experiment_id": baseline_experiment_id, + "description": description, + "inputs_schema_definition": inputs_schema_definition, + "metadata": metadata, + "name": name, + "outputs_schema_definition": outputs_schema_definition, + "patch_examples": patch_examples, + "transformations": transformations, + }, + dataset_update_params.DatasetUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetUpdateResponse, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + datatype: Union[List[DataType], DataType, None] | Omit = omit, + exclude: Optional[List[Literal["example_count"]]] | Omit = omit, + exclude_corrections_datasets: bool | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByDatasetColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Dataset, AsyncOffsetPaginationTopLevelArray[Dataset]]: + """ + Get all datasets by query params and owner. + + Args: + datatype: Enum for dataset data types. + + sort_by: Enum for available dataset columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/datasets", + page=AsyncOffsetPaginationTopLevelArray[Dataset], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "datatype": datatype, + "exclude": exclude, + "exclude_corrections_datasets": exclude_corrections_datasets, + "limit": limit, + "metadata": metadata, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "tag_value_id": tag_value_id, + }, + dataset_list_params.DatasetListParams, + ), + ), + model=Dataset, + ) + + async def delete( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._delete( + path_template("/api/v1/datasets/{dataset_id}", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def clone( + self, + *, + source_dataset_id: str, + target_dataset_id: str, + as_of: Union[Union[str, datetime], str, None] | Omit = omit, + examples: SequenceNotStr[str] | Omit = omit, + split: Union[str, SequenceNotStr[str], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetCloneResponse: + """ + Clone a dataset. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/datasets/clone", + body=await async_maybe_transform( + { + "source_dataset_id": source_dataset_id, + "target_dataset_id": target_dataset_id, + "as_of": as_of, + "examples": examples, + "split": split, + }, + dataset_clone_params.DatasetCloneParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetCloneResponse, + ) + + async def retrieve_csv( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as CSV format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/csv", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"as_of": as_of}, dataset_retrieve_csv_params.DatasetRetrieveCsvParams + ), + ), + cast_to=object, + ) + + async def retrieve_jsonl( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as CSV format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/jsonl", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"as_of": as_of}, dataset_retrieve_jsonl_params.DatasetRetrieveJSONLParams + ), + ), + cast_to=object, + ) + + async def retrieve_openai( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as OpenAI Evals Jsonl format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/openai", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"as_of": as_of}, dataset_retrieve_openai_params.DatasetRetrieveOpenAIParams + ), + ), + cast_to=object, + ) + + async def retrieve_openai_ft( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Download a dataset as OpenAI Jsonl format. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/openai_ft", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"as_of": as_of}, dataset_retrieve_openai_ft_params.DatasetRetrieveOpenAIFtParams + ), + ), + cast_to=object, + ) + + async def retrieve_version( + self, + dataset_id: str, + *, + as_of: Union[str, datetime, None] | Omit = omit, + tag: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetVersion: + """ + Get dataset version by as_of or exact tag. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/version", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "as_of": as_of, + "tag": tag, + }, + dataset_retrieve_version_params.DatasetRetrieveVersionParams, + ), + ), + cast_to=DatasetVersion, + ) + + async def update_tags( + self, + dataset_id: str, + *, + as_of: Union[Union[str, datetime], str], + tag: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetVersion: + """ + Set a tag on a dataset version. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._put( + path_template("/api/v1/datasets/{dataset_id}/tags", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "as_of": as_of, + "tag": tag, + }, + dataset_update_tags_params.DatasetUpdateTagsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetVersion, + ) + + async def upload( + self, + *, + file: FileTypes, + input_keys: SequenceNotStr[str], + data_type: DataType | Omit = omit, + description: Optional[str] | Omit = omit, + input_key_mappings: Optional[str] | Omit = omit, + inputs_schema_definition: Optional[str] | Omit = omit, + metadata_key_mappings: Optional[str] | Omit = omit, + metadata_keys: SequenceNotStr[str] | Omit = omit, + name: Optional[str] | Omit = omit, + output_key_mappings: Optional[str] | Omit = omit, + output_keys: SequenceNotStr[str] | Omit = omit, + outputs_schema_definition: Optional[str] | Omit = omit, + transformations: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Dataset: + """ + Create a new dataset from a CSV or JSONL file. + + Args: + data_type: Enum for dataset data types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_with_paths( + { + "file": file, + "input_keys": input_keys, + "data_type": data_type, + "description": description, + "input_key_mappings": input_key_mappings, + "inputs_schema_definition": inputs_schema_definition, + "metadata_key_mappings": metadata_key_mappings, + "metadata_keys": metadata_keys, + "name": name, + "output_key_mappings": output_key_mappings, + "output_keys": output_keys, + "outputs_schema_definition": outputs_schema_definition, + "transformations": transformations, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/api/v1/datasets/upload", + body=await async_maybe_transform(body, dataset_upload_params.DatasetUploadParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Dataset, + ) + + +class DatasetsResourceWithRawResponse: + def __init__(self, datasets: DatasetsResource) -> None: + self._datasets = datasets + + self.create = to_raw_response_wrapper( + datasets.create, + ) + self.retrieve = to_raw_response_wrapper( + datasets.retrieve, + ) + self.update = to_raw_response_wrapper( + datasets.update, + ) + self.list = to_raw_response_wrapper( + datasets.list, + ) + self.delete = to_raw_response_wrapper( + datasets.delete, + ) + self.clone = to_raw_response_wrapper( + datasets.clone, + ) + self.retrieve_csv = to_raw_response_wrapper( + datasets.retrieve_csv, + ) + self.retrieve_jsonl = to_raw_response_wrapper( + datasets.retrieve_jsonl, + ) + self.retrieve_openai = to_raw_response_wrapper( + datasets.retrieve_openai, + ) + self.retrieve_openai_ft = to_raw_response_wrapper( + datasets.retrieve_openai_ft, + ) + self.retrieve_version = to_raw_response_wrapper( + datasets.retrieve_version, + ) + self.update_tags = to_raw_response_wrapper( + datasets.update_tags, + ) + self.upload = to_raw_response_wrapper( + datasets.upload, + ) + + @cached_property + def versions(self) -> VersionsResourceWithRawResponse: + return VersionsResourceWithRawResponse(self._datasets.versions) + + @cached_property + def runs(self) -> RunsResourceWithRawResponse: + return RunsResourceWithRawResponse(self._datasets.runs) + + @cached_property + def group(self) -> GroupResourceWithRawResponse: + return GroupResourceWithRawResponse(self._datasets.group) + + @cached_property + def experiments(self) -> ExperimentsResourceWithRawResponse: + return ExperimentsResourceWithRawResponse(self._datasets.experiments) + + @cached_property + def share(self) -> ShareResourceWithRawResponse: + return ShareResourceWithRawResponse(self._datasets.share) + + @cached_property + def comparative(self) -> ComparativeResourceWithRawResponse: + return ComparativeResourceWithRawResponse(self._datasets.comparative) + + @cached_property + def splits(self) -> SplitsResourceWithRawResponse: + return SplitsResourceWithRawResponse(self._datasets.splits) + + @cached_property + def playground_experiment(self) -> PlaygroundExperimentResourceWithRawResponse: + return PlaygroundExperimentResourceWithRawResponse(self._datasets.playground_experiment) + + +class AsyncDatasetsResourceWithRawResponse: + def __init__(self, datasets: AsyncDatasetsResource) -> None: + self._datasets = datasets + + self.create = async_to_raw_response_wrapper( + datasets.create, + ) + self.retrieve = async_to_raw_response_wrapper( + datasets.retrieve, + ) + self.update = async_to_raw_response_wrapper( + datasets.update, + ) + self.list = async_to_raw_response_wrapper( + datasets.list, + ) + self.delete = async_to_raw_response_wrapper( + datasets.delete, + ) + self.clone = async_to_raw_response_wrapper( + datasets.clone, + ) + self.retrieve_csv = async_to_raw_response_wrapper( + datasets.retrieve_csv, + ) + self.retrieve_jsonl = async_to_raw_response_wrapper( + datasets.retrieve_jsonl, + ) + self.retrieve_openai = async_to_raw_response_wrapper( + datasets.retrieve_openai, + ) + self.retrieve_openai_ft = async_to_raw_response_wrapper( + datasets.retrieve_openai_ft, + ) + self.retrieve_version = async_to_raw_response_wrapper( + datasets.retrieve_version, + ) + self.update_tags = async_to_raw_response_wrapper( + datasets.update_tags, + ) + self.upload = async_to_raw_response_wrapper( + datasets.upload, + ) + + @cached_property + def versions(self) -> AsyncVersionsResourceWithRawResponse: + return AsyncVersionsResourceWithRawResponse(self._datasets.versions) + + @cached_property + def runs(self) -> AsyncRunsResourceWithRawResponse: + return AsyncRunsResourceWithRawResponse(self._datasets.runs) + + @cached_property + def group(self) -> AsyncGroupResourceWithRawResponse: + return AsyncGroupResourceWithRawResponse(self._datasets.group) + + @cached_property + def experiments(self) -> AsyncExperimentsResourceWithRawResponse: + return AsyncExperimentsResourceWithRawResponse(self._datasets.experiments) + + @cached_property + def share(self) -> AsyncShareResourceWithRawResponse: + return AsyncShareResourceWithRawResponse(self._datasets.share) + + @cached_property + def comparative(self) -> AsyncComparativeResourceWithRawResponse: + return AsyncComparativeResourceWithRawResponse(self._datasets.comparative) + + @cached_property + def splits(self) -> AsyncSplitsResourceWithRawResponse: + return AsyncSplitsResourceWithRawResponse(self._datasets.splits) + + @cached_property + def playground_experiment(self) -> AsyncPlaygroundExperimentResourceWithRawResponse: + return AsyncPlaygroundExperimentResourceWithRawResponse(self._datasets.playground_experiment) + + +class DatasetsResourceWithStreamingResponse: + def __init__(self, datasets: DatasetsResource) -> None: + self._datasets = datasets + + self.create = to_streamed_response_wrapper( + datasets.create, + ) + self.retrieve = to_streamed_response_wrapper( + datasets.retrieve, + ) + self.update = to_streamed_response_wrapper( + datasets.update, + ) + self.list = to_streamed_response_wrapper( + datasets.list, + ) + self.delete = to_streamed_response_wrapper( + datasets.delete, + ) + self.clone = to_streamed_response_wrapper( + datasets.clone, + ) + self.retrieve_csv = to_streamed_response_wrapper( + datasets.retrieve_csv, + ) + self.retrieve_jsonl = to_streamed_response_wrapper( + datasets.retrieve_jsonl, + ) + self.retrieve_openai = to_streamed_response_wrapper( + datasets.retrieve_openai, + ) + self.retrieve_openai_ft = to_streamed_response_wrapper( + datasets.retrieve_openai_ft, + ) + self.retrieve_version = to_streamed_response_wrapper( + datasets.retrieve_version, + ) + self.update_tags = to_streamed_response_wrapper( + datasets.update_tags, + ) + self.upload = to_streamed_response_wrapper( + datasets.upload, + ) + + @cached_property + def versions(self) -> VersionsResourceWithStreamingResponse: + return VersionsResourceWithStreamingResponse(self._datasets.versions) + + @cached_property + def runs(self) -> RunsResourceWithStreamingResponse: + return RunsResourceWithStreamingResponse(self._datasets.runs) + + @cached_property + def group(self) -> GroupResourceWithStreamingResponse: + return GroupResourceWithStreamingResponse(self._datasets.group) + + @cached_property + def experiments(self) -> ExperimentsResourceWithStreamingResponse: + return ExperimentsResourceWithStreamingResponse(self._datasets.experiments) + + @cached_property + def share(self) -> ShareResourceWithStreamingResponse: + return ShareResourceWithStreamingResponse(self._datasets.share) + + @cached_property + def comparative(self) -> ComparativeResourceWithStreamingResponse: + return ComparativeResourceWithStreamingResponse(self._datasets.comparative) + + @cached_property + def splits(self) -> SplitsResourceWithStreamingResponse: + return SplitsResourceWithStreamingResponse(self._datasets.splits) + + @cached_property + def playground_experiment(self) -> PlaygroundExperimentResourceWithStreamingResponse: + return PlaygroundExperimentResourceWithStreamingResponse(self._datasets.playground_experiment) + + +class AsyncDatasetsResourceWithStreamingResponse: + def __init__(self, datasets: AsyncDatasetsResource) -> None: + self._datasets = datasets + + self.create = async_to_streamed_response_wrapper( + datasets.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + datasets.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + datasets.update, + ) + self.list = async_to_streamed_response_wrapper( + datasets.list, + ) + self.delete = async_to_streamed_response_wrapper( + datasets.delete, + ) + self.clone = async_to_streamed_response_wrapper( + datasets.clone, + ) + self.retrieve_csv = async_to_streamed_response_wrapper( + datasets.retrieve_csv, + ) + self.retrieve_jsonl = async_to_streamed_response_wrapper( + datasets.retrieve_jsonl, + ) + self.retrieve_openai = async_to_streamed_response_wrapper( + datasets.retrieve_openai, + ) + self.retrieve_openai_ft = async_to_streamed_response_wrapper( + datasets.retrieve_openai_ft, + ) + self.retrieve_version = async_to_streamed_response_wrapper( + datasets.retrieve_version, + ) + self.update_tags = async_to_streamed_response_wrapper( + datasets.update_tags, + ) + self.upload = async_to_streamed_response_wrapper( + datasets.upload, + ) + + @cached_property + def versions(self) -> AsyncVersionsResourceWithStreamingResponse: + return AsyncVersionsResourceWithStreamingResponse(self._datasets.versions) + + @cached_property + def runs(self) -> AsyncRunsResourceWithStreamingResponse: + return AsyncRunsResourceWithStreamingResponse(self._datasets.runs) + + @cached_property + def group(self) -> AsyncGroupResourceWithStreamingResponse: + return AsyncGroupResourceWithStreamingResponse(self._datasets.group) + + @cached_property + def experiments(self) -> AsyncExperimentsResourceWithStreamingResponse: + return AsyncExperimentsResourceWithStreamingResponse(self._datasets.experiments) + + @cached_property + def share(self) -> AsyncShareResourceWithStreamingResponse: + return AsyncShareResourceWithStreamingResponse(self._datasets.share) + + @cached_property + def comparative(self) -> AsyncComparativeResourceWithStreamingResponse: + return AsyncComparativeResourceWithStreamingResponse(self._datasets.comparative) + + @cached_property + def splits(self) -> AsyncSplitsResourceWithStreamingResponse: + return AsyncSplitsResourceWithStreamingResponse(self._datasets.splits) + + @cached_property + def playground_experiment(self) -> AsyncPlaygroundExperimentResourceWithStreamingResponse: + return AsyncPlaygroundExperimentResourceWithStreamingResponse(self._datasets.playground_experiment) diff --git a/python/langsmith_api/resources/datasets/experiments.py b/python/langsmith_api/resources/datasets/experiments.py new file mode 100644 index 000000000..a3b5983f6 --- /dev/null +++ b/python/langsmith_api/resources/datasets/experiments.py @@ -0,0 +1,209 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import experiment_grouped_params + +__all__ = ["ExperimentsResource", "AsyncExperimentsResource"] + + +class ExperimentsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExperimentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ExperimentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExperimentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ExperimentsResourceWithStreamingResponse(self) + + def grouped( + self, + dataset_id: str, + *, + metadata_keys: SequenceNotStr[str], + dataset_version: Optional[str] | Omit = omit, + experiment_limit: int | Omit = omit, + filter: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + use_approx_stats: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Stream grouped and aggregated experiments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._post( + path_template("/api/v1/datasets/{dataset_id}/experiments/grouped", dataset_id=dataset_id), + body=maybe_transform( + { + "metadata_keys": metadata_keys, + "dataset_version": dataset_version, + "experiment_limit": experiment_limit, + "filter": filter, + "name_contains": name_contains, + "stats_start_time": stats_start_time, + "tag_value_id": tag_value_id, + "use_approx_stats": use_approx_stats, + }, + experiment_grouped_params.ExperimentGroupedParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncExperimentsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExperimentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncExperimentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExperimentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncExperimentsResourceWithStreamingResponse(self) + + async def grouped( + self, + dataset_id: str, + *, + metadata_keys: SequenceNotStr[str], + dataset_version: Optional[str] | Omit = omit, + experiment_limit: int | Omit = omit, + filter: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + use_approx_stats: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Stream grouped and aggregated experiments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._post( + path_template("/api/v1/datasets/{dataset_id}/experiments/grouped", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "metadata_keys": metadata_keys, + "dataset_version": dataset_version, + "experiment_limit": experiment_limit, + "filter": filter, + "name_contains": name_contains, + "stats_start_time": stats_start_time, + "tag_value_id": tag_value_id, + "use_approx_stats": use_approx_stats, + }, + experiment_grouped_params.ExperimentGroupedParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ExperimentsResourceWithRawResponse: + def __init__(self, experiments: ExperimentsResource) -> None: + self._experiments = experiments + + self.grouped = to_raw_response_wrapper( + experiments.grouped, + ) + + +class AsyncExperimentsResourceWithRawResponse: + def __init__(self, experiments: AsyncExperimentsResource) -> None: + self._experiments = experiments + + self.grouped = async_to_raw_response_wrapper( + experiments.grouped, + ) + + +class ExperimentsResourceWithStreamingResponse: + def __init__(self, experiments: ExperimentsResource) -> None: + self._experiments = experiments + + self.grouped = to_streamed_response_wrapper( + experiments.grouped, + ) + + +class AsyncExperimentsResourceWithStreamingResponse: + def __init__(self, experiments: AsyncExperimentsResource) -> None: + self._experiments = experiments + + self.grouped = async_to_streamed_response_wrapper( + experiments.grouped, + ) diff --git a/python/langsmith_api/resources/datasets/group.py b/python/langsmith_api/resources/datasets/group.py new file mode 100644 index 000000000..01224fe5b --- /dev/null +++ b/python/langsmith_api/resources/datasets/group.py @@ -0,0 +1,212 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import group_runs_params +from ...types.datasets.group_runs_response import GroupRunsResponse + +__all__ = ["GroupResource", "AsyncGroupResource"] + + +class GroupResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GroupResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return GroupResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroupResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return GroupResourceWithStreamingResponse(self) + + def runs( + self, + dataset_id: str, + *, + group_by: Literal["run_metadata", "example_metadata"], + metadata_key: str, + session_ids: SequenceNotStr[str], + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + per_group_limit: int | Omit = omit, + preview: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupRunsResponse: + """ + Fetch examples for a dataset, and fetch the runs for each example if they are + associated with the given session_ids. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._post( + path_template("/api/v1/datasets/{dataset_id}/group/runs", dataset_id=dataset_id), + body=maybe_transform( + { + "group_by": group_by, + "metadata_key": metadata_key, + "session_ids": session_ids, + "filters": filters, + "limit": limit, + "offset": offset, + "per_group_limit": per_group_limit, + "preview": preview, + }, + group_runs_params.GroupRunsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GroupRunsResponse, + ) + + +class AsyncGroupResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGroupResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncGroupResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroupResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncGroupResourceWithStreamingResponse(self) + + async def runs( + self, + dataset_id: str, + *, + group_by: Literal["run_metadata", "example_metadata"], + metadata_key: str, + session_ids: SequenceNotStr[str], + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + per_group_limit: int | Omit = omit, + preview: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupRunsResponse: + """ + Fetch examples for a dataset, and fetch the runs for each example if they are + associated with the given session_ids. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._post( + path_template("/api/v1/datasets/{dataset_id}/group/runs", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "group_by": group_by, + "metadata_key": metadata_key, + "session_ids": session_ids, + "filters": filters, + "limit": limit, + "offset": offset, + "per_group_limit": per_group_limit, + "preview": preview, + }, + group_runs_params.GroupRunsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GroupRunsResponse, + ) + + +class GroupResourceWithRawResponse: + def __init__(self, group: GroupResource) -> None: + self._group = group + + self.runs = to_raw_response_wrapper( + group.runs, + ) + + +class AsyncGroupResourceWithRawResponse: + def __init__(self, group: AsyncGroupResource) -> None: + self._group = group + + self.runs = async_to_raw_response_wrapper( + group.runs, + ) + + +class GroupResourceWithStreamingResponse: + def __init__(self, group: GroupResource) -> None: + self._group = group + + self.runs = to_streamed_response_wrapper( + group.runs, + ) + + +class AsyncGroupResourceWithStreamingResponse: + def __init__(self, group: AsyncGroupResource) -> None: + self._group = group + + self.runs = async_to_streamed_response_wrapper( + group.runs, + ) diff --git a/python/langsmith_api/resources/datasets/playground_experiment.py b/python/langsmith_api/resources/datasets/playground_experiment.py new file mode 100644 index 000000000..36e11140d --- /dev/null +++ b/python/langsmith_api/resources/datasets/playground_experiment.py @@ -0,0 +1,506 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import ( + RunnerContextEnum, + playground_experiment_batch_params, + playground_experiment_stream_params, +) +from ...types.datasets.runner_context_enum import RunnerContextEnum +from ...types.datasets.runnable_config_param import RunnableConfigParam + +__all__ = ["PlaygroundExperimentResource", "AsyncPlaygroundExperimentResource"] + + +class PlaygroundExperimentResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PlaygroundExperimentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return PlaygroundExperimentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PlaygroundExperimentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return PlaygroundExperimentResourceWithStreamingResponse(self) + + def batch( + self, + *, + dataset_id: str, + manifest: object, + options: RunnableConfigParam, + project_name: str, + secrets: Dict[str, str], + batch_size: Optional[int] | Omit = omit, + commit: Optional[str] | Omit = omit, + dataset_splits: Optional[SequenceNotStr[str]] | Omit = omit, + evaluator_rules: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + owner: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + repetitions: int | Omit = omit, + repo_handle: Optional[str] | Omit = omit, + repo_id: Optional[str] | Omit = omit, + requests_per_second: Optional[int] | Omit = omit, + run_id: Optional[str] | Omit = omit, + runner_context: Optional[RunnerContextEnum] | Omit = omit, + tool_choice: Optional[str] | Omit = omit, + tools: Optional[Iterable[object]] | Omit = omit, + use_or_fallback_to_workspace_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Dataset Handler + + Args: + options: Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/datasets/playground_experiment/batch", + body=maybe_transform( + { + "dataset_id": dataset_id, + "manifest": manifest, + "options": options, + "project_name": project_name, + "secrets": secrets, + "batch_size": batch_size, + "commit": commit, + "dataset_splits": dataset_splits, + "evaluator_rules": evaluator_rules, + "metadata": metadata, + "owner": owner, + "parallel_tool_calls": parallel_tool_calls, + "repetitions": repetitions, + "repo_handle": repo_handle, + "repo_id": repo_id, + "requests_per_second": requests_per_second, + "run_id": run_id, + "runner_context": runner_context, + "tool_choice": tool_choice, + "tools": tools, + "use_or_fallback_to_workspace_secrets": use_or_fallback_to_workspace_secrets, + }, + playground_experiment_batch_params.PlaygroundExperimentBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def stream( + self, + *, + dataset_id: str, + manifest: object, + options: RunnableConfigParam, + project_name: str, + secrets: Dict[str, str], + commit: Optional[str] | Omit = omit, + dataset_splits: Optional[SequenceNotStr[str]] | Omit = omit, + evaluator_rules: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + owner: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + repetitions: int | Omit = omit, + repo_handle: Optional[str] | Omit = omit, + repo_id: Optional[str] | Omit = omit, + requests_per_second: Optional[int] | Omit = omit, + run_id: Optional[str] | Omit = omit, + runner_context: Optional[RunnerContextEnum] | Omit = omit, + tool_choice: Optional[str] | Omit = omit, + tools: Optional[Iterable[object]] | Omit = omit, + use_or_fallback_to_workspace_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Stream Dataset Handler + + Args: + options: Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/datasets/playground_experiment/stream", + body=maybe_transform( + { + "dataset_id": dataset_id, + "manifest": manifest, + "options": options, + "project_name": project_name, + "secrets": secrets, + "commit": commit, + "dataset_splits": dataset_splits, + "evaluator_rules": evaluator_rules, + "metadata": metadata, + "owner": owner, + "parallel_tool_calls": parallel_tool_calls, + "repetitions": repetitions, + "repo_handle": repo_handle, + "repo_id": repo_id, + "requests_per_second": requests_per_second, + "run_id": run_id, + "runner_context": runner_context, + "tool_choice": tool_choice, + "tools": tools, + "use_or_fallback_to_workspace_secrets": use_or_fallback_to_workspace_secrets, + }, + playground_experiment_stream_params.PlaygroundExperimentStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncPlaygroundExperimentResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPlaygroundExperimentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncPlaygroundExperimentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPlaygroundExperimentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncPlaygroundExperimentResourceWithStreamingResponse(self) + + async def batch( + self, + *, + dataset_id: str, + manifest: object, + options: RunnableConfigParam, + project_name: str, + secrets: Dict[str, str], + batch_size: Optional[int] | Omit = omit, + commit: Optional[str] | Omit = omit, + dataset_splits: Optional[SequenceNotStr[str]] | Omit = omit, + evaluator_rules: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + owner: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + repetitions: int | Omit = omit, + repo_handle: Optional[str] | Omit = omit, + repo_id: Optional[str] | Omit = omit, + requests_per_second: Optional[int] | Omit = omit, + run_id: Optional[str] | Omit = omit, + runner_context: Optional[RunnerContextEnum] | Omit = omit, + tool_choice: Optional[str] | Omit = omit, + tools: Optional[Iterable[object]] | Omit = omit, + use_or_fallback_to_workspace_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Dataset Handler + + Args: + options: Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/datasets/playground_experiment/batch", + body=await async_maybe_transform( + { + "dataset_id": dataset_id, + "manifest": manifest, + "options": options, + "project_name": project_name, + "secrets": secrets, + "batch_size": batch_size, + "commit": commit, + "dataset_splits": dataset_splits, + "evaluator_rules": evaluator_rules, + "metadata": metadata, + "owner": owner, + "parallel_tool_calls": parallel_tool_calls, + "repetitions": repetitions, + "repo_handle": repo_handle, + "repo_id": repo_id, + "requests_per_second": requests_per_second, + "run_id": run_id, + "runner_context": runner_context, + "tool_choice": tool_choice, + "tools": tools, + "use_or_fallback_to_workspace_secrets": use_or_fallback_to_workspace_secrets, + }, + playground_experiment_batch_params.PlaygroundExperimentBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def stream( + self, + *, + dataset_id: str, + manifest: object, + options: RunnableConfigParam, + project_name: str, + secrets: Dict[str, str], + commit: Optional[str] | Omit = omit, + dataset_splits: Optional[SequenceNotStr[str]] | Omit = omit, + evaluator_rules: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + owner: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + repetitions: int | Omit = omit, + repo_handle: Optional[str] | Omit = omit, + repo_id: Optional[str] | Omit = omit, + requests_per_second: Optional[int] | Omit = omit, + run_id: Optional[str] | Omit = omit, + runner_context: Optional[RunnerContextEnum] | Omit = omit, + tool_choice: Optional[str] | Omit = omit, + tools: Optional[Iterable[object]] | Omit = omit, + use_or_fallback_to_workspace_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Stream Dataset Handler + + Args: + options: Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/datasets/playground_experiment/stream", + body=await async_maybe_transform( + { + "dataset_id": dataset_id, + "manifest": manifest, + "options": options, + "project_name": project_name, + "secrets": secrets, + "commit": commit, + "dataset_splits": dataset_splits, + "evaluator_rules": evaluator_rules, + "metadata": metadata, + "owner": owner, + "parallel_tool_calls": parallel_tool_calls, + "repetitions": repetitions, + "repo_handle": repo_handle, + "repo_id": repo_id, + "requests_per_second": requests_per_second, + "run_id": run_id, + "runner_context": runner_context, + "tool_choice": tool_choice, + "tools": tools, + "use_or_fallback_to_workspace_secrets": use_or_fallback_to_workspace_secrets, + }, + playground_experiment_stream_params.PlaygroundExperimentStreamParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class PlaygroundExperimentResourceWithRawResponse: + def __init__(self, playground_experiment: PlaygroundExperimentResource) -> None: + self._playground_experiment = playground_experiment + + self.batch = to_raw_response_wrapper( + playground_experiment.batch, + ) + self.stream = to_raw_response_wrapper( + playground_experiment.stream, + ) + + +class AsyncPlaygroundExperimentResourceWithRawResponse: + def __init__(self, playground_experiment: AsyncPlaygroundExperimentResource) -> None: + self._playground_experiment = playground_experiment + + self.batch = async_to_raw_response_wrapper( + playground_experiment.batch, + ) + self.stream = async_to_raw_response_wrapper( + playground_experiment.stream, + ) + + +class PlaygroundExperimentResourceWithStreamingResponse: + def __init__(self, playground_experiment: PlaygroundExperimentResource) -> None: + self._playground_experiment = playground_experiment + + self.batch = to_streamed_response_wrapper( + playground_experiment.batch, + ) + self.stream = to_streamed_response_wrapper( + playground_experiment.stream, + ) + + +class AsyncPlaygroundExperimentResourceWithStreamingResponse: + def __init__(self, playground_experiment: AsyncPlaygroundExperimentResource) -> None: + self._playground_experiment = playground_experiment + + self.batch = async_to_streamed_response_wrapper( + playground_experiment.batch, + ) + self.stream = async_to_streamed_response_wrapper( + playground_experiment.stream, + ) diff --git a/python/langsmith_api/resources/datasets/runs.py b/python/langsmith_api/resources/datasets/runs.py new file mode 100644 index 000000000..0cbd3c710 --- /dev/null +++ b/python/langsmith_api/resources/datasets/runs.py @@ -0,0 +1,350 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import run_delta_params, run_create_params +from ...types.datasets.run_create_response import RunCreateResponse +from ...types.datasets.session_feedback_delta import SessionFeedbackDelta +from ...types.datasets.sort_params_for_runs_comparison_view_param import SortParamsForRunsComparisonViewParam + +__all__ = ["RunsResource", "AsyncRunsResource"] + + +class RunsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return RunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return RunsResourceWithStreamingResponse(self) + + def create( + self, + dataset_id: str, + *, + session_ids: SequenceNotStr[str], + format: Optional[Literal["csv"]] | Omit = omit, + comparative_experiment_id: Optional[str] | Omit = omit, + example_ids: Optional[SequenceNotStr[str]] | Omit = omit, + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + include_annotator_detail: bool | Omit = omit, + limit: Optional[int] | Omit = omit, + offset: int | Omit = omit, + preview: bool | Omit = omit, + sort_params: Optional[SortParamsForRunsComparisonViewParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[RunCreateResponse]: + """ + Fetch examples for a dataset, and fetch the runs for each example if they are + associated with the given session_ids. + + Args: + format: Response format, e.g., 'csv' + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._post( + path_template("/api/v1/datasets/{dataset_id}/runs", dataset_id=dataset_id), + body=maybe_transform( + { + "session_ids": session_ids, + "comparative_experiment_id": comparative_experiment_id, + "example_ids": example_ids, + "filters": filters, + "include_annotator_detail": include_annotator_detail, + "limit": limit, + "offset": offset, + "preview": preview, + "sort_params": sort_params, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"format": format}, run_create_params.RunCreateParams), + ), + cast_to=RunCreateResponse, + ) + + def delta( + self, + dataset_id: str, + *, + baseline_session_id: str, + comparison_session_ids: SequenceNotStr[str], + feedback_key: str, + comparative_experiment_id: Optional[str] | Omit = omit, + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionFeedbackDelta: + """ + Fetch the number of regressions/improvements for each example in a dataset, + between sessions[0] and sessions[1]. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._post( + path_template("/api/v1/datasets/{dataset_id}/runs/delta", dataset_id=dataset_id), + body=maybe_transform( + { + "baseline_session_id": baseline_session_id, + "comparison_session_ids": comparison_session_ids, + "feedback_key": feedback_key, + "comparative_experiment_id": comparative_experiment_id, + "filters": filters, + "limit": limit, + "offset": offset, + }, + run_delta_params.RunDeltaParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionFeedbackDelta, + ) + + +class AsyncRunsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncRunsResourceWithStreamingResponse(self) + + async def create( + self, + dataset_id: str, + *, + session_ids: SequenceNotStr[str], + format: Optional[Literal["csv"]] | Omit = omit, + comparative_experiment_id: Optional[str] | Omit = omit, + example_ids: Optional[SequenceNotStr[str]] | Omit = omit, + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + include_annotator_detail: bool | Omit = omit, + limit: Optional[int] | Omit = omit, + offset: int | Omit = omit, + preview: bool | Omit = omit, + sort_params: Optional[SortParamsForRunsComparisonViewParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[RunCreateResponse]: + """ + Fetch examples for a dataset, and fetch the runs for each example if they are + associated with the given session_ids. + + Args: + format: Response format, e.g., 'csv' + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._post( + path_template("/api/v1/datasets/{dataset_id}/runs", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "session_ids": session_ids, + "comparative_experiment_id": comparative_experiment_id, + "example_ids": example_ids, + "filters": filters, + "include_annotator_detail": include_annotator_detail, + "limit": limit, + "offset": offset, + "preview": preview, + "sort_params": sort_params, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"format": format}, run_create_params.RunCreateParams), + ), + cast_to=RunCreateResponse, + ) + + async def delta( + self, + dataset_id: str, + *, + baseline_session_id: str, + comparison_session_ids: SequenceNotStr[str], + feedback_key: str, + comparative_experiment_id: Optional[str] | Omit = omit, + filters: Optional[Dict[str, SequenceNotStr[str]]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionFeedbackDelta: + """ + Fetch the number of regressions/improvements for each example in a dataset, + between sessions[0] and sessions[1]. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._post( + path_template("/api/v1/datasets/{dataset_id}/runs/delta", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "baseline_session_id": baseline_session_id, + "comparison_session_ids": comparison_session_ids, + "feedback_key": feedback_key, + "comparative_experiment_id": comparative_experiment_id, + "filters": filters, + "limit": limit, + "offset": offset, + }, + run_delta_params.RunDeltaParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SessionFeedbackDelta, + ) + + +class RunsResourceWithRawResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_raw_response_wrapper( + runs.create, + ) + self.delta = to_raw_response_wrapper( + runs.delta, + ) + + +class AsyncRunsResourceWithRawResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_raw_response_wrapper( + runs.create, + ) + self.delta = async_to_raw_response_wrapper( + runs.delta, + ) + + +class RunsResourceWithStreamingResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.delta = to_streamed_response_wrapper( + runs.delta, + ) + + +class AsyncRunsResourceWithStreamingResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_streamed_response_wrapper( + runs.create, + ) + self.delta = async_to_streamed_response_wrapper( + runs.delta, + ) diff --git a/python/langsmith_api/resources/datasets/share.py b/python/langsmith_api/resources/datasets/share.py new file mode 100644 index 000000000..6b76f524d --- /dev/null +++ b/python/langsmith_api/resources/datasets/share.py @@ -0,0 +1,335 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import share_create_params +from ...types.datasets.dataset_share_schema import DatasetShareSchema + +__all__ = ["ShareResource", "AsyncShareResource"] + + +class ShareResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ShareResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ShareResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ShareResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ShareResourceWithStreamingResponse(self) + + def create( + self, + dataset_id: str, + *, + share_projects: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetShareSchema: + """ + Share a dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._put( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"share_projects": share_projects}, share_create_params.ShareCreateParams), + ), + cast_to=DatasetShareSchema, + ) + + def retrieve( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[DatasetShareSchema]: + """ + Get the state of sharing a dataset + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetShareSchema, + ) + + def delete_all( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Unshare a dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._delete( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncShareResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncShareResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncShareResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncShareResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncShareResourceWithStreamingResponse(self) + + async def create( + self, + dataset_id: str, + *, + share_projects: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetShareSchema: + """ + Share a dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._put( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"share_projects": share_projects}, share_create_params.ShareCreateParams + ), + ), + cast_to=DatasetShareSchema, + ) + + async def retrieve( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Optional[DatasetShareSchema]: + """ + Get the state of sharing a dataset + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DatasetShareSchema, + ) + + async def delete_all( + self, + dataset_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Unshare a dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._delete( + path_template("/api/v1/datasets/{dataset_id}/share", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ShareResourceWithRawResponse: + def __init__(self, share: ShareResource) -> None: + self._share = share + + self.create = to_raw_response_wrapper( + share.create, + ) + self.retrieve = to_raw_response_wrapper( + share.retrieve, + ) + self.delete_all = to_raw_response_wrapper( + share.delete_all, + ) + + +class AsyncShareResourceWithRawResponse: + def __init__(self, share: AsyncShareResource) -> None: + self._share = share + + self.create = async_to_raw_response_wrapper( + share.create, + ) + self.retrieve = async_to_raw_response_wrapper( + share.retrieve, + ) + self.delete_all = async_to_raw_response_wrapper( + share.delete_all, + ) + + +class ShareResourceWithStreamingResponse: + def __init__(self, share: ShareResource) -> None: + self._share = share + + self.create = to_streamed_response_wrapper( + share.create, + ) + self.retrieve = to_streamed_response_wrapper( + share.retrieve, + ) + self.delete_all = to_streamed_response_wrapper( + share.delete_all, + ) + + +class AsyncShareResourceWithStreamingResponse: + def __init__(self, share: AsyncShareResource) -> None: + self._share = share + + self.create = async_to_streamed_response_wrapper( + share.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + share.retrieve, + ) + self.delete_all = async_to_streamed_response_wrapper( + share.delete_all, + ) diff --git a/python/langsmith_api/resources/datasets/splits.py b/python/langsmith_api/resources/datasets/splits.py new file mode 100644 index 000000000..fff68ea04 --- /dev/null +++ b/python/langsmith_api/resources/datasets/splits.py @@ -0,0 +1,285 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.datasets import split_create_params, split_retrieve_params +from ...types.datasets.split_create_response import SplitCreateResponse +from ...types.datasets.split_retrieve_response import SplitRetrieveResponse + +__all__ = ["SplitsResource", "AsyncSplitsResource"] + + +class SplitsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SplitsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return SplitsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SplitsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return SplitsResourceWithStreamingResponse(self) + + def create( + self, + dataset_id: str, + *, + examples: SequenceNotStr[str], + split_name: str, + remove: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SplitCreateResponse: + """ + Update Dataset Splits + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._put( + path_template("/api/v1/datasets/{dataset_id}/splits", dataset_id=dataset_id), + body=maybe_transform( + { + "examples": examples, + "split_name": split_name, + "remove": remove, + }, + split_create_params.SplitCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SplitCreateResponse, + ) + + def retrieve( + self, + dataset_id: str, + *, + as_of: Union[Union[str, datetime], str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SplitRetrieveResponse: + """ + Get Dataset Splits + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/splits", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"as_of": as_of}, split_retrieve_params.SplitRetrieveParams), + ), + cast_to=SplitRetrieveResponse, + ) + + +class AsyncSplitsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSplitsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncSplitsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSplitsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncSplitsResourceWithStreamingResponse(self) + + async def create( + self, + dataset_id: str, + *, + examples: SequenceNotStr[str], + split_name: str, + remove: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SplitCreateResponse: + """ + Update Dataset Splits + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._put( + path_template("/api/v1/datasets/{dataset_id}/splits", dataset_id=dataset_id), + body=await async_maybe_transform( + { + "examples": examples, + "split_name": split_name, + "remove": remove, + }, + split_create_params.SplitCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SplitCreateResponse, + ) + + async def retrieve( + self, + dataset_id: str, + *, + as_of: Union[Union[str, datetime], str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SplitRetrieveResponse: + """ + Get Dataset Splits + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/splits", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"as_of": as_of}, split_retrieve_params.SplitRetrieveParams), + ), + cast_to=SplitRetrieveResponse, + ) + + +class SplitsResourceWithRawResponse: + def __init__(self, splits: SplitsResource) -> None: + self._splits = splits + + self.create = to_raw_response_wrapper( + splits.create, + ) + self.retrieve = to_raw_response_wrapper( + splits.retrieve, + ) + + +class AsyncSplitsResourceWithRawResponse: + def __init__(self, splits: AsyncSplitsResource) -> None: + self._splits = splits + + self.create = async_to_raw_response_wrapper( + splits.create, + ) + self.retrieve = async_to_raw_response_wrapper( + splits.retrieve, + ) + + +class SplitsResourceWithStreamingResponse: + def __init__(self, splits: SplitsResource) -> None: + self._splits = splits + + self.create = to_streamed_response_wrapper( + splits.create, + ) + self.retrieve = to_streamed_response_wrapper( + splits.retrieve, + ) + + +class AsyncSplitsResourceWithStreamingResponse: + def __init__(self, splits: AsyncSplitsResource) -> None: + self._splits = splits + + self.create = async_to_streamed_response_wrapper( + splits.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + splits.retrieve, + ) diff --git a/python/langsmith_api/resources/datasets/versions.py b/python/langsmith_api/resources/datasets/versions.py new file mode 100644 index 000000000..73a6dfadd --- /dev/null +++ b/python/langsmith_api/resources/datasets/versions.py @@ -0,0 +1,306 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.datasets import version_list_params, version_retrieve_diff_params +from ...types.dataset_version import DatasetVersion +from ...types.datasets.version_retrieve_diff_response import VersionRetrieveDiffResponse + +__all__ = ["VersionsResource", "AsyncVersionsResource"] + + +class VersionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> VersionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return VersionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VersionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return VersionsResourceWithStreamingResponse(self) + + def list( + self, + dataset_id: str, + *, + example: Optional[str] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + search: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[DatasetVersion]: + """ + Get dataset versions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get_api_list( + path_template("/api/v1/datasets/{dataset_id}/versions", dataset_id=dataset_id), + page=SyncOffsetPaginationTopLevelArray[DatasetVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "example": example, + "limit": limit, + "offset": offset, + "search": search, + }, + version_list_params.VersionListParams, + ), + ), + model=DatasetVersion, + ) + + def retrieve_diff( + self, + dataset_id: str, + *, + from_version: Union[Union[str, datetime], str], + to_version: Union[Union[str, datetime], str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionRetrieveDiffResponse: + """ + Get diff between two dataset versions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get( + path_template("/api/v1/datasets/{dataset_id}/versions/diff", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "from_version": from_version, + "to_version": to_version, + }, + version_retrieve_diff_params.VersionRetrieveDiffParams, + ), + ), + cast_to=VersionRetrieveDiffResponse, + ) + + +class AsyncVersionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncVersionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncVersionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVersionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncVersionsResourceWithStreamingResponse(self) + + def list( + self, + dataset_id: str, + *, + example: Optional[str] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + search: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[DatasetVersion, AsyncOffsetPaginationTopLevelArray[DatasetVersion]]: + """ + Get dataset versions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return self._get_api_list( + path_template("/api/v1/datasets/{dataset_id}/versions", dataset_id=dataset_id), + page=AsyncOffsetPaginationTopLevelArray[DatasetVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "example": example, + "limit": limit, + "offset": offset, + "search": search, + }, + version_list_params.VersionListParams, + ), + ), + model=DatasetVersion, + ) + + async def retrieve_diff( + self, + dataset_id: str, + *, + from_version: Union[Union[str, datetime], str], + to_version: Union[Union[str, datetime], str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VersionRetrieveDiffResponse: + """ + Get diff between two dataset versions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + return await self._get( + path_template("/api/v1/datasets/{dataset_id}/versions/diff", dataset_id=dataset_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "from_version": from_version, + "to_version": to_version, + }, + version_retrieve_diff_params.VersionRetrieveDiffParams, + ), + ), + cast_to=VersionRetrieveDiffResponse, + ) + + +class VersionsResourceWithRawResponse: + def __init__(self, versions: VersionsResource) -> None: + self._versions = versions + + self.list = to_raw_response_wrapper( + versions.list, + ) + self.retrieve_diff = to_raw_response_wrapper( + versions.retrieve_diff, + ) + + +class AsyncVersionsResourceWithRawResponse: + def __init__(self, versions: AsyncVersionsResource) -> None: + self._versions = versions + + self.list = async_to_raw_response_wrapper( + versions.list, + ) + self.retrieve_diff = async_to_raw_response_wrapper( + versions.retrieve_diff, + ) + + +class VersionsResourceWithStreamingResponse: + def __init__(self, versions: VersionsResource) -> None: + self._versions = versions + + self.list = to_streamed_response_wrapper( + versions.list, + ) + self.retrieve_diff = to_streamed_response_wrapper( + versions.retrieve_diff, + ) + + +class AsyncVersionsResourceWithStreamingResponse: + def __init__(self, versions: AsyncVersionsResource) -> None: + self._versions = versions + + self.list = async_to_streamed_response_wrapper( + versions.list, + ) + self.retrieve_diff = async_to_streamed_response_wrapper( + versions.retrieve_diff, + ) diff --git a/python/langsmith_api/resources/evaluators.py b/python/langsmith_api/resources/evaluators.py new file mode 100644 index 000000000..79edc3fdd --- /dev/null +++ b/python/langsmith_api/resources/evaluators.py @@ -0,0 +1,210 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..types import evaluator_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.evaluator_list_response import EvaluatorListResponse + +__all__ = ["EvaluatorsResource", "AsyncEvaluatorsResource"] + + +class EvaluatorsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EvaluatorsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return EvaluatorsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EvaluatorsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return EvaluatorsResourceWithStreamingResponse(self) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + evaluator_id: Optional[str] | Omit = omit, + include_backfill_progress: bool | Omit = omit, + name_contains: Optional[str] | Omit = omit, + session_id: Optional[str] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + type: Optional[Literal["session", "dataset"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluatorListResponse: + """ + List all run rules. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/api/v1/runs/rules", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "dataset_id": dataset_id, + "evaluator_id": evaluator_id, + "include_backfill_progress": include_backfill_progress, + "name_contains": name_contains, + "session_id": session_id, + "tag_value_id": tag_value_id, + "type": type, + }, + evaluator_list_params.EvaluatorListParams, + ), + ), + cast_to=EvaluatorListResponse, + ) + + +class AsyncEvaluatorsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEvaluatorsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncEvaluatorsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEvaluatorsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncEvaluatorsResourceWithStreamingResponse(self) + + async def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + evaluator_id: Optional[str] | Omit = omit, + include_backfill_progress: bool | Omit = omit, + name_contains: Optional[str] | Omit = omit, + session_id: Optional[str] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + type: Optional[Literal["session", "dataset"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EvaluatorListResponse: + """ + List all run rules. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/api/v1/runs/rules", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "id": id, + "dataset_id": dataset_id, + "evaluator_id": evaluator_id, + "include_backfill_progress": include_backfill_progress, + "name_contains": name_contains, + "session_id": session_id, + "tag_value_id": tag_value_id, + "type": type, + }, + evaluator_list_params.EvaluatorListParams, + ), + ), + cast_to=EvaluatorListResponse, + ) + + +class EvaluatorsResourceWithRawResponse: + def __init__(self, evaluators: EvaluatorsResource) -> None: + self._evaluators = evaluators + + self.list = to_raw_response_wrapper( + evaluators.list, + ) + + +class AsyncEvaluatorsResourceWithRawResponse: + def __init__(self, evaluators: AsyncEvaluatorsResource) -> None: + self._evaluators = evaluators + + self.list = async_to_raw_response_wrapper( + evaluators.list, + ) + + +class EvaluatorsResourceWithStreamingResponse: + def __init__(self, evaluators: EvaluatorsResource) -> None: + self._evaluators = evaluators + + self.list = to_streamed_response_wrapper( + evaluators.list, + ) + + +class AsyncEvaluatorsResourceWithStreamingResponse: + def __init__(self, evaluators: AsyncEvaluatorsResource) -> None: + self._evaluators = evaluators + + self.list = async_to_streamed_response_wrapper( + evaluators.list, + ) diff --git a/python/langsmith_api/resources/examples/__init__.py b/python/langsmith_api/resources/examples/__init__.py new file mode 100644 index 000000000..14799b695 --- /dev/null +++ b/python/langsmith_api/resources/examples/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .bulk import ( + BulkResource, + AsyncBulkResource, + BulkResourceWithRawResponse, + AsyncBulkResourceWithRawResponse, + BulkResourceWithStreamingResponse, + AsyncBulkResourceWithStreamingResponse, +) +from .examples import ( + ExamplesResource, + AsyncExamplesResource, + ExamplesResourceWithRawResponse, + AsyncExamplesResourceWithRawResponse, + ExamplesResourceWithStreamingResponse, + AsyncExamplesResourceWithStreamingResponse, +) +from .validate import ( + ValidateResource, + AsyncValidateResource, + ValidateResourceWithRawResponse, + AsyncValidateResourceWithRawResponse, + ValidateResourceWithStreamingResponse, + AsyncValidateResourceWithStreamingResponse, +) + +__all__ = [ + "BulkResource", + "AsyncBulkResource", + "BulkResourceWithRawResponse", + "AsyncBulkResourceWithRawResponse", + "BulkResourceWithStreamingResponse", + "AsyncBulkResourceWithStreamingResponse", + "ValidateResource", + "AsyncValidateResource", + "ValidateResourceWithRawResponse", + "AsyncValidateResourceWithRawResponse", + "ValidateResourceWithStreamingResponse", + "AsyncValidateResourceWithStreamingResponse", + "ExamplesResource", + "AsyncExamplesResource", + "ExamplesResourceWithRawResponse", + "AsyncExamplesResourceWithRawResponse", + "ExamplesResourceWithStreamingResponse", + "AsyncExamplesResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/examples/bulk.py b/python/langsmith_api/resources/examples/bulk.py new file mode 100644 index 000000000..4a2dcde74 --- /dev/null +++ b/python/langsmith_api/resources/examples/bulk.py @@ -0,0 +1,249 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.examples import bulk_create_params, bulk_patch_all_params +from ...types.examples.bulk_create_response import BulkCreateResponse + +__all__ = ["BulkResource", "AsyncBulkResource"] + + +class BulkResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BulkResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return BulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BulkResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return BulkResourceWithStreamingResponse(self) + + def create( + self, + *, + body: Iterable[bulk_create_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BulkCreateResponse: + """ + Create bulk examples. + + Args: + body: Schema for a batch of examples to be created. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/examples/bulk", + body=maybe_transform(body, Iterable[bulk_create_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BulkCreateResponse, + ) + + def patch_all( + self, + *, + body: Iterable[bulk_patch_all_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Legacy update examples in bulk. + + For update involving attachments, use PATCH + /v1/platform/datasets/{dataset_id}/examples instead. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._patch( + "/api/v1/examples/bulk", + body=maybe_transform(body, Iterable[bulk_patch_all_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncBulkResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBulkResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncBulkResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBulkResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncBulkResourceWithStreamingResponse(self) + + async def create( + self, + *, + body: Iterable[bulk_create_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BulkCreateResponse: + """ + Create bulk examples. + + Args: + body: Schema for a batch of examples to be created. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/examples/bulk", + body=await async_maybe_transform(body, Iterable[bulk_create_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BulkCreateResponse, + ) + + async def patch_all( + self, + *, + body: Iterable[bulk_patch_all_params.Body], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Legacy update examples in bulk. + + For update involving attachments, use PATCH + /v1/platform/datasets/{dataset_id}/examples instead. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._patch( + "/api/v1/examples/bulk", + body=await async_maybe_transform(body, Iterable[bulk_patch_all_params.Body]), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class BulkResourceWithRawResponse: + def __init__(self, bulk: BulkResource) -> None: + self._bulk = bulk + + self.create = to_raw_response_wrapper( + bulk.create, + ) + self.patch_all = to_raw_response_wrapper( + bulk.patch_all, + ) + + +class AsyncBulkResourceWithRawResponse: + def __init__(self, bulk: AsyncBulkResource) -> None: + self._bulk = bulk + + self.create = async_to_raw_response_wrapper( + bulk.create, + ) + self.patch_all = async_to_raw_response_wrapper( + bulk.patch_all, + ) + + +class BulkResourceWithStreamingResponse: + def __init__(self, bulk: BulkResource) -> None: + self._bulk = bulk + + self.create = to_streamed_response_wrapper( + bulk.create, + ) + self.patch_all = to_streamed_response_wrapper( + bulk.patch_all, + ) + + +class AsyncBulkResourceWithStreamingResponse: + def __init__(self, bulk: AsyncBulkResource) -> None: + self._bulk = bulk + + self.create = async_to_streamed_response_wrapper( + bulk.create, + ) + self.patch_all = async_to_streamed_response_wrapper( + bulk.patch_all, + ) diff --git a/python/langsmith_api/resources/examples/examples.py b/python/langsmith_api/resources/examples/examples.py new file mode 100644 index 000000000..cfe5ef26a --- /dev/null +++ b/python/langsmith_api/resources/examples/examples.py @@ -0,0 +1,1099 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Mapping, Optional, cast +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from .bulk import ( + BulkResource, + AsyncBulkResource, + BulkResourceWithRawResponse, + AsyncBulkResourceWithRawResponse, + BulkResourceWithStreamingResponse, + AsyncBulkResourceWithStreamingResponse, +) +from ...types import ( + example_list_params, + example_create_params, + example_update_params, + example_retrieve_params, + example_delete_all_params, + example_retrieve_count_params, + example_upload_from_csv_params, +) +from ..._files import deepcopy_with_paths +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform +from .validate import ( + ValidateResource, + AsyncValidateResource, + ValidateResourceWithRawResponse, + AsyncValidateResourceWithRawResponse, + ValidateResourceWithStreamingResponse, + AsyncValidateResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.example import Example +from ...types.example_select import ExampleSelect +from ...types.attachments_operations_param import AttachmentsOperationsParam +from ...types.example_retrieve_count_response import ExampleRetrieveCountResponse +from ...types.example_upload_from_csv_response import ExampleUploadFromCsvResponse + +__all__ = ["ExamplesResource", "AsyncExamplesResource"] + + +class ExamplesResource(SyncAPIResource): + @cached_property + def bulk(self) -> BulkResource: + return BulkResource(self._client) + + @cached_property + def validate(self) -> ValidateResource: + return ValidateResource(self._client) + + @cached_property + def with_raw_response(self) -> ExamplesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ExamplesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExamplesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ExamplesResourceWithStreamingResponse(self) + + def create( + self, + *, + dataset_id: str, + id: Optional[str] | Omit = omit, + created_at: str | Omit = omit, + inputs: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + outputs: Optional[Dict[str, object]] | Omit = omit, + source_run_id: Optional[str] | Omit = omit, + split: Union[SequenceNotStr[str], str, None] | Omit = omit, + use_legacy_message_format: bool | Omit = omit, + use_source_run_attachments: SequenceNotStr[str] | Omit = omit, + use_source_run_io: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Example: + """ + Create a new example. + + Args: + use_legacy_message_format: Use Legacy Message Format for LLM runs + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/examples", + body=maybe_transform( + { + "dataset_id": dataset_id, + "id": id, + "created_at": created_at, + "inputs": inputs, + "metadata": metadata, + "outputs": outputs, + "source_run_id": source_run_id, + "split": split, + "use_legacy_message_format": use_legacy_message_format, + "use_source_run_attachments": use_source_run_attachments, + "use_source_run_io": use_source_run_io, + }, + example_create_params.ExampleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Example, + ) + + def retrieve( + self, + example_id: str, + *, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Example: + """ + Get a specific example. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return self._get( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "as_of": as_of, + "dataset": dataset, + }, + example_retrieve_params.ExampleRetrieveParams, + ), + ), + cast_to=Example, + ) + + def update( + self, + example_id: str, + *, + attachments_operations: Optional[AttachmentsOperationsParam] | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + inputs: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + outputs: Optional[Dict[str, object]] | Omit = omit, + overwrite: bool | Omit = omit, + split: Union[SequenceNotStr[str], str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update a specific example. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return self._patch( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + body=maybe_transform( + { + "attachments_operations": attachments_operations, + "dataset_id": dataset_id, + "inputs": inputs, + "metadata": metadata, + "outputs": outputs, + "overwrite": overwrite, + "split": split, + }, + example_update_params.ExampleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + descending: Optional[bool] | Omit = omit, + filter: Optional[str] | Omit = omit, + full_text_contains: Optional[SequenceNotStr[str]] | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + offset: int | Omit = omit, + order: Literal["recent", "random", "recently_created", "id"] | Omit = omit, + random_seed: Optional[float] | Omit = omit, + select: List[ExampleSelect] | Omit = omit, + splits: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[Example]: + """ + Get all examples by query params + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/examples", + page=SyncOffsetPaginationTopLevelArray[Example], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "as_of": as_of, + "dataset": dataset, + "descending": descending, + "filter": filter, + "full_text_contains": full_text_contains, + "limit": limit, + "metadata": metadata, + "offset": offset, + "order": order, + "random_seed": random_seed, + "select": select, + "splits": splits, + }, + example_list_params.ExampleListParams, + ), + ), + model=Example, + ) + + def delete( + self, + example_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Soft delete an example. + + Only deletes the example in the 'latest' version of the + dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return self._delete( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete_all( + self, + *, + example_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Soft delete examples. + + Only deletes the examples in the 'latest' version of the + dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._delete( + "/api/v1/examples", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"example_ids": example_ids}, example_delete_all_params.ExampleDeleteAllParams), + ), + cast_to=object, + ) + + def retrieve_count( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + filter: Optional[str] | Omit = omit, + full_text_contains: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[str] | Omit = omit, + splits: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleRetrieveCountResponse: + """ + Count all examples by query params + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/api/v1/examples/count", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "as_of": as_of, + "dataset": dataset, + "filter": filter, + "full_text_contains": full_text_contains, + "metadata": metadata, + "splits": splits, + }, + example_retrieve_count_params.ExampleRetrieveCountParams, + ), + ), + cast_to=int, + ) + + def upload_from_csv( + self, + dataset_id: str, + *, + file: FileTypes, + input_keys: SequenceNotStr[str], + metadata_keys: SequenceNotStr[str] | Omit = omit, + output_keys: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleUploadFromCsvResponse: + """ + Upload examples from a CSV file. + + Note: For non-csv upload, please use the POST + /v1/platform/datasets/{dataset_id}/examples endpoint which provides more + efficient upload. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + body = deepcopy_with_paths( + { + "file": file, + "input_keys": input_keys, + "metadata_keys": metadata_keys, + "output_keys": output_keys, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + path_template("/api/v1/examples/upload/{dataset_id}", dataset_id=dataset_id), + body=maybe_transform(body, example_upload_from_csv_params.ExampleUploadFromCsvParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExampleUploadFromCsvResponse, + ) + + +class AsyncExamplesResource(AsyncAPIResource): + @cached_property + def bulk(self) -> AsyncBulkResource: + return AsyncBulkResource(self._client) + + @cached_property + def validate(self) -> AsyncValidateResource: + return AsyncValidateResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncExamplesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncExamplesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExamplesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncExamplesResourceWithStreamingResponse(self) + + async def create( + self, + *, + dataset_id: str, + id: Optional[str] | Omit = omit, + created_at: str | Omit = omit, + inputs: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + outputs: Optional[Dict[str, object]] | Omit = omit, + source_run_id: Optional[str] | Omit = omit, + split: Union[SequenceNotStr[str], str, None] | Omit = omit, + use_legacy_message_format: bool | Omit = omit, + use_source_run_attachments: SequenceNotStr[str] | Omit = omit, + use_source_run_io: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Example: + """ + Create a new example. + + Args: + use_legacy_message_format: Use Legacy Message Format for LLM runs + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/examples", + body=await async_maybe_transform( + { + "dataset_id": dataset_id, + "id": id, + "created_at": created_at, + "inputs": inputs, + "metadata": metadata, + "outputs": outputs, + "source_run_id": source_run_id, + "split": split, + "use_legacy_message_format": use_legacy_message_format, + "use_source_run_attachments": use_source_run_attachments, + "use_source_run_io": use_source_run_io, + }, + example_create_params.ExampleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Example, + ) + + async def retrieve( + self, + example_id: str, + *, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Example: + """ + Get a specific example. + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return await self._get( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "as_of": as_of, + "dataset": dataset, + }, + example_retrieve_params.ExampleRetrieveParams, + ), + ), + cast_to=Example, + ) + + async def update( + self, + example_id: str, + *, + attachments_operations: Optional[AttachmentsOperationsParam] | Omit = omit, + dataset_id: Optional[str] | Omit = omit, + inputs: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + outputs: Optional[Dict[str, object]] | Omit = omit, + overwrite: bool | Omit = omit, + split: Union[SequenceNotStr[str], str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update a specific example. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return await self._patch( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + body=await async_maybe_transform( + { + "attachments_operations": attachments_operations, + "dataset_id": dataset_id, + "inputs": inputs, + "metadata": metadata, + "outputs": outputs, + "overwrite": overwrite, + "split": split, + }, + example_update_params.ExampleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + descending: Optional[bool] | Omit = omit, + filter: Optional[str] | Omit = omit, + full_text_contains: Optional[SequenceNotStr[str]] | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + offset: int | Omit = omit, + order: Literal["recent", "random", "recently_created", "id"] | Omit = omit, + random_seed: Optional[float] | Omit = omit, + select: List[ExampleSelect] | Omit = omit, + splits: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Example, AsyncOffsetPaginationTopLevelArray[Example]]: + """ + Get all examples by query params + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/examples", + page=AsyncOffsetPaginationTopLevelArray[Example], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "as_of": as_of, + "dataset": dataset, + "descending": descending, + "filter": filter, + "full_text_contains": full_text_contains, + "limit": limit, + "metadata": metadata, + "offset": offset, + "order": order, + "random_seed": random_seed, + "select": select, + "splits": splits, + }, + example_list_params.ExampleListParams, + ), + ), + model=Example, + ) + + async def delete( + self, + example_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Soft delete an example. + + Only deletes the example in the 'latest' version of the + dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not example_id: + raise ValueError(f"Expected a non-empty value for `example_id` but received {example_id!r}") + return await self._delete( + path_template("/api/v1/examples/{example_id}", example_id=example_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete_all( + self, + *, + example_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """Soft delete examples. + + Only deletes the examples in the 'latest' version of the + dataset. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._delete( + "/api/v1/examples", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"example_ids": example_ids}, example_delete_all_params.ExampleDeleteAllParams + ), + ), + cast_to=object, + ) + + async def retrieve_count( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + as_of: Union[Union[str, datetime], str] | Omit = omit, + dataset: Optional[str] | Omit = omit, + filter: Optional[str] | Omit = omit, + full_text_contains: Optional[SequenceNotStr[str]] | Omit = omit, + metadata: Optional[str] | Omit = omit, + splits: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleRetrieveCountResponse: + """ + Count all examples by query params + + Args: + as_of: Only modifications made on or before this time are included. If None, the latest + version of the dataset is used. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/api/v1/examples/count", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "id": id, + "as_of": as_of, + "dataset": dataset, + "filter": filter, + "full_text_contains": full_text_contains, + "metadata": metadata, + "splits": splits, + }, + example_retrieve_count_params.ExampleRetrieveCountParams, + ), + ), + cast_to=int, + ) + + async def upload_from_csv( + self, + dataset_id: str, + *, + file: FileTypes, + input_keys: SequenceNotStr[str], + metadata_keys: SequenceNotStr[str] | Omit = omit, + output_keys: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleUploadFromCsvResponse: + """ + Upload examples from a CSV file. + + Note: For non-csv upload, please use the POST + /v1/platform/datasets/{dataset_id}/examples endpoint which provides more + efficient upload. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not dataset_id: + raise ValueError(f"Expected a non-empty value for `dataset_id` but received {dataset_id!r}") + body = deepcopy_with_paths( + { + "file": file, + "input_keys": input_keys, + "metadata_keys": metadata_keys, + "output_keys": output_keys, + }, + [["file"]], + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + path_template("/api/v1/examples/upload/{dataset_id}", dataset_id=dataset_id), + body=await async_maybe_transform(body, example_upload_from_csv_params.ExampleUploadFromCsvParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExampleUploadFromCsvResponse, + ) + + +class ExamplesResourceWithRawResponse: + def __init__(self, examples: ExamplesResource) -> None: + self._examples = examples + + self.create = to_raw_response_wrapper( + examples.create, + ) + self.retrieve = to_raw_response_wrapper( + examples.retrieve, + ) + self.update = to_raw_response_wrapper( + examples.update, + ) + self.list = to_raw_response_wrapper( + examples.list, + ) + self.delete = to_raw_response_wrapper( + examples.delete, + ) + self.delete_all = to_raw_response_wrapper( + examples.delete_all, + ) + self.retrieve_count = to_raw_response_wrapper( + examples.retrieve_count, + ) + self.upload_from_csv = to_raw_response_wrapper( + examples.upload_from_csv, + ) + + @cached_property + def bulk(self) -> BulkResourceWithRawResponse: + return BulkResourceWithRawResponse(self._examples.bulk) + + @cached_property + def validate(self) -> ValidateResourceWithRawResponse: + return ValidateResourceWithRawResponse(self._examples.validate) + + +class AsyncExamplesResourceWithRawResponse: + def __init__(self, examples: AsyncExamplesResource) -> None: + self._examples = examples + + self.create = async_to_raw_response_wrapper( + examples.create, + ) + self.retrieve = async_to_raw_response_wrapper( + examples.retrieve, + ) + self.update = async_to_raw_response_wrapper( + examples.update, + ) + self.list = async_to_raw_response_wrapper( + examples.list, + ) + self.delete = async_to_raw_response_wrapper( + examples.delete, + ) + self.delete_all = async_to_raw_response_wrapper( + examples.delete_all, + ) + self.retrieve_count = async_to_raw_response_wrapper( + examples.retrieve_count, + ) + self.upload_from_csv = async_to_raw_response_wrapper( + examples.upload_from_csv, + ) + + @cached_property + def bulk(self) -> AsyncBulkResourceWithRawResponse: + return AsyncBulkResourceWithRawResponse(self._examples.bulk) + + @cached_property + def validate(self) -> AsyncValidateResourceWithRawResponse: + return AsyncValidateResourceWithRawResponse(self._examples.validate) + + +class ExamplesResourceWithStreamingResponse: + def __init__(self, examples: ExamplesResource) -> None: + self._examples = examples + + self.create = to_streamed_response_wrapper( + examples.create, + ) + self.retrieve = to_streamed_response_wrapper( + examples.retrieve, + ) + self.update = to_streamed_response_wrapper( + examples.update, + ) + self.list = to_streamed_response_wrapper( + examples.list, + ) + self.delete = to_streamed_response_wrapper( + examples.delete, + ) + self.delete_all = to_streamed_response_wrapper( + examples.delete_all, + ) + self.retrieve_count = to_streamed_response_wrapper( + examples.retrieve_count, + ) + self.upload_from_csv = to_streamed_response_wrapper( + examples.upload_from_csv, + ) + + @cached_property + def bulk(self) -> BulkResourceWithStreamingResponse: + return BulkResourceWithStreamingResponse(self._examples.bulk) + + @cached_property + def validate(self) -> ValidateResourceWithStreamingResponse: + return ValidateResourceWithStreamingResponse(self._examples.validate) + + +class AsyncExamplesResourceWithStreamingResponse: + def __init__(self, examples: AsyncExamplesResource) -> None: + self._examples = examples + + self.create = async_to_streamed_response_wrapper( + examples.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + examples.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + examples.update, + ) + self.list = async_to_streamed_response_wrapper( + examples.list, + ) + self.delete = async_to_streamed_response_wrapper( + examples.delete, + ) + self.delete_all = async_to_streamed_response_wrapper( + examples.delete_all, + ) + self.retrieve_count = async_to_streamed_response_wrapper( + examples.retrieve_count, + ) + self.upload_from_csv = async_to_streamed_response_wrapper( + examples.upload_from_csv, + ) + + @cached_property + def bulk(self) -> AsyncBulkResourceWithStreamingResponse: + return AsyncBulkResourceWithStreamingResponse(self._examples.bulk) + + @cached_property + def validate(self) -> AsyncValidateResourceWithStreamingResponse: + return AsyncValidateResourceWithStreamingResponse(self._examples.validate) diff --git a/python/langsmith_api/resources/examples/validate.py b/python/langsmith_api/resources/examples/validate.py new file mode 100644 index 000000000..1e78c3d29 --- /dev/null +++ b/python/langsmith_api/resources/examples/validate.py @@ -0,0 +1,186 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.examples.validate_bulk_response import ValidateBulkResponse +from ...types.examples.example_validation_result import ExampleValidationResult + +__all__ = ["ValidateResource", "AsyncValidateResource"] + + +class ValidateResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ValidateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ValidateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ValidateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ValidateResourceWithStreamingResponse(self) + + def create( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleValidationResult: + """Validate an example.""" + return self._post( + "/api/v1/examples/validate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExampleValidationResult, + ) + + def bulk( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ValidateBulkResponse: + """Validate examples in bulk.""" + return self._post( + "/api/v1/examples/validate/bulk", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ValidateBulkResponse, + ) + + +class AsyncValidateResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncValidateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncValidateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncValidateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncValidateResourceWithStreamingResponse(self) + + async def create( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExampleValidationResult: + """Validate an example.""" + return await self._post( + "/api/v1/examples/validate", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ExampleValidationResult, + ) + + async def bulk( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ValidateBulkResponse: + """Validate examples in bulk.""" + return await self._post( + "/api/v1/examples/validate/bulk", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ValidateBulkResponse, + ) + + +class ValidateResourceWithRawResponse: + def __init__(self, validate: ValidateResource) -> None: + self._validate = validate + + self.create = to_raw_response_wrapper( + validate.create, + ) + self.bulk = to_raw_response_wrapper( + validate.bulk, + ) + + +class AsyncValidateResourceWithRawResponse: + def __init__(self, validate: AsyncValidateResource) -> None: + self._validate = validate + + self.create = async_to_raw_response_wrapper( + validate.create, + ) + self.bulk = async_to_raw_response_wrapper( + validate.bulk, + ) + + +class ValidateResourceWithStreamingResponse: + def __init__(self, validate: ValidateResource) -> None: + self._validate = validate + + self.create = to_streamed_response_wrapper( + validate.create, + ) + self.bulk = to_streamed_response_wrapper( + validate.bulk, + ) + + +class AsyncValidateResourceWithStreamingResponse: + def __init__(self, validate: AsyncValidateResource) -> None: + self._validate = validate + + self.create = async_to_streamed_response_wrapper( + validate.create, + ) + self.bulk = async_to_streamed_response_wrapper( + validate.bulk, + ) diff --git a/python/langsmith_api/resources/feedback/__init__.py b/python/langsmith_api/resources/feedback/__init__.py new file mode 100644 index 000000000..7e1d937fb --- /dev/null +++ b/python/langsmith_api/resources/feedback/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .tokens import ( + TokensResource, + AsyncTokensResource, + TokensResourceWithRawResponse, + AsyncTokensResourceWithRawResponse, + TokensResourceWithStreamingResponse, + AsyncTokensResourceWithStreamingResponse, +) +from .configs import ( + ConfigsResource, + AsyncConfigsResource, + ConfigsResourceWithRawResponse, + AsyncConfigsResourceWithRawResponse, + ConfigsResourceWithStreamingResponse, + AsyncConfigsResourceWithStreamingResponse, +) +from .feedback import ( + FeedbackResource, + AsyncFeedbackResource, + FeedbackResourceWithRawResponse, + AsyncFeedbackResourceWithRawResponse, + FeedbackResourceWithStreamingResponse, + AsyncFeedbackResourceWithStreamingResponse, +) + +__all__ = [ + "TokensResource", + "AsyncTokensResource", + "TokensResourceWithRawResponse", + "AsyncTokensResourceWithRawResponse", + "TokensResourceWithStreamingResponse", + "AsyncTokensResourceWithStreamingResponse", + "ConfigsResource", + "AsyncConfigsResource", + "ConfigsResourceWithRawResponse", + "AsyncConfigsResourceWithRawResponse", + "ConfigsResourceWithStreamingResponse", + "AsyncConfigsResourceWithStreamingResponse", + "FeedbackResource", + "AsyncFeedbackResource", + "FeedbackResourceWithRawResponse", + "AsyncFeedbackResourceWithRawResponse", + "FeedbackResourceWithStreamingResponse", + "AsyncFeedbackResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/feedback/configs.py b/python/langsmith_api/resources/feedback/configs.py new file mode 100644 index 000000000..a2e634a6d --- /dev/null +++ b/python/langsmith_api/resources/feedback/configs.py @@ -0,0 +1,178 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NoneType, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.feedback import config_delete_params + +__all__ = ["ConfigsResource", "AsyncConfigsResource"] + + +class ConfigsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ConfigsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ConfigsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConfigsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ConfigsResourceWithStreamingResponse(self) + + def delete( + self, + *, + feedback_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Soft delete a feedback config by marking it as deleted. + + The config can be recreated later with the same key (simple reuse pattern). + Existing feedback records with this key will remain unchanged. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + "/api/v1/feedback-configs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"feedback_key": feedback_key}, config_delete_params.ConfigDeleteParams), + ), + cast_to=NoneType, + ) + + +class AsyncConfigsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncConfigsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncConfigsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConfigsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncConfigsResourceWithStreamingResponse(self) + + async def delete( + self, + *, + feedback_key: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Soft delete a feedback config by marking it as deleted. + + The config can be recreated later with the same key (simple reuse pattern). + Existing feedback records with this key will remain unchanged. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + "/api/v1/feedback-configs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"feedback_key": feedback_key}, config_delete_params.ConfigDeleteParams + ), + ), + cast_to=NoneType, + ) + + +class ConfigsResourceWithRawResponse: + def __init__(self, configs: ConfigsResource) -> None: + self._configs = configs + + self.delete = to_raw_response_wrapper( + configs.delete, + ) + + +class AsyncConfigsResourceWithRawResponse: + def __init__(self, configs: AsyncConfigsResource) -> None: + self._configs = configs + + self.delete = async_to_raw_response_wrapper( + configs.delete, + ) + + +class ConfigsResourceWithStreamingResponse: + def __init__(self, configs: ConfigsResource) -> None: + self._configs = configs + + self.delete = to_streamed_response_wrapper( + configs.delete, + ) + + +class AsyncConfigsResourceWithStreamingResponse: + def __init__(self, configs: AsyncConfigsResource) -> None: + self._configs = configs + + self.delete = async_to_streamed_response_wrapper( + configs.delete, + ) diff --git a/python/langsmith_api/resources/feedback/feedback.py b/python/langsmith_api/resources/feedback/feedback.py new file mode 100644 index 000000000..9760a8cc9 --- /dev/null +++ b/python/langsmith_api/resources/feedback/feedback.py @@ -0,0 +1,745 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from datetime import datetime + +import httpx + +from .tokens import ( + TokensResource, + AsyncTokensResource, + TokensResourceWithRawResponse, + AsyncTokensResourceWithRawResponse, + TokensResourceWithStreamingResponse, + AsyncTokensResourceWithStreamingResponse, +) +from ...types import ( + FeedbackLevel, + feedback_list_params, + feedback_create_params, + feedback_update_params, + feedback_retrieve_params, +) +from .configs import ( + ConfigsResource, + AsyncConfigsResource, + ConfigsResourceWithRawResponse, + AsyncConfigsResourceWithRawResponse, + ConfigsResourceWithStreamingResponse, + AsyncConfigsResourceWithStreamingResponse, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.source_type import SourceType +from ...types.feedback_level import FeedbackLevel +from ...types.feedback_schema import FeedbackSchema + +__all__ = ["FeedbackResource", "AsyncFeedbackResource"] + + +class FeedbackResource(SyncAPIResource): + @cached_property + def tokens(self) -> TokensResource: + return TokensResource(self._client) + + @cached_property + def configs(self) -> ConfigsResource: + return ConfigsResource(self._client) + + @cached_property + def with_raw_response(self) -> FeedbackResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return FeedbackResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FeedbackResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return FeedbackResourceWithStreamingResponse(self) + + def create( + self, + *, + key: str, + id: str | Omit = omit, + comment: Optional[str] | Omit = omit, + comparative_experiment_id: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + error: Optional[bool] | Omit = omit, + feedback_config: Optional[feedback_create_params.FeedbackConfig] | Omit = omit, + feedback_group_id: Optional[str] | Omit = omit, + feedback_source: Optional[feedback_create_params.FeedbackSource] | Omit = omit, + modified_at: Union[str, datetime] | Omit = omit, + run_id: Optional[str] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + session_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace_id: Optional[str] | Omit = omit, + value: Union[float, bool, str, Dict[str, object], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Create a new feedback. + + Args: + feedback_source: Feedback from the LangChainPlus App. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/feedback", + body=maybe_transform( + { + "key": key, + "id": id, + "comment": comment, + "comparative_experiment_id": comparative_experiment_id, + "correction": correction, + "created_at": created_at, + "error": error, + "feedback_config": feedback_config, + "feedback_group_id": feedback_group_id, + "feedback_source": feedback_source, + "modified_at": modified_at, + "run_id": run_id, + "score": score, + "session_id": session_id, + "start_time": start_time, + "trace_id": trace_id, + "value": value, + }, + feedback_create_params.FeedbackCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FeedbackSchema, + ) + + def retrieve( + self, + feedback_id: str, + *, + include_user_names: Optional[bool] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Get a specific feedback. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return self._get( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"include_user_names": include_user_names}, feedback_retrieve_params.FeedbackRetrieveParams + ), + ), + cast_to=FeedbackSchema, + ) + + def update( + self, + feedback_id: str, + *, + comment: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + feedback_config: Optional[feedback_update_params.FeedbackConfig] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, Dict[str, object], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Replace an existing feedback entry with a new, modified entry. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return self._patch( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + body=maybe_transform( + { + "comment": comment, + "correction": correction, + "feedback_config": feedback_config, + "score": score, + "value": value, + }, + feedback_update_params.FeedbackUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FeedbackSchema, + ) + + def list( + self, + *, + comparative_experiment_id: Optional[str] | Omit = omit, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + include_user_names: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + max_created_at: Union[str, datetime, None] | Omit = omit, + min_created_at: Union[str, datetime, None] | Omit = omit, + offset: int | Omit = omit, + run: Union[SequenceNotStr[str], str, None] | Omit = omit, + session: Union[SequenceNotStr[str], str, None] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[FeedbackSchema]: + """ + List all Feedback by query params. + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/feedback", + page=SyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "comparative_experiment_id": comparative_experiment_id, + "has_comment": has_comment, + "has_score": has_score, + "include_user_names": include_user_names, + "key": key, + "level": level, + "limit": limit, + "max_created_at": max_created_at, + "min_created_at": min_created_at, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + feedback_list_params.FeedbackListParams, + ), + ), + model=FeedbackSchema, + ) + + def delete( + self, + feedback_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a feedback. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return self._delete( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncFeedbackResource(AsyncAPIResource): + @cached_property + def tokens(self) -> AsyncTokensResource: + return AsyncTokensResource(self._client) + + @cached_property + def configs(self) -> AsyncConfigsResource: + return AsyncConfigsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncFeedbackResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncFeedbackResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFeedbackResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncFeedbackResourceWithStreamingResponse(self) + + async def create( + self, + *, + key: str, + id: str | Omit = omit, + comment: Optional[str] | Omit = omit, + comparative_experiment_id: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + created_at: Union[str, datetime] | Omit = omit, + error: Optional[bool] | Omit = omit, + feedback_config: Optional[feedback_create_params.FeedbackConfig] | Omit = omit, + feedback_group_id: Optional[str] | Omit = omit, + feedback_source: Optional[feedback_create_params.FeedbackSource] | Omit = omit, + modified_at: Union[str, datetime] | Omit = omit, + run_id: Optional[str] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + session_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace_id: Optional[str] | Omit = omit, + value: Union[float, bool, str, Dict[str, object], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Create a new feedback. + + Args: + feedback_source: Feedback from the LangChainPlus App. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/feedback", + body=await async_maybe_transform( + { + "key": key, + "id": id, + "comment": comment, + "comparative_experiment_id": comparative_experiment_id, + "correction": correction, + "created_at": created_at, + "error": error, + "feedback_config": feedback_config, + "feedback_group_id": feedback_group_id, + "feedback_source": feedback_source, + "modified_at": modified_at, + "run_id": run_id, + "score": score, + "session_id": session_id, + "start_time": start_time, + "trace_id": trace_id, + "value": value, + }, + feedback_create_params.FeedbackCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FeedbackSchema, + ) + + async def retrieve( + self, + feedback_id: str, + *, + include_user_names: Optional[bool] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Get a specific feedback. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return await self._get( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include_user_names": include_user_names}, feedback_retrieve_params.FeedbackRetrieveParams + ), + ), + cast_to=FeedbackSchema, + ) + + async def update( + self, + feedback_id: str, + *, + comment: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + feedback_config: Optional[feedback_update_params.FeedbackConfig] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, Dict[str, object], None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FeedbackSchema: + """ + Replace an existing feedback entry with a new, modified entry. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return await self._patch( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + body=await async_maybe_transform( + { + "comment": comment, + "correction": correction, + "feedback_config": feedback_config, + "score": score, + "value": value, + }, + feedback_update_params.FeedbackUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FeedbackSchema, + ) + + def list( + self, + *, + comparative_experiment_id: Optional[str] | Omit = omit, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + include_user_names: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + max_created_at: Union[str, datetime, None] | Omit = omit, + min_created_at: Union[str, datetime, None] | Omit = omit, + offset: int | Omit = omit, + run: Union[SequenceNotStr[str], str, None] | Omit = omit, + session: Union[SequenceNotStr[str], str, None] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FeedbackSchema, AsyncOffsetPaginationTopLevelArray[FeedbackSchema]]: + """ + List all Feedback by query params. + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/feedback", + page=AsyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "comparative_experiment_id": comparative_experiment_id, + "has_comment": has_comment, + "has_score": has_score, + "include_user_names": include_user_names, + "key": key, + "level": level, + "limit": limit, + "max_created_at": max_created_at, + "min_created_at": min_created_at, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + feedback_list_params.FeedbackListParams, + ), + ), + model=FeedbackSchema, + ) + + async def delete( + self, + feedback_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a feedback. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not feedback_id: + raise ValueError(f"Expected a non-empty value for `feedback_id` but received {feedback_id!r}") + return await self._delete( + path_template("/api/v1/feedback/{feedback_id}", feedback_id=feedback_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class FeedbackResourceWithRawResponse: + def __init__(self, feedback: FeedbackResource) -> None: + self._feedback = feedback + + self.create = to_raw_response_wrapper( + feedback.create, + ) + self.retrieve = to_raw_response_wrapper( + feedback.retrieve, + ) + self.update = to_raw_response_wrapper( + feedback.update, + ) + self.list = to_raw_response_wrapper( + feedback.list, + ) + self.delete = to_raw_response_wrapper( + feedback.delete, + ) + + @cached_property + def tokens(self) -> TokensResourceWithRawResponse: + return TokensResourceWithRawResponse(self._feedback.tokens) + + @cached_property + def configs(self) -> ConfigsResourceWithRawResponse: + return ConfigsResourceWithRawResponse(self._feedback.configs) + + +class AsyncFeedbackResourceWithRawResponse: + def __init__(self, feedback: AsyncFeedbackResource) -> None: + self._feedback = feedback + + self.create = async_to_raw_response_wrapper( + feedback.create, + ) + self.retrieve = async_to_raw_response_wrapper( + feedback.retrieve, + ) + self.update = async_to_raw_response_wrapper( + feedback.update, + ) + self.list = async_to_raw_response_wrapper( + feedback.list, + ) + self.delete = async_to_raw_response_wrapper( + feedback.delete, + ) + + @cached_property + def tokens(self) -> AsyncTokensResourceWithRawResponse: + return AsyncTokensResourceWithRawResponse(self._feedback.tokens) + + @cached_property + def configs(self) -> AsyncConfigsResourceWithRawResponse: + return AsyncConfigsResourceWithRawResponse(self._feedback.configs) + + +class FeedbackResourceWithStreamingResponse: + def __init__(self, feedback: FeedbackResource) -> None: + self._feedback = feedback + + self.create = to_streamed_response_wrapper( + feedback.create, + ) + self.retrieve = to_streamed_response_wrapper( + feedback.retrieve, + ) + self.update = to_streamed_response_wrapper( + feedback.update, + ) + self.list = to_streamed_response_wrapper( + feedback.list, + ) + self.delete = to_streamed_response_wrapper( + feedback.delete, + ) + + @cached_property + def tokens(self) -> TokensResourceWithStreamingResponse: + return TokensResourceWithStreamingResponse(self._feedback.tokens) + + @cached_property + def configs(self) -> ConfigsResourceWithStreamingResponse: + return ConfigsResourceWithStreamingResponse(self._feedback.configs) + + +class AsyncFeedbackResourceWithStreamingResponse: + def __init__(self, feedback: AsyncFeedbackResource) -> None: + self._feedback = feedback + + self.create = async_to_streamed_response_wrapper( + feedback.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + feedback.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + feedback.update, + ) + self.list = async_to_streamed_response_wrapper( + feedback.list, + ) + self.delete = async_to_streamed_response_wrapper( + feedback.delete, + ) + + @cached_property + def tokens(self) -> AsyncTokensResourceWithStreamingResponse: + return AsyncTokensResourceWithStreamingResponse(self._feedback.tokens) + + @cached_property + def configs(self) -> AsyncConfigsResourceWithStreamingResponse: + return AsyncConfigsResourceWithStreamingResponse(self._feedback.configs) diff --git a/python/langsmith_api/resources/feedback/tokens.py b/python/langsmith_api/resources/feedback/tokens.py new file mode 100644 index 000000000..e85306efc --- /dev/null +++ b/python/langsmith_api/resources/feedback/tokens.py @@ -0,0 +1,604 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, Dict, Union, Iterable, Optional, cast +from datetime import datetime +from typing_extensions import overload + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.feedback import token_list_params, token_create_params, token_update_params, token_retrieve_params +from ...types.timedelta_input_param import TimedeltaInputParam +from ...types.feedback.token_list_response import TokenListResponse +from ...types.feedback.token_create_response import TokenCreateResponse +from ...types.feedback.feedback_ingest_token_create_schema_param import FeedbackIngestTokenCreateSchemaParam + +__all__ = ["TokensResource", "AsyncTokensResource"] + + +class TokensResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TokensResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return TokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TokensResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return TokensResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + feedback_key: str, + run_id: str, + expires_at: Union[str, datetime, None] | Omit = omit, + expires_in: Optional[TimedeltaInputParam] | Omit = omit, + feedback_config: Optional[token_create_params.FeedbackIngestTokenCreateSchemaFeedbackConfig] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + """ + Create a new feedback ingest token. + + Args: + expires_in: Timedelta input. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + body: Iterable[FeedbackIngestTokenCreateSchemaParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + """ + Create a new feedback ingest token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["feedback_key", "run_id"], ["body"]) + def create( + self, + *, + feedback_key: str | Omit = omit, + run_id: str | Omit = omit, + expires_at: Union[str, datetime, None] | Omit = omit, + expires_in: Optional[TimedeltaInputParam] | Omit = omit, + feedback_config: Optional[token_create_params.FeedbackIngestTokenCreateSchemaFeedbackConfig] | Omit = omit, + body: Iterable[FeedbackIngestTokenCreateSchemaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + return cast( + TokenCreateResponse, + self._post( + "/api/v1/feedback/tokens", + body=maybe_transform( + { + "feedback_key": feedback_key, + "run_id": run_id, + "expires_at": expires_at, + "expires_in": expires_in, + "feedback_config": feedback_config, + "body": body, + }, + token_create_params.TokenCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, TokenCreateResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + def retrieve( + self, + token: str, + *, + comment: Optional[str] | Omit = omit, + correction: Optional[str] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create a new feedback with a token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return self._get( + path_template("/api/v1/feedback/tokens/{token}", token=token), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "comment": comment, + "correction": correction, + "score": score, + "value": value, + }, + token_retrieve_params.TokenRetrieveParams, + ), + ), + cast_to=object, + ) + + def update( + self, + token: str, + *, + comment: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create a new feedback with a token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return self._post( + path_template("/api/v1/feedback/tokens/{token}", token=token), + body=maybe_transform( + { + "comment": comment, + "correction": correction, + "metadata": metadata, + "score": score, + "value": value, + }, + token_update_params.TokenUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def list( + self, + *, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenListResponse: + """ + List all feedback ingest tokens for a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/api/v1/feedback/tokens", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"run_id": run_id}, token_list_params.TokenListParams), + ), + cast_to=TokenListResponse, + ) + + +class AsyncTokensResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTokensResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncTokensResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTokensResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncTokensResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + feedback_key: str, + run_id: str, + expires_at: Union[str, datetime, None] | Omit = omit, + expires_in: Optional[TimedeltaInputParam] | Omit = omit, + feedback_config: Optional[token_create_params.FeedbackIngestTokenCreateSchemaFeedbackConfig] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + """ + Create a new feedback ingest token. + + Args: + expires_in: Timedelta input. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + body: Iterable[FeedbackIngestTokenCreateSchemaParam], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + """ + Create a new feedback ingest token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["feedback_key", "run_id"], ["body"]) + async def create( + self, + *, + feedback_key: str | Omit = omit, + run_id: str | Omit = omit, + expires_at: Union[str, datetime, None] | Omit = omit, + expires_in: Optional[TimedeltaInputParam] | Omit = omit, + feedback_config: Optional[token_create_params.FeedbackIngestTokenCreateSchemaFeedbackConfig] | Omit = omit, + body: Iterable[FeedbackIngestTokenCreateSchemaParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenCreateResponse: + return cast( + TokenCreateResponse, + await self._post( + "/api/v1/feedback/tokens", + body=await async_maybe_transform( + { + "feedback_key": feedback_key, + "run_id": run_id, + "expires_at": expires_at, + "expires_in": expires_in, + "feedback_config": feedback_config, + "body": body, + }, + token_create_params.TokenCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, TokenCreateResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + async def retrieve( + self, + token: str, + *, + comment: Optional[str] | Omit = omit, + correction: Optional[str] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create a new feedback with a token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return await self._get( + path_template("/api/v1/feedback/tokens/{token}", token=token), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "comment": comment, + "correction": correction, + "score": score, + "value": value, + }, + token_retrieve_params.TokenRetrieveParams, + ), + ), + cast_to=object, + ) + + async def update( + self, + token: str, + *, + comment: Optional[str] | Omit = omit, + correction: Union[Dict[str, object], str, None] | Omit = omit, + metadata: Optional[Dict[str, object]] | Omit = omit, + score: Union[float, bool, None] | Omit = omit, + value: Union[float, bool, str, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Create a new feedback with a token. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token: + raise ValueError(f"Expected a non-empty value for `token` but received {token!r}") + return await self._post( + path_template("/api/v1/feedback/tokens/{token}", token=token), + body=await async_maybe_transform( + { + "comment": comment, + "correction": correction, + "metadata": metadata, + "score": score, + "value": value, + }, + token_update_params.TokenUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def list( + self, + *, + run_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TokenListResponse: + """ + List all feedback ingest tokens for a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/api/v1/feedback/tokens", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"run_id": run_id}, token_list_params.TokenListParams), + ), + cast_to=TokenListResponse, + ) + + +class TokensResourceWithRawResponse: + def __init__(self, tokens: TokensResource) -> None: + self._tokens = tokens + + self.create = to_raw_response_wrapper( + tokens.create, + ) + self.retrieve = to_raw_response_wrapper( + tokens.retrieve, + ) + self.update = to_raw_response_wrapper( + tokens.update, + ) + self.list = to_raw_response_wrapper( + tokens.list, + ) + + +class AsyncTokensResourceWithRawResponse: + def __init__(self, tokens: AsyncTokensResource) -> None: + self._tokens = tokens + + self.create = async_to_raw_response_wrapper( + tokens.create, + ) + self.retrieve = async_to_raw_response_wrapper( + tokens.retrieve, + ) + self.update = async_to_raw_response_wrapper( + tokens.update, + ) + self.list = async_to_raw_response_wrapper( + tokens.list, + ) + + +class TokensResourceWithStreamingResponse: + def __init__(self, tokens: TokensResource) -> None: + self._tokens = tokens + + self.create = to_streamed_response_wrapper( + tokens.create, + ) + self.retrieve = to_streamed_response_wrapper( + tokens.retrieve, + ) + self.update = to_streamed_response_wrapper( + tokens.update, + ) + self.list = to_streamed_response_wrapper( + tokens.list, + ) + + +class AsyncTokensResourceWithStreamingResponse: + def __init__(self, tokens: AsyncTokensResource) -> None: + self._tokens = tokens + + self.create = async_to_streamed_response_wrapper( + tokens.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + tokens.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + tokens.update, + ) + self.list = async_to_streamed_response_wrapper( + tokens.list, + ) diff --git a/python/langsmith_api/resources/info.py b/python/langsmith_api/resources/info.py new file mode 100644 index 000000000..23f1fb64b --- /dev/null +++ b/python/langsmith_api/resources/info.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.info_list_response import InfoListResponse + +__all__ = ["InfoResource", "AsyncInfoResource"] + + +class InfoResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return InfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return InfoResourceWithStreamingResponse(self) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InfoListResponse: + """Get information about the current deployment of LangSmith.""" + return self._get( + "/api/v1/info", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InfoListResponse, + ) + + +class AsyncInfoResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInfoResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncInfoResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInfoResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncInfoResourceWithStreamingResponse(self) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InfoListResponse: + """Get information about the current deployment of LangSmith.""" + return await self._get( + "/api/v1/info", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InfoListResponse, + ) + + +class InfoResourceWithRawResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.list = to_raw_response_wrapper( + info.list, + ) + + +class AsyncInfoResourceWithRawResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.list = async_to_raw_response_wrapper( + info.list, + ) + + +class InfoResourceWithStreamingResponse: + def __init__(self, info: InfoResource) -> None: + self._info = info + + self.list = to_streamed_response_wrapper( + info.list, + ) + + +class AsyncInfoResourceWithStreamingResponse: + def __init__(self, info: AsyncInfoResource) -> None: + self._info = info + + self.list = async_to_streamed_response_wrapper( + info.list, + ) diff --git a/python/langsmith_api/resources/public/__init__.py b/python/langsmith_api/resources/public/__init__.py new file mode 100644 index 000000000..8fe464060 --- /dev/null +++ b/python/langsmith_api/resources/public/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .public import ( + PublicResource, + AsyncPublicResource, + PublicResourceWithRawResponse, + AsyncPublicResourceWithRawResponse, + PublicResourceWithStreamingResponse, + AsyncPublicResourceWithStreamingResponse, +) +from .datasets import ( + DatasetsResource, + AsyncDatasetsResource, + DatasetsResourceWithRawResponse, + AsyncDatasetsResourceWithRawResponse, + DatasetsResourceWithStreamingResponse, + AsyncDatasetsResourceWithStreamingResponse, +) + +__all__ = [ + "DatasetsResource", + "AsyncDatasetsResource", + "DatasetsResourceWithRawResponse", + "AsyncDatasetsResourceWithRawResponse", + "DatasetsResourceWithStreamingResponse", + "AsyncDatasetsResourceWithStreamingResponse", + "PublicResource", + "AsyncPublicResource", + "PublicResourceWithRawResponse", + "AsyncPublicResourceWithRawResponse", + "PublicResourceWithStreamingResponse", + "AsyncPublicResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/public/datasets.py b/python/langsmith_api/resources/public/datasets.py new file mode 100644 index 000000000..63b31aca5 --- /dev/null +++ b/python/langsmith_api/resources/public/datasets.py @@ -0,0 +1,715 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +import httpx + +from ...types import FeedbackLevel, SortByDatasetColumn, SessionSortableColumns +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.public import ( + dataset_list_params, + dataset_list_feedback_params, + dataset_list_sessions_params, + dataset_list_comparative_params, + dataset_retrieve_sessions_bulk_params, +) +from ...types.datasets import SortByComparativeExperimentColumn +from ...types.source_type import SourceType +from ...types.feedback_level import FeedbackLevel +from ...types.tracer_session import TracerSession +from ...types.feedback_schema import FeedbackSchema +from ...types.sort_by_dataset_column import SortByDatasetColumn +from ...types.session_sortable_columns import SessionSortableColumns +from ...types.public.dataset_list_response import DatasetListResponse +from ...types.public.dataset_list_comparative_response import DatasetListComparativeResponse +from ...types.datasets.sort_by_comparative_experiment_column import SortByComparativeExperimentColumn +from ...types.public.dataset_retrieve_sessions_bulk_response import DatasetRetrieveSessionsBulkResponse + +__all__ = ["DatasetsResource", "AsyncDatasetsResource"] + + +class DatasetsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return DatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return DatasetsResourceWithStreamingResponse(self) + + def list( + self, + share_token: str, + *, + limit: int | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByDatasetColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetListResponse: + """ + Get dataset by ids or the shared dataset if not specifed. + + Args: + sort_by: Enum for available dataset columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get( + path_template("/api/v1/public/{share_token}/datasets", share_token=share_token), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + }, + dataset_list_params.DatasetListParams, + ), + ), + cast_to=DatasetListResponse, + ) + + def list_comparative( + self, + share_token: str, + *, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByComparativeExperimentColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[DatasetListComparativeResponse]: + """ + Get all comparative experiments for a given dataset. + + Args: + sort_by: Enum for available comparative experiment columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/comparative", share_token=share_token), + page=SyncOffsetPaginationTopLevelArray[DatasetListComparativeResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + }, + dataset_list_comparative_params.DatasetListComparativeParams, + ), + ), + model=DatasetListComparativeResponse, + ) + + def list_feedback( + self, + share_token: str, + *, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + run: Optional[SequenceNotStr[str]] | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[FeedbackSchema]: + """ + Get feedback for runs in projects run over a dataset that has been shared. + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/feedback", share_token=share_token), + page=SyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_comment": has_comment, + "has_score": has_score, + "key": key, + "level": level, + "limit": limit, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + dataset_list_feedback_params.DatasetListFeedbackParams, + ), + ), + model=FeedbackSchema, + ) + + def list_sessions( + self, + share_token: str, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_version: Optional[str] | Omit = omit, + facets: bool | Omit = omit, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SessionSortableColumns | Omit = omit, + sort_by_desc: bool | Omit = omit, + sort_by_feedback_key: Optional[str] | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[TracerSession]: + """ + Get projects run on a dataset that has been shared. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/sessions", share_token=share_token), + page=SyncOffsetPaginationTopLevelArray[TracerSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "dataset_version": dataset_version, + "facets": facets, + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "sort_by_feedback_key": sort_by_feedback_key, + }, + dataset_list_sessions_params.DatasetListSessionsParams, + ), + ), + model=TracerSession, + ) + + def retrieve_sessions_bulk( + self, + *, + share_tokens: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetRetrieveSessionsBulkResponse: + """ + Get sessions from multiple datasets using share tokens. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/api/v1/public/datasets/sessions-bulk", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"share_tokens": share_tokens}, + dataset_retrieve_sessions_bulk_params.DatasetRetrieveSessionsBulkParams, + ), + ), + cast_to=DatasetRetrieveSessionsBulkResponse, + ) + + +class AsyncDatasetsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDatasetsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncDatasetsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDatasetsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncDatasetsResourceWithStreamingResponse(self) + + async def list( + self, + share_token: str, + *, + limit: int | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByDatasetColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetListResponse: + """ + Get dataset by ids or the shared dataset if not specifed. + + Args: + sort_by: Enum for available dataset columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return await self._get( + path_template("/api/v1/public/{share_token}/datasets", share_token=share_token), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "limit": limit, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + }, + dataset_list_params.DatasetListParams, + ), + ), + cast_to=DatasetListResponse, + ) + + def list_comparative( + self, + share_token: str, + *, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SortByComparativeExperimentColumn | Omit = omit, + sort_by_desc: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ + DatasetListComparativeResponse, AsyncOffsetPaginationTopLevelArray[DatasetListComparativeResponse] + ]: + """ + Get all comparative experiments for a given dataset. + + Args: + sort_by: Enum for available comparative experiment columns to sort by. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/comparative", share_token=share_token), + page=AsyncOffsetPaginationTopLevelArray[DatasetListComparativeResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + }, + dataset_list_comparative_params.DatasetListComparativeParams, + ), + ), + model=DatasetListComparativeResponse, + ) + + def list_feedback( + self, + share_token: str, + *, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + run: Optional[SequenceNotStr[str]] | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FeedbackSchema, AsyncOffsetPaginationTopLevelArray[FeedbackSchema]]: + """ + Get feedback for runs in projects run over a dataset that has been shared. + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/feedback", share_token=share_token), + page=AsyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_comment": has_comment, + "has_score": has_score, + "key": key, + "level": level, + "limit": limit, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + dataset_list_feedback_params.DatasetListFeedbackParams, + ), + ), + model=FeedbackSchema, + ) + + def list_sessions( + self, + share_token: str, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_version: Optional[str] | Omit = omit, + facets: bool | Omit = omit, + limit: int | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + sort_by: SessionSortableColumns | Omit = omit, + sort_by_desc: bool | Omit = omit, + sort_by_feedback_key: Optional[str] | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[TracerSession, AsyncOffsetPaginationTopLevelArray[TracerSession]]: + """ + Get projects run on a dataset that has been shared. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._get_api_list( + path_template("/api/v1/public/{share_token}/datasets/sessions", share_token=share_token), + page=AsyncOffsetPaginationTopLevelArray[TracerSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "dataset_version": dataset_version, + "facets": facets, + "limit": limit, + "name": name, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "sort_by_feedback_key": sort_by_feedback_key, + }, + dataset_list_sessions_params.DatasetListSessionsParams, + ), + ), + model=TracerSession, + ) + + async def retrieve_sessions_bulk( + self, + *, + share_tokens: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DatasetRetrieveSessionsBulkResponse: + """ + Get sessions from multiple datasets using share tokens. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/api/v1/public/datasets/sessions-bulk", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"share_tokens": share_tokens}, + dataset_retrieve_sessions_bulk_params.DatasetRetrieveSessionsBulkParams, + ), + ), + cast_to=DatasetRetrieveSessionsBulkResponse, + ) + + +class DatasetsResourceWithRawResponse: + def __init__(self, datasets: DatasetsResource) -> None: + self._datasets = datasets + + self.list = to_raw_response_wrapper( + datasets.list, + ) + self.list_comparative = to_raw_response_wrapper( + datasets.list_comparative, + ) + self.list_feedback = to_raw_response_wrapper( + datasets.list_feedback, + ) + self.list_sessions = to_raw_response_wrapper( + datasets.list_sessions, + ) + self.retrieve_sessions_bulk = to_raw_response_wrapper( + datasets.retrieve_sessions_bulk, + ) + + +class AsyncDatasetsResourceWithRawResponse: + def __init__(self, datasets: AsyncDatasetsResource) -> None: + self._datasets = datasets + + self.list = async_to_raw_response_wrapper( + datasets.list, + ) + self.list_comparative = async_to_raw_response_wrapper( + datasets.list_comparative, + ) + self.list_feedback = async_to_raw_response_wrapper( + datasets.list_feedback, + ) + self.list_sessions = async_to_raw_response_wrapper( + datasets.list_sessions, + ) + self.retrieve_sessions_bulk = async_to_raw_response_wrapper( + datasets.retrieve_sessions_bulk, + ) + + +class DatasetsResourceWithStreamingResponse: + def __init__(self, datasets: DatasetsResource) -> None: + self._datasets = datasets + + self.list = to_streamed_response_wrapper( + datasets.list, + ) + self.list_comparative = to_streamed_response_wrapper( + datasets.list_comparative, + ) + self.list_feedback = to_streamed_response_wrapper( + datasets.list_feedback, + ) + self.list_sessions = to_streamed_response_wrapper( + datasets.list_sessions, + ) + self.retrieve_sessions_bulk = to_streamed_response_wrapper( + datasets.retrieve_sessions_bulk, + ) + + +class AsyncDatasetsResourceWithStreamingResponse: + def __init__(self, datasets: AsyncDatasetsResource) -> None: + self._datasets = datasets + + self.list = async_to_streamed_response_wrapper( + datasets.list, + ) + self.list_comparative = async_to_streamed_response_wrapper( + datasets.list_comparative, + ) + self.list_feedback = async_to_streamed_response_wrapper( + datasets.list_feedback, + ) + self.list_sessions = async_to_streamed_response_wrapper( + datasets.list_sessions, + ) + self.retrieve_sessions_bulk = async_to_streamed_response_wrapper( + datasets.retrieve_sessions_bulk, + ) diff --git a/python/langsmith_api/resources/public/public.py b/python/langsmith_api/resources/public/public.py new file mode 100644 index 000000000..0eb9773d5 --- /dev/null +++ b/python/langsmith_api/resources/public/public.py @@ -0,0 +1,264 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional + +import httpx + +from ...types import FeedbackLevel, public_retrieve_feedbacks_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform +from .datasets import ( + DatasetsResource, + AsyncDatasetsResource, + DatasetsResourceWithRawResponse, + AsyncDatasetsResourceWithRawResponse, + DatasetsResourceWithStreamingResponse, + AsyncDatasetsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.source_type import SourceType +from ...types.feedback_level import FeedbackLevel +from ...types.feedback_schema import FeedbackSchema + +__all__ = ["PublicResource", "AsyncPublicResource"] + + +class PublicResource(SyncAPIResource): + @cached_property + def datasets(self) -> DatasetsResource: + return DatasetsResource(self._client) + + @cached_property + def with_raw_response(self) -> PublicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return PublicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PublicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return PublicResourceWithStreamingResponse(self) + + def retrieve_feedbacks( + self, + share_token: str, + *, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + run: Optional[SequenceNotStr[str]] | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[FeedbackSchema]: + """ + Read Shared Feedbacks + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/feedbacks", share_token=share_token), + page=SyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_comment": has_comment, + "has_score": has_score, + "key": key, + "level": level, + "limit": limit, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + public_retrieve_feedbacks_params.PublicRetrieveFeedbacksParams, + ), + ), + model=FeedbackSchema, + ) + + +class AsyncPublicResource(AsyncAPIResource): + @cached_property + def datasets(self) -> AsyncDatasetsResource: + return AsyncDatasetsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncPublicResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncPublicResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPublicResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncPublicResourceWithStreamingResponse(self) + + def retrieve_feedbacks( + self, + share_token: str, + *, + has_comment: Optional[bool] | Omit = omit, + has_score: Optional[bool] | Omit = omit, + key: Optional[SequenceNotStr[str]] | Omit = omit, + level: Optional[FeedbackLevel] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + run: Optional[SequenceNotStr[str]] | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + source: Optional[List[SourceType]] | Omit = omit, + user: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[FeedbackSchema, AsyncOffsetPaginationTopLevelArray[FeedbackSchema]]: + """ + Read Shared Feedbacks + + Args: + level: Enum for feedback levels. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not share_token: + raise ValueError(f"Expected a non-empty value for `share_token` but received {share_token!r}") + return self._get_api_list( + path_template("/api/v1/public/{share_token}/feedbacks", share_token=share_token), + page=AsyncOffsetPaginationTopLevelArray[FeedbackSchema], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_comment": has_comment, + "has_score": has_score, + "key": key, + "level": level, + "limit": limit, + "offset": offset, + "run": run, + "session": session, + "source": source, + "user": user, + }, + public_retrieve_feedbacks_params.PublicRetrieveFeedbacksParams, + ), + ), + model=FeedbackSchema, + ) + + +class PublicResourceWithRawResponse: + def __init__(self, public: PublicResource) -> None: + self._public = public + + self.retrieve_feedbacks = to_raw_response_wrapper( + public.retrieve_feedbacks, + ) + + @cached_property + def datasets(self) -> DatasetsResourceWithRawResponse: + return DatasetsResourceWithRawResponse(self._public.datasets) + + +class AsyncPublicResourceWithRawResponse: + def __init__(self, public: AsyncPublicResource) -> None: + self._public = public + + self.retrieve_feedbacks = async_to_raw_response_wrapper( + public.retrieve_feedbacks, + ) + + @cached_property + def datasets(self) -> AsyncDatasetsResourceWithRawResponse: + return AsyncDatasetsResourceWithRawResponse(self._public.datasets) + + +class PublicResourceWithStreamingResponse: + def __init__(self, public: PublicResource) -> None: + self._public = public + + self.retrieve_feedbacks = to_streamed_response_wrapper( + public.retrieve_feedbacks, + ) + + @cached_property + def datasets(self) -> DatasetsResourceWithStreamingResponse: + return DatasetsResourceWithStreamingResponse(self._public.datasets) + + +class AsyncPublicResourceWithStreamingResponse: + def __init__(self, public: AsyncPublicResource) -> None: + self._public = public + + self.retrieve_feedbacks = async_to_streamed_response_wrapper( + public.retrieve_feedbacks, + ) + + @cached_property + def datasets(self) -> AsyncDatasetsResourceWithStreamingResponse: + return AsyncDatasetsResourceWithStreamingResponse(self._public.datasets) diff --git a/python/langsmith_api/resources/repos/__init__.py b/python/langsmith_api/resources/repos/__init__.py new file mode 100644 index 000000000..7b40e3223 --- /dev/null +++ b/python/langsmith_api/resources/repos/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .repos import ( + ReposResource, + AsyncReposResource, + ReposResourceWithRawResponse, + AsyncReposResourceWithRawResponse, + ReposResourceWithStreamingResponse, + AsyncReposResourceWithStreamingResponse, +) +from .directories import ( + DirectoriesResource, + AsyncDirectoriesResource, + DirectoriesResourceWithRawResponse, + AsyncDirectoriesResourceWithRawResponse, + DirectoriesResourceWithStreamingResponse, + AsyncDirectoriesResourceWithStreamingResponse, +) + +__all__ = [ + "DirectoriesResource", + "AsyncDirectoriesResource", + "DirectoriesResourceWithRawResponse", + "AsyncDirectoriesResourceWithRawResponse", + "DirectoriesResourceWithStreamingResponse", + "AsyncDirectoriesResourceWithStreamingResponse", + "ReposResource", + "AsyncReposResource", + "ReposResourceWithRawResponse", + "AsyncReposResourceWithRawResponse", + "ReposResourceWithStreamingResponse", + "AsyncReposResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/repos/directories.py b/python/langsmith_api/resources/repos/directories.py new file mode 100644 index 000000000..b329f2006 --- /dev/null +++ b/python/langsmith_api/resources/repos/directories.py @@ -0,0 +1,384 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...types.repos import directory_list_params, directory_commit_params +from ..._base_client import make_request_options +from ...types.repos.directory_list_response import DirectoryListResponse +from ...types.repos.directory_commit_response import DirectoryCommitResponse + +__all__ = ["DirectoriesResource", "AsyncDirectoriesResource"] + + +class DirectoriesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DirectoriesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return DirectoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DirectoriesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return DirectoriesResourceWithStreamingResponse(self) + + def list( + self, + repo: str, + *, + owner: str, + commit: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DirectoryListResponse: + """ + Resolves the flattened file tree for an agent or skill repository at a specific + commit, tag, or latest. + + Args: + commit: Commit hash/tag to resolve (defaults to latest) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._get( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"commit": commit}, directory_list_params.DirectoryListParams), + ), + cast_to=DirectoryListResponse, + ) + + def delete( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes an agent or skill repository and its owned child file repositories. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def commit( + self, + repo: str, + *, + owner: str, + files: Dict[str, object] | Omit = omit, + parent_commit: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DirectoryCommitResponse: + """ + Creates a new directory commit for an agent or skill repository by applying + file/link create, update, and delete operations. + + Args: + files: Files maps path to an Entry (object = create/update/link, null = delete/unlink). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._post( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories/commits", owner=owner, repo=repo), + body=maybe_transform( + { + "files": files, + "parent_commit": parent_commit, + }, + directory_commit_params.DirectoryCommitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DirectoryCommitResponse, + ) + + +class AsyncDirectoriesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDirectoriesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncDirectoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDirectoriesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncDirectoriesResourceWithStreamingResponse(self) + + async def list( + self, + repo: str, + *, + owner: str, + commit: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DirectoryListResponse: + """ + Resolves the flattened file tree for an agent or skill repository at a specific + commit, tag, or latest. + + Args: + commit: Commit hash/tag to resolve (defaults to latest) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._get( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"commit": commit}, directory_list_params.DirectoryListParams), + ), + cast_to=DirectoryListResponse, + ) + + async def delete( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes an agent or skill repository and its owned child file repositories. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def commit( + self, + repo: str, + *, + owner: str, + files: Dict[str, object] | Omit = omit, + parent_commit: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DirectoryCommitResponse: + """ + Creates a new directory commit for an agent or skill repository by applying + file/link create, update, and delete operations. + + Args: + files: Files maps path to an Entry (object = create/update/link, null = delete/unlink). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._post( + path_template("/v1/platform/hub/repos/{owner}/{repo}/directories/commits", owner=owner, repo=repo), + body=await async_maybe_transform( + { + "files": files, + "parent_commit": parent_commit, + }, + directory_commit_params.DirectoryCommitParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DirectoryCommitResponse, + ) + + +class DirectoriesResourceWithRawResponse: + def __init__(self, directories: DirectoriesResource) -> None: + self._directories = directories + + self.list = to_raw_response_wrapper( + directories.list, + ) + self.delete = to_raw_response_wrapper( + directories.delete, + ) + self.commit = to_raw_response_wrapper( + directories.commit, + ) + + +class AsyncDirectoriesResourceWithRawResponse: + def __init__(self, directories: AsyncDirectoriesResource) -> None: + self._directories = directories + + self.list = async_to_raw_response_wrapper( + directories.list, + ) + self.delete = async_to_raw_response_wrapper( + directories.delete, + ) + self.commit = async_to_raw_response_wrapper( + directories.commit, + ) + + +class DirectoriesResourceWithStreamingResponse: + def __init__(self, directories: DirectoriesResource) -> None: + self._directories = directories + + self.list = to_streamed_response_wrapper( + directories.list, + ) + self.delete = to_streamed_response_wrapper( + directories.delete, + ) + self.commit = to_streamed_response_wrapper( + directories.commit, + ) + + +class AsyncDirectoriesResourceWithStreamingResponse: + def __init__(self, directories: AsyncDirectoriesResource) -> None: + self._directories = directories + + self.list = async_to_streamed_response_wrapper( + directories.list, + ) + self.delete = async_to_streamed_response_wrapper( + directories.delete, + ) + self.commit = async_to_streamed_response_wrapper( + directories.commit, + ) diff --git a/python/langsmith_api/resources/repos/repos.py b/python/langsmith_api/resources/repos/repos.py new file mode 100644 index 000000000..9a1213a44 --- /dev/null +++ b/python/langsmith_api/resources/repos/repos.py @@ -0,0 +1,689 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal + +import httpx + +from ...types import repo_list_params, repo_create_params, repo_update_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .directories import ( + DirectoriesResource, + AsyncDirectoriesResource, + DirectoriesResourceWithRawResponse, + AsyncDirectoriesResourceWithRawResponse, + DirectoriesResourceWithStreamingResponse, + AsyncDirectoriesResourceWithStreamingResponse, +) +from ...pagination import SyncOffsetPaginationRepos, AsyncOffsetPaginationRepos +from ..._base_client import AsyncPaginator, make_request_options +from ...types.get_repo_response import GetRepoResponse +from ...types.repo_with_lookups import RepoWithLookups +from ...types.create_repo_response import CreateRepoResponse + +__all__ = ["ReposResource", "AsyncReposResource"] + + +class ReposResource(SyncAPIResource): + @cached_property + def directories(self) -> DirectoriesResource: + return DirectoriesResource(self._client) + + @cached_property + def with_raw_response(self) -> ReposResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return ReposResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ReposResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return ReposResourceWithStreamingResponse(self) + + def create( + self, + *, + is_public: bool, + repo_handle: str, + description: Optional[str] | Omit = omit, + readme: Optional[str] | Omit = omit, + repo_type: Literal["prompt", "file", "agent", "skill"] | Omit = omit, + restricted_mode: Optional[bool] | Omit = omit, + source: Optional[Literal["internal", "external"]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CreateRepoResponse: + """ + Create a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/repos", + body=maybe_transform( + { + "is_public": is_public, + "repo_handle": repo_handle, + "description": description, + "readme": readme, + "repo_type": repo_type, + "restricted_mode": restricted_mode, + "source": source, + "tags": tags, + }, + repo_create_params.RepoCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreateRepoResponse, + ) + + def retrieve( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GetRepoResponse: + """ + Get a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._get( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetRepoResponse, + ) + + def update( + self, + repo: str, + *, + owner: str, + description: Optional[str] | Omit = omit, + is_archived: Optional[bool] | Omit = omit, + is_public: Optional[bool] | Omit = omit, + readme: Optional[str] | Omit = omit, + restricted_mode: Optional[bool] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CreateRepoResponse: + """ + Update a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._patch( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + body=maybe_transform( + { + "description": description, + "is_archived": is_archived, + "is_public": is_public, + "readme": readme, + "restricted_mode": restricted_mode, + "tags": tags, + }, + repo_update_params.RepoUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreateRepoResponse, + ) + + def list( + self, + *, + has_commits: Optional[bool] | Omit = omit, + is_archived: Optional[Literal["true", "allow", "false"]] | Omit = omit, + is_public: Optional[Literal["true", "false"]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + query: Optional[str] | Omit = omit, + single_repo_type: Optional[Literal["prompt", "file", "agent", "skill"]] | Omit = omit, + repo_types: Optional[List[Literal["prompt", "file", "agent", "skill"]]] | Omit = omit, + sort_direction: Optional[Literal["asc", "desc"]] | Omit = omit, + sort_field: Optional[Literal["num_likes", "num_downloads", "num_views", "updated_at", "relevance"]] + | Omit = omit, + source: Optional[Literal["internal", "external"]] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + tenant_handle: Optional[str] | Omit = omit, + tenant_id: Optional[str] | Omit = omit, + upstream_repo_handle: Optional[str] | Omit = omit, + upstream_repo_owner: Optional[str] | Omit = omit, + with_latest_manifest: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationRepos[RepoWithLookups]: + """ + Get all repos. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/repos", + page=SyncOffsetPaginationRepos[RepoWithLookups], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_commits": has_commits, + "is_archived": is_archived, + "is_public": is_public, + "limit": limit, + "offset": offset, + "query": query, + "single_repo_type": single_repo_type, + "repo_types": repo_types, + "sort_direction": sort_direction, + "sort_field": sort_field, + "source": source, + "tag_value_id": tag_value_id, + "tags": tags, + "tenant_handle": tenant_handle, + "tenant_id": tenant_id, + "upstream_repo_handle": upstream_repo_handle, + "upstream_repo_owner": upstream_repo_owner, + "with_latest_manifest": with_latest_manifest, + }, + repo_list_params.RepoListParams, + ), + ), + model=RepoWithLookups, + ) + + def delete( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return self._delete( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncReposResource(AsyncAPIResource): + @cached_property + def directories(self) -> AsyncDirectoriesResource: + return AsyncDirectoriesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncReposResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncReposResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncReposResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncReposResourceWithStreamingResponse(self) + + async def create( + self, + *, + is_public: bool, + repo_handle: str, + description: Optional[str] | Omit = omit, + readme: Optional[str] | Omit = omit, + repo_type: Literal["prompt", "file", "agent", "skill"] | Omit = omit, + restricted_mode: Optional[bool] | Omit = omit, + source: Optional[Literal["internal", "external"]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CreateRepoResponse: + """ + Create a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/repos", + body=await async_maybe_transform( + { + "is_public": is_public, + "repo_handle": repo_handle, + "description": description, + "readme": readme, + "repo_type": repo_type, + "restricted_mode": restricted_mode, + "source": source, + "tags": tags, + }, + repo_create_params.RepoCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreateRepoResponse, + ) + + async def retrieve( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GetRepoResponse: + """ + Get a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._get( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=GetRepoResponse, + ) + + async def update( + self, + repo: str, + *, + owner: str, + description: Optional[str] | Omit = omit, + is_archived: Optional[bool] | Omit = omit, + is_public: Optional[bool] | Omit = omit, + readme: Optional[str] | Omit = omit, + restricted_mode: Optional[bool] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CreateRepoResponse: + """ + Update a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._patch( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + body=await async_maybe_transform( + { + "description": description, + "is_archived": is_archived, + "is_public": is_public, + "readme": readme, + "restricted_mode": restricted_mode, + "tags": tags, + }, + repo_update_params.RepoUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CreateRepoResponse, + ) + + def list( + self, + *, + has_commits: Optional[bool] | Omit = omit, + is_archived: Optional[Literal["true", "allow", "false"]] | Omit = omit, + is_public: Optional[Literal["true", "false"]] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + query: Optional[str] | Omit = omit, + single_repo_type: Optional[Literal["prompt", "file", "agent", "skill"]] | Omit = omit, + repo_types: Optional[List[Literal["prompt", "file", "agent", "skill"]]] | Omit = omit, + sort_direction: Optional[Literal["asc", "desc"]] | Omit = omit, + sort_field: Optional[Literal["num_likes", "num_downloads", "num_views", "updated_at", "relevance"]] + | Omit = omit, + source: Optional[Literal["internal", "external"]] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + tags: Optional[SequenceNotStr[str]] | Omit = omit, + tenant_handle: Optional[str] | Omit = omit, + tenant_id: Optional[str] | Omit = omit, + upstream_repo_handle: Optional[str] | Omit = omit, + upstream_repo_owner: Optional[str] | Omit = omit, + with_latest_manifest: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RepoWithLookups, AsyncOffsetPaginationRepos[RepoWithLookups]]: + """ + Get all repos. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/api/v1/repos", + page=AsyncOffsetPaginationRepos[RepoWithLookups], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "has_commits": has_commits, + "is_archived": is_archived, + "is_public": is_public, + "limit": limit, + "offset": offset, + "query": query, + "single_repo_type": single_repo_type, + "repo_types": repo_types, + "sort_direction": sort_direction, + "sort_field": sort_field, + "source": source, + "tag_value_id": tag_value_id, + "tags": tags, + "tenant_handle": tenant_handle, + "tenant_id": tenant_id, + "upstream_repo_handle": upstream_repo_handle, + "upstream_repo_owner": upstream_repo_owner, + "with_latest_manifest": with_latest_manifest, + }, + repo_list_params.RepoListParams, + ), + ), + model=RepoWithLookups, + ) + + async def delete( + self, + repo: str, + *, + owner: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a repo. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not owner: + raise ValueError(f"Expected a non-empty value for `owner` but received {owner!r}") + if not repo: + raise ValueError(f"Expected a non-empty value for `repo` but received {repo!r}") + return await self._delete( + path_template("/api/v1/repos/{owner}/{repo}", owner=owner, repo=repo), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class ReposResourceWithRawResponse: + def __init__(self, repos: ReposResource) -> None: + self._repos = repos + + self.create = to_raw_response_wrapper( + repos.create, + ) + self.retrieve = to_raw_response_wrapper( + repos.retrieve, + ) + self.update = to_raw_response_wrapper( + repos.update, + ) + self.list = to_raw_response_wrapper( + repos.list, + ) + self.delete = to_raw_response_wrapper( + repos.delete, + ) + + @cached_property + def directories(self) -> DirectoriesResourceWithRawResponse: + return DirectoriesResourceWithRawResponse(self._repos.directories) + + +class AsyncReposResourceWithRawResponse: + def __init__(self, repos: AsyncReposResource) -> None: + self._repos = repos + + self.create = async_to_raw_response_wrapper( + repos.create, + ) + self.retrieve = async_to_raw_response_wrapper( + repos.retrieve, + ) + self.update = async_to_raw_response_wrapper( + repos.update, + ) + self.list = async_to_raw_response_wrapper( + repos.list, + ) + self.delete = async_to_raw_response_wrapper( + repos.delete, + ) + + @cached_property + def directories(self) -> AsyncDirectoriesResourceWithRawResponse: + return AsyncDirectoriesResourceWithRawResponse(self._repos.directories) + + +class ReposResourceWithStreamingResponse: + def __init__(self, repos: ReposResource) -> None: + self._repos = repos + + self.create = to_streamed_response_wrapper( + repos.create, + ) + self.retrieve = to_streamed_response_wrapper( + repos.retrieve, + ) + self.update = to_streamed_response_wrapper( + repos.update, + ) + self.list = to_streamed_response_wrapper( + repos.list, + ) + self.delete = to_streamed_response_wrapper( + repos.delete, + ) + + @cached_property + def directories(self) -> DirectoriesResourceWithStreamingResponse: + return DirectoriesResourceWithStreamingResponse(self._repos.directories) + + +class AsyncReposResourceWithStreamingResponse: + def __init__(self, repos: AsyncReposResource) -> None: + self._repos = repos + + self.create = async_to_streamed_response_wrapper( + repos.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + repos.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + repos.update, + ) + self.list = async_to_streamed_response_wrapper( + repos.list, + ) + self.delete = async_to_streamed_response_wrapper( + repos.delete, + ) + + @cached_property + def directories(self) -> AsyncDirectoriesResourceWithStreamingResponse: + return AsyncDirectoriesResourceWithStreamingResponse(self._repos.directories) diff --git a/python/langsmith_api/resources/runs.py b/python/langsmith_api/resources/runs.py new file mode 100644 index 000000000..5f9a3538e --- /dev/null +++ b/python/langsmith_api/resources/runs.py @@ -0,0 +1,1329 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, Dict, List, Union, Iterable, Optional, cast +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ..types import ( + RunTypeEnum, + RunsFilterDataSourceTypeEnum, + run_query_params, + run_stats_params, + run_create_params, + run_update_params, + run_retrieve_params, + run_ingest_batch_params, +) +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.run_param import RunParam +from ..types.run_schema import RunSchema +from ..types.run_type_enum import RunTypeEnum +from ..types.run_query_response import RunQueryResponse +from ..types.run_stats_response import RunStatsResponse +from ..types.run_create_response import RunCreateResponse +from ..types.run_update_response import RunUpdateResponse +from ..types.run_stats_group_by_param import RunStatsGroupByParam +from ..types.run_ingest_batch_response import RunIngestBatchResponse +from ..types.runs_filter_data_source_type_enum import RunsFilterDataSourceTypeEnum + +__all__ = ["RunsResource", "AsyncRunsResource"] + + +class RunsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return RunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return RunsResourceWithStreamingResponse(self) + + def create( + self, + *, + id: str | Omit = omit, + dotted_order: str | Omit = omit, + end_time: str | Omit = omit, + error: str | Omit = omit, + events: Iterable[Dict[str, object]] | Omit = omit, + extra: Dict[str, object] | Omit = omit, + input_attachments: Dict[str, object] | Omit = omit, + inputs: Dict[str, object] | Omit = omit, + name: str | Omit = omit, + output_attachments: Dict[str, object] | Omit = omit, + outputs: Dict[str, object] | Omit = omit, + parent_run_id: str | Omit = omit, + reference_example_id: str | Omit = omit, + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] | Omit = omit, + serialized: Dict[str, object] | Omit = omit, + session_id: str | Omit = omit, + session_name: str | Omit = omit, + start_time: str | Omit = omit, + status: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + trace_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """Queues a single run for ingestion. + + The request body must be a JSON-encoded run + object that follows the Run schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/runs", + body=maybe_transform( + { + "id": id, + "dotted_order": dotted_order, + "end_time": end_time, + "error": error, + "events": events, + "extra": extra, + "input_attachments": input_attachments, + "inputs": inputs, + "name": name, + "output_attachments": output_attachments, + "outputs": outputs, + "parent_run_id": parent_run_id, + "reference_example_id": reference_example_id, + "run_type": run_type, + "serialized": serialized, + "session_id": session_id, + "session_name": session_name, + "start_time": start_time, + "status": status, + "tags": tags, + "trace_id": trace_id, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + def retrieve( + self, + run_id: str, + *, + exclude_s3_stored_attributes: bool | Omit = omit, + exclude_serialized: bool | Omit = omit, + include_messages: bool | Omit = omit, + session_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunSchema: + """ + Get a specific run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._get( + path_template("/api/v1/runs/{run_id}", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "exclude_s3_stored_attributes": exclude_s3_stored_attributes, + "exclude_serialized": exclude_serialized, + "include_messages": include_messages, + "session_id": session_id, + "start_time": start_time, + }, + run_retrieve_params.RunRetrieveParams, + ), + ), + cast_to=RunSchema, + ) + + def update( + self, + run_id: str, + *, + id: str | Omit = omit, + dotted_order: str | Omit = omit, + end_time: str | Omit = omit, + error: str | Omit = omit, + events: Iterable[Dict[str, object]] | Omit = omit, + extra: Dict[str, object] | Omit = omit, + input_attachments: Dict[str, object] | Omit = omit, + inputs: Dict[str, object] | Omit = omit, + name: str | Omit = omit, + output_attachments: Dict[str, object] | Omit = omit, + outputs: Dict[str, object] | Omit = omit, + parent_run_id: str | Omit = omit, + reference_example_id: str | Omit = omit, + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] | Omit = omit, + serialized: Dict[str, object] | Omit = omit, + session_id: str | Omit = omit, + session_name: str | Omit = omit, + start_time: str | Omit = omit, + status: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + trace_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunUpdateResponse: + """Updates a run identified by its ID. + + The body should contain only the fields to + be changed; unknown fields are ignored. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._patch( + path_template("/runs/{run_id}", run_id=run_id), + body=maybe_transform( + { + "id": id, + "dotted_order": dotted_order, + "end_time": end_time, + "error": error, + "events": events, + "extra": extra, + "input_attachments": input_attachments, + "inputs": inputs, + "name": name, + "output_attachments": output_attachments, + "outputs": outputs, + "parent_run_id": parent_run_id, + "reference_example_id": reference_example_id, + "run_type": run_type, + "serialized": serialized, + "session_id": session_id, + "session_name": session_name, + "start_time": start_time, + "status": status, + "tags": tags, + "trace_id": trace_id, + }, + run_update_params.RunUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunUpdateResponse, + ) + + def ingest_batch( + self, + *, + patch: Iterable[RunParam] | Omit = omit, + post: Iterable[RunParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunIngestBatchResponse: + """Ingests a batch of runs in a single JSON payload. + + The payload must have `post` + and/or `patch` arrays containing run objects. Prefer this endpoint over + single‑run ingestion when submitting hundreds of runs, but `/runs/multipart` + offers better handling for very large fields and attachments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/runs/batch", + body=maybe_transform( + { + "patch": patch, + "post": post, + }, + run_ingest_batch_params.RunIngestBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunIngestBatchResponse, + ) + + def query( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + cursor: Optional[str] | Omit = omit, + data_source_type: Optional[RunsFilterDataSourceTypeEnum] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + error: Optional[bool] | Omit = omit, + execution_order: Optional[int] | Omit = omit, + filter: Optional[str] | Omit = omit, + is_root: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + parent_run: Optional[str] | Omit = omit, + query: Optional[str] | Omit = omit, + reference_example: Optional[SequenceNotStr[str]] | Omit = omit, + run_type: Optional[RunTypeEnum] | Omit = omit, + search_filter: Optional[str] | Omit = omit, + select: List[ + Literal[ + "id", + "name", + "run_type", + "start_time", + "end_time", + "status", + "error", + "extra", + "events", + "inputs", + "inputs_preview", + "inputs_s3_urls", + "inputs_or_signed_url", + "outputs", + "outputs_preview", + "outputs_s3_urls", + "outputs_or_signed_url", + "s3_urls", + "error_or_signed_url", + "events_or_signed_url", + "extra_or_signed_url", + "serialized_or_signed_url", + "parent_run_id", + "manifest_id", + "manifest_s3_id", + "manifest", + "session_id", + "serialized", + "reference_example_id", + "reference_dataset_id", + "total_tokens", + "prompt_tokens", + "prompt_token_details", + "completion_tokens", + "completion_token_details", + "total_cost", + "prompt_cost", + "prompt_cost_details", + "completion_cost", + "completion_cost_details", + "price_model_id", + "first_token_time", + "trace_id", + "dotted_order", + "last_queued_at", + "feedback_stats", + "child_run_ids", + "parent_run_ids", + "tags", + "in_dataset", + "app_path", + "share_token", + "trace_tier", + "trace_first_received_at", + "ttl_seconds", + "trace_upgrade", + "thread_id", + "trace_min_max_start_time", + "messages", + "inserted_at", + ] + ] + | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + skip_pagination: Optional[bool] | Omit = omit, + skip_prev_cursor: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace: Optional[str] | Omit = omit, + trace_filter: Optional[str] | Omit = omit, + tree_filter: Optional[str] | Omit = omit, + use_experimental_search: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunQueryResponse: + """ + Query Runs + + Args: + data_source_type: Enum for run data source types. + + order: Enum for run start date order. + + run_type: Enum for run types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/runs/query", + body=maybe_transform( + { + "id": id, + "cursor": cursor, + "data_source_type": data_source_type, + "end_time": end_time, + "error": error, + "execution_order": execution_order, + "filter": filter, + "is_root": is_root, + "limit": limit, + "order": order, + "parent_run": parent_run, + "query": query, + "reference_example": reference_example, + "run_type": run_type, + "search_filter": search_filter, + "select": select, + "session": session, + "skip_pagination": skip_pagination, + "skip_prev_cursor": skip_prev_cursor, + "start_time": start_time, + "trace": trace, + "trace_filter": trace_filter, + "tree_filter": tree_filter, + "use_experimental_search": use_experimental_search, + }, + run_query_params.RunQueryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunQueryResponse, + ) + + def stats( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + data_source_type: Optional[RunsFilterDataSourceTypeEnum] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + error: Optional[bool] | Omit = omit, + execution_order: Optional[int] | Omit = omit, + filter: Optional[str] | Omit = omit, + group_by: Optional[RunStatsGroupByParam] | Omit = omit, + groups: Optional[SequenceNotStr[Optional[str]]] | Omit = omit, + is_root: Optional[bool] | Omit = omit, + parent_run: Optional[str] | Omit = omit, + query: Optional[str] | Omit = omit, + reference_example: Optional[SequenceNotStr[str]] | Omit = omit, + run_type: Optional[RunTypeEnum] | Omit = omit, + search_filter: Optional[str] | Omit = omit, + select: Optional[ + List[ + Literal[ + "run_count", + "latency_p50", + "latency_p99", + "latency_avg", + "first_token_p50", + "first_token_p99", + "total_tokens", + "prompt_tokens", + "completion_tokens", + "median_tokens", + "completion_tokens_p50", + "prompt_tokens_p50", + "tokens_p99", + "completion_tokens_p99", + "prompt_tokens_p99", + "last_run_start_time", + "feedback_stats", + "thread_feedback_stats", + "run_facets", + "error_rate", + "streaming_rate", + "total_cost", + "prompt_cost", + "completion_cost", + "cost_p50", + "cost_p99", + "session_feedback_stats", + "all_run_stats", + "all_token_stats", + "prompt_token_details", + "completion_token_details", + "prompt_cost_details", + "completion_cost_details", + ] + ] + ] + | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + skip_pagination: Optional[bool] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace: Optional[str] | Omit = omit, + trace_filter: Optional[str] | Omit = omit, + tree_filter: Optional[str] | Omit = omit, + use_experimental_search: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunStatsResponse: + """ + Get all runs by query in body payload. + + Args: + data_source_type: Enum for run data source types. + + group_by: Group by param for run stats. + + run_type: Enum for run types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return cast( + RunStatsResponse, + self._post( + "/api/v1/runs/stats", + body=maybe_transform( + { + "id": id, + "data_source_type": data_source_type, + "end_time": end_time, + "error": error, + "execution_order": execution_order, + "filter": filter, + "group_by": group_by, + "groups": groups, + "is_root": is_root, + "parent_run": parent_run, + "query": query, + "reference_example": reference_example, + "run_type": run_type, + "search_filter": search_filter, + "select": select, + "session": session, + "skip_pagination": skip_pagination, + "start_time": start_time, + "trace": trace, + "trace_filter": trace_filter, + "tree_filter": tree_filter, + "use_experimental_search": use_experimental_search, + }, + run_stats_params.RunStatsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast(Any, RunStatsResponse), # Union types cannot be passed in as arguments in the type system + ), + ) + + def update_2( + self, + run_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return self._patch( + path_template("/api/v1/runs/{run_id}", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncRunsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRunsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncRunsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRunsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncRunsResourceWithStreamingResponse(self) + + async def create( + self, + *, + id: str | Omit = omit, + dotted_order: str | Omit = omit, + end_time: str | Omit = omit, + error: str | Omit = omit, + events: Iterable[Dict[str, object]] | Omit = omit, + extra: Dict[str, object] | Omit = omit, + input_attachments: Dict[str, object] | Omit = omit, + inputs: Dict[str, object] | Omit = omit, + name: str | Omit = omit, + output_attachments: Dict[str, object] | Omit = omit, + outputs: Dict[str, object] | Omit = omit, + parent_run_id: str | Omit = omit, + reference_example_id: str | Omit = omit, + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] | Omit = omit, + serialized: Dict[str, object] | Omit = omit, + session_id: str | Omit = omit, + session_name: str | Omit = omit, + start_time: str | Omit = omit, + status: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + trace_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunCreateResponse: + """Queues a single run for ingestion. + + The request body must be a JSON-encoded run + object that follows the Run schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/runs", + body=await async_maybe_transform( + { + "id": id, + "dotted_order": dotted_order, + "end_time": end_time, + "error": error, + "events": events, + "extra": extra, + "input_attachments": input_attachments, + "inputs": inputs, + "name": name, + "output_attachments": output_attachments, + "outputs": outputs, + "parent_run_id": parent_run_id, + "reference_example_id": reference_example_id, + "run_type": run_type, + "serialized": serialized, + "session_id": session_id, + "session_name": session_name, + "start_time": start_time, + "status": status, + "tags": tags, + "trace_id": trace_id, + }, + run_create_params.RunCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunCreateResponse, + ) + + async def retrieve( + self, + run_id: str, + *, + exclude_s3_stored_attributes: bool | Omit = omit, + exclude_serialized: bool | Omit = omit, + include_messages: bool | Omit = omit, + session_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunSchema: + """ + Get a specific run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._get( + path_template("/api/v1/runs/{run_id}", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "exclude_s3_stored_attributes": exclude_s3_stored_attributes, + "exclude_serialized": exclude_serialized, + "include_messages": include_messages, + "session_id": session_id, + "start_time": start_time, + }, + run_retrieve_params.RunRetrieveParams, + ), + ), + cast_to=RunSchema, + ) + + async def update( + self, + run_id: str, + *, + id: str | Omit = omit, + dotted_order: str | Omit = omit, + end_time: str | Omit = omit, + error: str | Omit = omit, + events: Iterable[Dict[str, object]] | Omit = omit, + extra: Dict[str, object] | Omit = omit, + input_attachments: Dict[str, object] | Omit = omit, + inputs: Dict[str, object] | Omit = omit, + name: str | Omit = omit, + output_attachments: Dict[str, object] | Omit = omit, + outputs: Dict[str, object] | Omit = omit, + parent_run_id: str | Omit = omit, + reference_example_id: str | Omit = omit, + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] | Omit = omit, + serialized: Dict[str, object] | Omit = omit, + session_id: str | Omit = omit, + session_name: str | Omit = omit, + start_time: str | Omit = omit, + status: str | Omit = omit, + tags: SequenceNotStr[str] | Omit = omit, + trace_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunUpdateResponse: + """Updates a run identified by its ID. + + The body should contain only the fields to + be changed; unknown fields are ignored. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._patch( + path_template("/runs/{run_id}", run_id=run_id), + body=await async_maybe_transform( + { + "id": id, + "dotted_order": dotted_order, + "end_time": end_time, + "error": error, + "events": events, + "extra": extra, + "input_attachments": input_attachments, + "inputs": inputs, + "name": name, + "output_attachments": output_attachments, + "outputs": outputs, + "parent_run_id": parent_run_id, + "reference_example_id": reference_example_id, + "run_type": run_type, + "serialized": serialized, + "session_id": session_id, + "session_name": session_name, + "start_time": start_time, + "status": status, + "tags": tags, + "trace_id": trace_id, + }, + run_update_params.RunUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunUpdateResponse, + ) + + async def ingest_batch( + self, + *, + patch: Iterable[RunParam] | Omit = omit, + post: Iterable[RunParam] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunIngestBatchResponse: + """Ingests a batch of runs in a single JSON payload. + + The payload must have `post` + and/or `patch` arrays containing run objects. Prefer this endpoint over + single‑run ingestion when submitting hundreds of runs, but `/runs/multipart` + offers better handling for very large fields and attachments. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/runs/batch", + body=await async_maybe_transform( + { + "patch": patch, + "post": post, + }, + run_ingest_batch_params.RunIngestBatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunIngestBatchResponse, + ) + + async def query( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + cursor: Optional[str] | Omit = omit, + data_source_type: Optional[RunsFilterDataSourceTypeEnum] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + error: Optional[bool] | Omit = omit, + execution_order: Optional[int] | Omit = omit, + filter: Optional[str] | Omit = omit, + is_root: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + parent_run: Optional[str] | Omit = omit, + query: Optional[str] | Omit = omit, + reference_example: Optional[SequenceNotStr[str]] | Omit = omit, + run_type: Optional[RunTypeEnum] | Omit = omit, + search_filter: Optional[str] | Omit = omit, + select: List[ + Literal[ + "id", + "name", + "run_type", + "start_time", + "end_time", + "status", + "error", + "extra", + "events", + "inputs", + "inputs_preview", + "inputs_s3_urls", + "inputs_or_signed_url", + "outputs", + "outputs_preview", + "outputs_s3_urls", + "outputs_or_signed_url", + "s3_urls", + "error_or_signed_url", + "events_or_signed_url", + "extra_or_signed_url", + "serialized_or_signed_url", + "parent_run_id", + "manifest_id", + "manifest_s3_id", + "manifest", + "session_id", + "serialized", + "reference_example_id", + "reference_dataset_id", + "total_tokens", + "prompt_tokens", + "prompt_token_details", + "completion_tokens", + "completion_token_details", + "total_cost", + "prompt_cost", + "prompt_cost_details", + "completion_cost", + "completion_cost_details", + "price_model_id", + "first_token_time", + "trace_id", + "dotted_order", + "last_queued_at", + "feedback_stats", + "child_run_ids", + "parent_run_ids", + "tags", + "in_dataset", + "app_path", + "share_token", + "trace_tier", + "trace_first_received_at", + "ttl_seconds", + "trace_upgrade", + "thread_id", + "trace_min_max_start_time", + "messages", + "inserted_at", + ] + ] + | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + skip_pagination: Optional[bool] | Omit = omit, + skip_prev_cursor: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace: Optional[str] | Omit = omit, + trace_filter: Optional[str] | Omit = omit, + tree_filter: Optional[str] | Omit = omit, + use_experimental_search: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunQueryResponse: + """ + Query Runs + + Args: + data_source_type: Enum for run data source types. + + order: Enum for run start date order. + + run_type: Enum for run types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/runs/query", + body=await async_maybe_transform( + { + "id": id, + "cursor": cursor, + "data_source_type": data_source_type, + "end_time": end_time, + "error": error, + "execution_order": execution_order, + "filter": filter, + "is_root": is_root, + "limit": limit, + "order": order, + "parent_run": parent_run, + "query": query, + "reference_example": reference_example, + "run_type": run_type, + "search_filter": search_filter, + "select": select, + "session": session, + "skip_pagination": skip_pagination, + "skip_prev_cursor": skip_prev_cursor, + "start_time": start_time, + "trace": trace, + "trace_filter": trace_filter, + "tree_filter": tree_filter, + "use_experimental_search": use_experimental_search, + }, + run_query_params.RunQueryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunQueryResponse, + ) + + async def stats( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + data_source_type: Optional[RunsFilterDataSourceTypeEnum] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + error: Optional[bool] | Omit = omit, + execution_order: Optional[int] | Omit = omit, + filter: Optional[str] | Omit = omit, + group_by: Optional[RunStatsGroupByParam] | Omit = omit, + groups: Optional[SequenceNotStr[Optional[str]]] | Omit = omit, + is_root: Optional[bool] | Omit = omit, + parent_run: Optional[str] | Omit = omit, + query: Optional[str] | Omit = omit, + reference_example: Optional[SequenceNotStr[str]] | Omit = omit, + run_type: Optional[RunTypeEnum] | Omit = omit, + search_filter: Optional[str] | Omit = omit, + select: Optional[ + List[ + Literal[ + "run_count", + "latency_p50", + "latency_p99", + "latency_avg", + "first_token_p50", + "first_token_p99", + "total_tokens", + "prompt_tokens", + "completion_tokens", + "median_tokens", + "completion_tokens_p50", + "prompt_tokens_p50", + "tokens_p99", + "completion_tokens_p99", + "prompt_tokens_p99", + "last_run_start_time", + "feedback_stats", + "thread_feedback_stats", + "run_facets", + "error_rate", + "streaming_rate", + "total_cost", + "prompt_cost", + "completion_cost", + "cost_p50", + "cost_p99", + "session_feedback_stats", + "all_run_stats", + "all_token_stats", + "prompt_token_details", + "completion_token_details", + "prompt_cost_details", + "completion_cost_details", + ] + ] + ] + | Omit = omit, + session: Optional[SequenceNotStr[str]] | Omit = omit, + skip_pagination: Optional[bool] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + trace: Optional[str] | Omit = omit, + trace_filter: Optional[str] | Omit = omit, + tree_filter: Optional[str] | Omit = omit, + use_experimental_search: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunStatsResponse: + """ + Get all runs by query in body payload. + + Args: + data_source_type: Enum for run data source types. + + group_by: Group by param for run stats. + + run_type: Enum for run types. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return cast( + RunStatsResponse, + await self._post( + "/api/v1/runs/stats", + body=await async_maybe_transform( + { + "id": id, + "data_source_type": data_source_type, + "end_time": end_time, + "error": error, + "execution_order": execution_order, + "filter": filter, + "group_by": group_by, + "groups": groups, + "is_root": is_root, + "parent_run": parent_run, + "query": query, + "reference_example": reference_example, + "run_type": run_type, + "search_filter": search_filter, + "select": select, + "session": session, + "skip_pagination": skip_pagination, + "start_time": start_time, + "trace": trace, + "trace_filter": trace_filter, + "tree_filter": tree_filter, + "use_experimental_search": use_experimental_search, + }, + run_stats_params.RunStatsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast(Any, RunStatsResponse), # Union types cannot be passed in as arguments in the type system + ), + ) + + async def update_2( + self, + run_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Update a run. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not run_id: + raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") + return await self._patch( + path_template("/api/v1/runs/{run_id}", run_id=run_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class RunsResourceWithRawResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_raw_response_wrapper( + runs.create, + ) + self.retrieve = to_raw_response_wrapper( + runs.retrieve, + ) + self.update = to_raw_response_wrapper( + runs.update, + ) + self.ingest_batch = to_raw_response_wrapper( + runs.ingest_batch, + ) + self.query = to_raw_response_wrapper( + runs.query, + ) + self.stats = to_raw_response_wrapper( + runs.stats, + ) + self.update_2 = to_raw_response_wrapper( + runs.update_2, + ) + + +class AsyncRunsResourceWithRawResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_raw_response_wrapper( + runs.create, + ) + self.retrieve = async_to_raw_response_wrapper( + runs.retrieve, + ) + self.update = async_to_raw_response_wrapper( + runs.update, + ) + self.ingest_batch = async_to_raw_response_wrapper( + runs.ingest_batch, + ) + self.query = async_to_raw_response_wrapper( + runs.query, + ) + self.stats = async_to_raw_response_wrapper( + runs.stats, + ) + self.update_2 = async_to_raw_response_wrapper( + runs.update_2, + ) + + +class RunsResourceWithStreamingResponse: + def __init__(self, runs: RunsResource) -> None: + self._runs = runs + + self.create = to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = to_streamed_response_wrapper( + runs.retrieve, + ) + self.update = to_streamed_response_wrapper( + runs.update, + ) + self.ingest_batch = to_streamed_response_wrapper( + runs.ingest_batch, + ) + self.query = to_streamed_response_wrapper( + runs.query, + ) + self.stats = to_streamed_response_wrapper( + runs.stats, + ) + self.update_2 = to_streamed_response_wrapper( + runs.update_2, + ) + + +class AsyncRunsResourceWithStreamingResponse: + def __init__(self, runs: AsyncRunsResource) -> None: + self._runs = runs + + self.create = async_to_streamed_response_wrapper( + runs.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + runs.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + runs.update, + ) + self.ingest_batch = async_to_streamed_response_wrapper( + runs.ingest_batch, + ) + self.query = async_to_streamed_response_wrapper( + runs.query, + ) + self.stats = async_to_streamed_response_wrapper( + runs.stats, + ) + self.update_2 = async_to_streamed_response_wrapper( + runs.update_2, + ) diff --git a/python/langsmith_api/resources/sandboxes/__init__.py b/python/langsmith_api/resources/sandboxes/__init__.py new file mode 100644 index 000000000..bdeebc1dc --- /dev/null +++ b/python/langsmith_api/resources/sandboxes/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .boxes import ( + BoxesResource, + AsyncBoxesResource, + BoxesResourceWithRawResponse, + AsyncBoxesResourceWithRawResponse, + BoxesResourceWithStreamingResponse, + AsyncBoxesResourceWithStreamingResponse, +) +from .sandboxes import ( + SandboxesResource, + AsyncSandboxesResource, + SandboxesResourceWithRawResponse, + AsyncSandboxesResourceWithRawResponse, + SandboxesResourceWithStreamingResponse, + AsyncSandboxesResourceWithStreamingResponse, +) +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) + +__all__ = [ + "BoxesResource", + "AsyncBoxesResource", + "BoxesResourceWithRawResponse", + "AsyncBoxesResourceWithRawResponse", + "BoxesResourceWithStreamingResponse", + "AsyncBoxesResourceWithStreamingResponse", + "SnapshotsResource", + "AsyncSnapshotsResource", + "SnapshotsResourceWithRawResponse", + "AsyncSnapshotsResourceWithRawResponse", + "SnapshotsResourceWithStreamingResponse", + "AsyncSnapshotsResourceWithStreamingResponse", + "SandboxesResource", + "AsyncSandboxesResource", + "SandboxesResourceWithRawResponse", + "AsyncSandboxesResourceWithRawResponse", + "SandboxesResourceWithStreamingResponse", + "AsyncSandboxesResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/sandboxes/boxes.py b/python/langsmith_api/resources/sandboxes/boxes.py new file mode 100644 index 000000000..3062ec2a7 --- /dev/null +++ b/python/langsmith_api/resources/sandboxes/boxes.py @@ -0,0 +1,1166 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.sandboxes import ( + box_list_params, + box_create_params, + box_update_params, + box_create_snapshot_params, + box_generate_service_url_params, +) +from ...types.sandboxes.box_list_response import BoxListResponse +from ...types.sandboxes.box_start_response import BoxStartResponse +from ...types.sandboxes.box_create_response import BoxCreateResponse +from ...types.sandboxes.box_update_response import BoxUpdateResponse +from ...types.sandboxes.box_retrieve_response import BoxRetrieveResponse +from ...types.sandboxes.box_get_status_response import BoxGetStatusResponse +from ...types.sandboxes.box_create_snapshot_response import BoxCreateSnapshotResponse +from ...types.sandboxes.box_generate_service_url_response import BoxGenerateServiceURLResponse + +__all__ = ["BoxesResource", "AsyncBoxesResource"] + + +class BoxesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BoxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return BoxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BoxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return BoxesResourceWithStreamingResponse(self) + + def create( + self, + *, + delete_after_stop_seconds: int | Omit = omit, + env_vars: Dict[str, str] | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + idle_ttl_seconds: int | Omit = omit, + mem_bytes: int | Omit = omit, + name: str | Omit = omit, + proxy_config: box_create_params.ProxyConfig | Omit = omit, + restore_memory: bool | Omit = omit, + snapshot_id: str | Omit = omit, + snapshot_name: str | Omit = omit, + tag_value_ids: SequenceNotStr[str] | Omit = omit, + vcpus: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxCreateResponse: + """Create a new sandbox from a snapshot. + + Provide at most one of `snapshot_id` or + `snapshot_name`; if neither is provided, the server uses the default static + blueprint. + + Args: + restore_memory: RestoreMemory, when non-nil, overrides the server default for whether to resume + the sandbox from its captured memory snapshot. + + true → resume from the memory snapshot if it exists; cold-boot the sandbox + otherwise. false → always cold-boot, even if a memory snapshot exists. nil → use + the server default. + + Applies to this request only; a later stop+start of the same sandbox reverts to + the server default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/sandboxes/boxes", + body=maybe_transform( + { + "delete_after_stop_seconds": delete_after_stop_seconds, + "env_vars": env_vars, + "fs_capacity_bytes": fs_capacity_bytes, + "idle_ttl_seconds": idle_ttl_seconds, + "mem_bytes": mem_bytes, + "name": name, + "proxy_config": proxy_config, + "restore_memory": restore_memory, + "snapshot_id": snapshot_id, + "snapshot_name": snapshot_name, + "tag_value_ids": tag_value_ids, + "vcpus": vcpus, + }, + box_create_params.BoxCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxCreateResponse, + ) + + def retrieve( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxRetrieveResponse: + """Retrieve a sandbox by name. + + Stale provisioning sandboxes are auto-failed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return self._get( + path_template("/v2/sandboxes/boxes/{name}", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxRetrieveResponse, + ) + + def update( + self, + path_name: str, + *, + delete_after_stop_seconds: int | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + idle_ttl_seconds: int | Omit = omit, + mem_bytes: int | Omit = omit, + body_name: str | Omit = omit, + proxy_config: box_update_params.ProxyConfig | Omit = omit, + tag_value_ids: SequenceNotStr[str] | Omit = omit, + vcpus: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxUpdateResponse: + """Update a sandbox's display name. + + The name must be unique within the tenant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_name: + raise ValueError(f"Expected a non-empty value for `path_name` but received {path_name!r}") + return self._patch( + path_template("/v2/sandboxes/boxes/{path_name}", path_name=path_name), + body=maybe_transform( + { + "delete_after_stop_seconds": delete_after_stop_seconds, + "fs_capacity_bytes": fs_capacity_bytes, + "idle_ttl_seconds": idle_ttl_seconds, + "mem_bytes": mem_bytes, + "body_name": body_name, + "proxy_config": proxy_config, + "tag_value_ids": tag_value_ids, + "vcpus": vcpus, + }, + box_update_params.BoxUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxUpdateResponse, + ) + + def list( + self, + *, + created_by: str | Omit = omit, + limit: int | Omit = omit, + name_contains: str | Omit = omit, + offset: int | Omit = omit, + sort_by: str | Omit = omit, + sort_direction: str | Omit = omit, + status: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxListResponse: + """ + List sandboxes for the authenticated tenant, with optional filtering, sorting, + and pagination. + + Args: + created_by: Filter by creator identity. Only 'me' is supported. + + limit: Maximum number of results + + name_contains: Filter by name substring + + offset: Pagination offset + + sort_by: Sort column (name, status, created_at) + + sort_direction: Sort direction (asc, desc) + + status: Filter by status (provisioning, ready, failed, stopped, deleting) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/sandboxes/boxes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "created_by": created_by, + "limit": limit, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_direction": sort_direction, + "status": status, + }, + box_list_params.BoxListParams, + ), + ), + cast_to=BoxListResponse, + ) + + def delete( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Delete a sandbox by name or UUID. + + Tears down the sandbox runtime and removes the + DB record. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/v2/sandboxes/boxes/{name}", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def create_snapshot( + self, + path_name: str, + *, + body_name: str, + checkpoint: str | Omit = omit, + docker_image: str | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + include_memory: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxCreateSnapshotResponse: + """ + Create a snapshot by capturing the current state of a sandbox or promoting an + existing checkpoint. + + Args: + checkpoint: if omitted, creates a fresh checkpoint from the running VM + + docker_image: sandbox-local Docker image to export + + fs_capacity_bytes: required for Docker image export unless the sandbox has a capacity + + include_memory: IncludeMemory, when true, captures a full VM memory snapshot alongside the + filesystem clone. Only honored when the sandbox is running AND Checkpoint is + omitted (i.e. a fresh in-VM checkpoint is requested). Defaults to false to keep + snapshots small unless memory restore is explicitly desired. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_name: + raise ValueError(f"Expected a non-empty value for `path_name` but received {path_name!r}") + return self._post( + path_template("/v2/sandboxes/boxes/{path_name}/snapshot", path_name=path_name), + body=maybe_transform( + { + "body_name": body_name, + "checkpoint": checkpoint, + "docker_image": docker_image, + "fs_capacity_bytes": fs_capacity_bytes, + "include_memory": include_memory, + }, + box_create_snapshot_params.BoxCreateSnapshotParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxCreateSnapshotResponse, + ) + + def generate_service_url( + self, + name: str, + *, + expires_in_seconds: int | Omit = omit, + port: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxGenerateServiceURLResponse: + """ + Create a short-lived JWT for accessing an HTTP service running on a specific + port inside a sandbox. Returns a browser_url (sets auth cookie via redirect), a + service_url (for use with the X-Langsmith-Sandbox-Service-Token header), the raw + token, and its expiry. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return self._post( + path_template("/v2/sandboxes/boxes/{name}/service-url", name=name), + body=maybe_transform( + { + "expires_in_seconds": expires_in_seconds, + "port": port, + }, + box_generate_service_url_params.BoxGenerateServiceURLParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxGenerateServiceURLResponse, + ) + + def get_status( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxGetStatusResponse: + """ + Retrieve the lightweight status of a sandbox for polling. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return self._get( + path_template("/v2/sandboxes/boxes/{name}/status", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxGetStatusResponse, + ) + + def start( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxStartResponse: + """Start a stopped or failed sandbox. + + This endpoint is not idempotent. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return self._post( + path_template("/v2/sandboxes/boxes/{name}/start", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxStartResponse, + ) + + def stop( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Stop a ready sandbox. + + This endpoint is not idempotent; the filesystem is + preserved for later restart. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + path_template("/v2/sandboxes/boxes/{name}/stop", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncBoxesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBoxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncBoxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBoxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncBoxesResourceWithStreamingResponse(self) + + async def create( + self, + *, + delete_after_stop_seconds: int | Omit = omit, + env_vars: Dict[str, str] | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + idle_ttl_seconds: int | Omit = omit, + mem_bytes: int | Omit = omit, + name: str | Omit = omit, + proxy_config: box_create_params.ProxyConfig | Omit = omit, + restore_memory: bool | Omit = omit, + snapshot_id: str | Omit = omit, + snapshot_name: str | Omit = omit, + tag_value_ids: SequenceNotStr[str] | Omit = omit, + vcpus: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxCreateResponse: + """Create a new sandbox from a snapshot. + + Provide at most one of `snapshot_id` or + `snapshot_name`; if neither is provided, the server uses the default static + blueprint. + + Args: + restore_memory: RestoreMemory, when non-nil, overrides the server default for whether to resume + the sandbox from its captured memory snapshot. + + true → resume from the memory snapshot if it exists; cold-boot the sandbox + otherwise. false → always cold-boot, even if a memory snapshot exists. nil → use + the server default. + + Applies to this request only; a later stop+start of the same sandbox reverts to + the server default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/sandboxes/boxes", + body=await async_maybe_transform( + { + "delete_after_stop_seconds": delete_after_stop_seconds, + "env_vars": env_vars, + "fs_capacity_bytes": fs_capacity_bytes, + "idle_ttl_seconds": idle_ttl_seconds, + "mem_bytes": mem_bytes, + "name": name, + "proxy_config": proxy_config, + "restore_memory": restore_memory, + "snapshot_id": snapshot_id, + "snapshot_name": snapshot_name, + "tag_value_ids": tag_value_ids, + "vcpus": vcpus, + }, + box_create_params.BoxCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxCreateResponse, + ) + + async def retrieve( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxRetrieveResponse: + """Retrieve a sandbox by name. + + Stale provisioning sandboxes are auto-failed. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return await self._get( + path_template("/v2/sandboxes/boxes/{name}", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxRetrieveResponse, + ) + + async def update( + self, + path_name: str, + *, + delete_after_stop_seconds: int | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + idle_ttl_seconds: int | Omit = omit, + mem_bytes: int | Omit = omit, + body_name: str | Omit = omit, + proxy_config: box_update_params.ProxyConfig | Omit = omit, + tag_value_ids: SequenceNotStr[str] | Omit = omit, + vcpus: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxUpdateResponse: + """Update a sandbox's display name. + + The name must be unique within the tenant. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_name: + raise ValueError(f"Expected a non-empty value for `path_name` but received {path_name!r}") + return await self._patch( + path_template("/v2/sandboxes/boxes/{path_name}", path_name=path_name), + body=await async_maybe_transform( + { + "delete_after_stop_seconds": delete_after_stop_seconds, + "fs_capacity_bytes": fs_capacity_bytes, + "idle_ttl_seconds": idle_ttl_seconds, + "mem_bytes": mem_bytes, + "body_name": body_name, + "proxy_config": proxy_config, + "tag_value_ids": tag_value_ids, + "vcpus": vcpus, + }, + box_update_params.BoxUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxUpdateResponse, + ) + + async def list( + self, + *, + created_by: str | Omit = omit, + limit: int | Omit = omit, + name_contains: str | Omit = omit, + offset: int | Omit = omit, + sort_by: str | Omit = omit, + sort_direction: str | Omit = omit, + status: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxListResponse: + """ + List sandboxes for the authenticated tenant, with optional filtering, sorting, + and pagination. + + Args: + created_by: Filter by creator identity. Only 'me' is supported. + + limit: Maximum number of results + + name_contains: Filter by name substring + + offset: Pagination offset + + sort_by: Sort column (name, status, created_at) + + sort_direction: Sort direction (asc, desc) + + status: Filter by status (provisioning, ready, failed, stopped, deleting) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/sandboxes/boxes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "created_by": created_by, + "limit": limit, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_direction": sort_direction, + "status": status, + }, + box_list_params.BoxListParams, + ), + ), + cast_to=BoxListResponse, + ) + + async def delete( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Delete a sandbox by name or UUID. + + Tears down the sandbox runtime and removes the + DB record. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/v2/sandboxes/boxes/{name}", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def create_snapshot( + self, + path_name: str, + *, + body_name: str, + checkpoint: str | Omit = omit, + docker_image: str | Omit = omit, + fs_capacity_bytes: int | Omit = omit, + include_memory: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxCreateSnapshotResponse: + """ + Create a snapshot by capturing the current state of a sandbox or promoting an + existing checkpoint. + + Args: + checkpoint: if omitted, creates a fresh checkpoint from the running VM + + docker_image: sandbox-local Docker image to export + + fs_capacity_bytes: required for Docker image export unless the sandbox has a capacity + + include_memory: IncludeMemory, when true, captures a full VM memory snapshot alongside the + filesystem clone. Only honored when the sandbox is running AND Checkpoint is + omitted (i.e. a fresh in-VM checkpoint is requested). Defaults to false to keep + snapshots small unless memory restore is explicitly desired. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not path_name: + raise ValueError(f"Expected a non-empty value for `path_name` but received {path_name!r}") + return await self._post( + path_template("/v2/sandboxes/boxes/{path_name}/snapshot", path_name=path_name), + body=await async_maybe_transform( + { + "body_name": body_name, + "checkpoint": checkpoint, + "docker_image": docker_image, + "fs_capacity_bytes": fs_capacity_bytes, + "include_memory": include_memory, + }, + box_create_snapshot_params.BoxCreateSnapshotParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxCreateSnapshotResponse, + ) + + async def generate_service_url( + self, + name: str, + *, + expires_in_seconds: int | Omit = omit, + port: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxGenerateServiceURLResponse: + """ + Create a short-lived JWT for accessing an HTTP service running on a specific + port inside a sandbox. Returns a browser_url (sets auth cookie via redirect), a + service_url (for use with the X-Langsmith-Sandbox-Service-Token header), the raw + token, and its expiry. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return await self._post( + path_template("/v2/sandboxes/boxes/{name}/service-url", name=name), + body=await async_maybe_transform( + { + "expires_in_seconds": expires_in_seconds, + "port": port, + }, + box_generate_service_url_params.BoxGenerateServiceURLParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxGenerateServiceURLResponse, + ) + + async def get_status( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxGetStatusResponse: + """ + Retrieve the lightweight status of a sandbox for polling. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return await self._get( + path_template("/v2/sandboxes/boxes/{name}/status", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxGetStatusResponse, + ) + + async def start( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BoxStartResponse: + """Start a stopped or failed sandbox. + + This endpoint is not idempotent. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + return await self._post( + path_template("/v2/sandboxes/boxes/{name}/start", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BoxStartResponse, + ) + + async def stop( + self, + name: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Stop a ready sandbox. + + This endpoint is not idempotent; the filesystem is + preserved for later restart. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not name: + raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + path_template("/v2/sandboxes/boxes/{name}/stop", name=name), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class BoxesResourceWithRawResponse: + def __init__(self, boxes: BoxesResource) -> None: + self._boxes = boxes + + self.create = to_raw_response_wrapper( + boxes.create, + ) + self.retrieve = to_raw_response_wrapper( + boxes.retrieve, + ) + self.update = to_raw_response_wrapper( + boxes.update, + ) + self.list = to_raw_response_wrapper( + boxes.list, + ) + self.delete = to_raw_response_wrapper( + boxes.delete, + ) + self.create_snapshot = to_raw_response_wrapper( + boxes.create_snapshot, + ) + self.generate_service_url = to_raw_response_wrapper( + boxes.generate_service_url, + ) + self.get_status = to_raw_response_wrapper( + boxes.get_status, + ) + self.start = to_raw_response_wrapper( + boxes.start, + ) + self.stop = to_raw_response_wrapper( + boxes.stop, + ) + + +class AsyncBoxesResourceWithRawResponse: + def __init__(self, boxes: AsyncBoxesResource) -> None: + self._boxes = boxes + + self.create = async_to_raw_response_wrapper( + boxes.create, + ) + self.retrieve = async_to_raw_response_wrapper( + boxes.retrieve, + ) + self.update = async_to_raw_response_wrapper( + boxes.update, + ) + self.list = async_to_raw_response_wrapper( + boxes.list, + ) + self.delete = async_to_raw_response_wrapper( + boxes.delete, + ) + self.create_snapshot = async_to_raw_response_wrapper( + boxes.create_snapshot, + ) + self.generate_service_url = async_to_raw_response_wrapper( + boxes.generate_service_url, + ) + self.get_status = async_to_raw_response_wrapper( + boxes.get_status, + ) + self.start = async_to_raw_response_wrapper( + boxes.start, + ) + self.stop = async_to_raw_response_wrapper( + boxes.stop, + ) + + +class BoxesResourceWithStreamingResponse: + def __init__(self, boxes: BoxesResource) -> None: + self._boxes = boxes + + self.create = to_streamed_response_wrapper( + boxes.create, + ) + self.retrieve = to_streamed_response_wrapper( + boxes.retrieve, + ) + self.update = to_streamed_response_wrapper( + boxes.update, + ) + self.list = to_streamed_response_wrapper( + boxes.list, + ) + self.delete = to_streamed_response_wrapper( + boxes.delete, + ) + self.create_snapshot = to_streamed_response_wrapper( + boxes.create_snapshot, + ) + self.generate_service_url = to_streamed_response_wrapper( + boxes.generate_service_url, + ) + self.get_status = to_streamed_response_wrapper( + boxes.get_status, + ) + self.start = to_streamed_response_wrapper( + boxes.start, + ) + self.stop = to_streamed_response_wrapper( + boxes.stop, + ) + + +class AsyncBoxesResourceWithStreamingResponse: + def __init__(self, boxes: AsyncBoxesResource) -> None: + self._boxes = boxes + + self.create = async_to_streamed_response_wrapper( + boxes.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + boxes.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + boxes.update, + ) + self.list = async_to_streamed_response_wrapper( + boxes.list, + ) + self.delete = async_to_streamed_response_wrapper( + boxes.delete, + ) + self.create_snapshot = async_to_streamed_response_wrapper( + boxes.create_snapshot, + ) + self.generate_service_url = async_to_streamed_response_wrapper( + boxes.generate_service_url, + ) + self.get_status = async_to_streamed_response_wrapper( + boxes.get_status, + ) + self.start = async_to_streamed_response_wrapper( + boxes.start, + ) + self.stop = async_to_streamed_response_wrapper( + boxes.stop, + ) diff --git a/python/langsmith_api/resources/sandboxes/sandboxes.py b/python/langsmith_api/resources/sandboxes/sandboxes.py new file mode 100644 index 000000000..b5907c739 --- /dev/null +++ b/python/langsmith_api/resources/sandboxes/sandboxes.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .boxes import ( + BoxesResource, + AsyncBoxesResource, + BoxesResourceWithRawResponse, + AsyncBoxesResourceWithRawResponse, + BoxesResourceWithStreamingResponse, + AsyncBoxesResourceWithStreamingResponse, +) +from ..._compat import cached_property +from .snapshots import ( + SnapshotsResource, + AsyncSnapshotsResource, + SnapshotsResourceWithRawResponse, + AsyncSnapshotsResourceWithRawResponse, + SnapshotsResourceWithStreamingResponse, + AsyncSnapshotsResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["SandboxesResource", "AsyncSandboxesResource"] + + +class SandboxesResource(SyncAPIResource): + @cached_property + def boxes(self) -> BoxesResource: + return BoxesResource(self._client) + + @cached_property + def snapshots(self) -> SnapshotsResource: + return SnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> SandboxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return SandboxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SandboxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return SandboxesResourceWithStreamingResponse(self) + + +class AsyncSandboxesResource(AsyncAPIResource): + @cached_property + def boxes(self) -> AsyncBoxesResource: + return AsyncBoxesResource(self._client) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResource: + return AsyncSnapshotsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSandboxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncSandboxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSandboxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncSandboxesResourceWithStreamingResponse(self) + + +class SandboxesResourceWithRawResponse: + def __init__(self, sandboxes: SandboxesResource) -> None: + self._sandboxes = sandboxes + + @cached_property + def boxes(self) -> BoxesResourceWithRawResponse: + return BoxesResourceWithRawResponse(self._sandboxes.boxes) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithRawResponse: + return SnapshotsResourceWithRawResponse(self._sandboxes.snapshots) + + +class AsyncSandboxesResourceWithRawResponse: + def __init__(self, sandboxes: AsyncSandboxesResource) -> None: + self._sandboxes = sandboxes + + @cached_property + def boxes(self) -> AsyncBoxesResourceWithRawResponse: + return AsyncBoxesResourceWithRawResponse(self._sandboxes.boxes) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithRawResponse: + return AsyncSnapshotsResourceWithRawResponse(self._sandboxes.snapshots) + + +class SandboxesResourceWithStreamingResponse: + def __init__(self, sandboxes: SandboxesResource) -> None: + self._sandboxes = sandboxes + + @cached_property + def boxes(self) -> BoxesResourceWithStreamingResponse: + return BoxesResourceWithStreamingResponse(self._sandboxes.boxes) + + @cached_property + def snapshots(self) -> SnapshotsResourceWithStreamingResponse: + return SnapshotsResourceWithStreamingResponse(self._sandboxes.snapshots) + + +class AsyncSandboxesResourceWithStreamingResponse: + def __init__(self, sandboxes: AsyncSandboxesResource) -> None: + self._sandboxes = sandboxes + + @cached_property + def boxes(self) -> AsyncBoxesResourceWithStreamingResponse: + return AsyncBoxesResourceWithStreamingResponse(self._sandboxes.boxes) + + @cached_property + def snapshots(self) -> AsyncSnapshotsResourceWithStreamingResponse: + return AsyncSnapshotsResourceWithStreamingResponse(self._sandboxes.snapshots) diff --git a/python/langsmith_api/resources/sandboxes/snapshots.py b/python/langsmith_api/resources/sandboxes/snapshots.py new file mode 100644 index 000000000..1e729b402 --- /dev/null +++ b/python/langsmith_api/resources/sandboxes/snapshots.py @@ -0,0 +1,493 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.sandboxes import snapshot_list_params, snapshot_create_params +from ...types.sandboxes.snapshot_list_response import SnapshotListResponse +from ...types.sandboxes.snapshot_create_response import SnapshotCreateResponse +from ...types.sandboxes.snapshot_retrieve_response import SnapshotRetrieveResponse + +__all__ = ["SnapshotsResource", "AsyncSnapshotsResource"] + + +class SnapshotsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return SnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return SnapshotsResourceWithStreamingResponse(self) + + def create( + self, + *, + docker_image: str, + fs_capacity_bytes: int, + name: str, + registry_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotCreateResponse: + """ + Create a snapshot from a Docker image (async build). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v2/sandboxes/snapshots", + body=maybe_transform( + { + "docker_image": docker_image, + "fs_capacity_bytes": fs_capacity_bytes, + "name": name, + "registry_id": registry_id, + }, + snapshot_create_params.SnapshotCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotCreateResponse, + ) + + def retrieve( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + Get a sandbox snapshot by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + return self._get( + path_template("/v2/sandboxes/snapshots/{snapshot_id}", snapshot_id=snapshot_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + def list( + self, + *, + created_by: str | Omit = omit, + limit: int | Omit = omit, + name_contains: str | Omit = omit, + offset: int | Omit = omit, + sort_by: str | Omit = omit, + sort_direction: str | Omit = omit, + status: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + List sandbox snapshots for the authenticated tenant, with optional filtering, + sorting, and pagination. + + Args: + created_by: Filter by creator identity. Only 'me' is supported. + + limit: Maximum number of results + + name_contains: Filter by name substring + + offset: Pagination offset + + sort_by: Sort column (name, status, created_at) + + sort_direction: Sort direction (asc, desc) + + status: Filter by status (building, ready, failed, deleting) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v2/sandboxes/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "created_by": created_by, + "limit": limit, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_direction": sort_direction, + "status": status, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + def delete( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Delete a snapshot by ID. + + The underlying storage is reclaimed asynchronously. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + path_template("/v2/sandboxes/snapshots/{snapshot_id}", snapshot_id=snapshot_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncSnapshotsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSnapshotsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncSnapshotsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSnapshotsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncSnapshotsResourceWithStreamingResponse(self) + + async def create( + self, + *, + docker_image: str, + fs_capacity_bytes: int, + name: str, + registry_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotCreateResponse: + """ + Create a snapshot from a Docker image (async build). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v2/sandboxes/snapshots", + body=await async_maybe_transform( + { + "docker_image": docker_image, + "fs_capacity_bytes": fs_capacity_bytes, + "name": name, + "registry_id": registry_id, + }, + snapshot_create_params.SnapshotCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotCreateResponse, + ) + + async def retrieve( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotRetrieveResponse: + """ + Get a sandbox snapshot by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + return await self._get( + path_template("/v2/sandboxes/snapshots/{snapshot_id}", snapshot_id=snapshot_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SnapshotRetrieveResponse, + ) + + async def list( + self, + *, + created_by: str | Omit = omit, + limit: int | Omit = omit, + name_contains: str | Omit = omit, + offset: int | Omit = omit, + sort_by: str | Omit = omit, + sort_direction: str | Omit = omit, + status: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SnapshotListResponse: + """ + List sandbox snapshots for the authenticated tenant, with optional filtering, + sorting, and pagination. + + Args: + created_by: Filter by creator identity. Only 'me' is supported. + + limit: Maximum number of results + + name_contains: Filter by name substring + + offset: Pagination offset + + sort_by: Sort column (name, status, created_at) + + sort_direction: Sort direction (asc, desc) + + status: Filter by status (building, ready, failed, deleting) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v2/sandboxes/snapshots", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "created_by": created_by, + "limit": limit, + "name_contains": name_contains, + "offset": offset, + "sort_by": sort_by, + "sort_direction": sort_direction, + "status": status, + }, + snapshot_list_params.SnapshotListParams, + ), + ), + cast_to=SnapshotListResponse, + ) + + async def delete( + self, + snapshot_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """Delete a snapshot by ID. + + The underlying storage is reclaimed asynchronously. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not snapshot_id: + raise ValueError(f"Expected a non-empty value for `snapshot_id` but received {snapshot_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + path_template("/v2/sandboxes/snapshots/{snapshot_id}", snapshot_id=snapshot_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class SnapshotsResourceWithRawResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = to_raw_response_wrapper( + snapshots.create, + ) + self.retrieve = to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = to_raw_response_wrapper( + snapshots.list, + ) + self.delete = to_raw_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithRawResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = async_to_raw_response_wrapper( + snapshots.create, + ) + self.retrieve = async_to_raw_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_raw_response_wrapper( + snapshots.list, + ) + self.delete = async_to_raw_response_wrapper( + snapshots.delete, + ) + + +class SnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: SnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = to_streamed_response_wrapper( + snapshots.create, + ) + self.retrieve = to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = to_streamed_response_wrapper( + snapshots.delete, + ) + + +class AsyncSnapshotsResourceWithStreamingResponse: + def __init__(self, snapshots: AsyncSnapshotsResource) -> None: + self._snapshots = snapshots + + self.create = async_to_streamed_response_wrapper( + snapshots.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + snapshots.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + snapshots.list, + ) + self.delete = async_to_streamed_response_wrapper( + snapshots.delete, + ) diff --git a/python/langsmith_api/resources/sessions/__init__.py b/python/langsmith_api/resources/sessions/__init__.py new file mode 100644 index 000000000..164d36a3f --- /dev/null +++ b/python/langsmith_api/resources/sessions/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .insights import ( + InsightsResource, + AsyncInsightsResource, + InsightsResourceWithRawResponse, + AsyncInsightsResourceWithRawResponse, + InsightsResourceWithStreamingResponse, + AsyncInsightsResourceWithStreamingResponse, +) +from .sessions import ( + SessionsResource, + AsyncSessionsResource, + SessionsResourceWithRawResponse, + AsyncSessionsResourceWithRawResponse, + SessionsResourceWithStreamingResponse, + AsyncSessionsResourceWithStreamingResponse, +) + +__all__ = [ + "InsightsResource", + "AsyncInsightsResource", + "InsightsResourceWithRawResponse", + "AsyncInsightsResourceWithRawResponse", + "InsightsResourceWithStreamingResponse", + "AsyncInsightsResourceWithStreamingResponse", + "SessionsResource", + "AsyncSessionsResource", + "SessionsResourceWithRawResponse", + "AsyncSessionsResourceWithRawResponse", + "SessionsResourceWithStreamingResponse", + "AsyncSessionsResourceWithStreamingResponse", +] diff --git a/python/langsmith_api/resources/sessions/insights.py b/python/langsmith_api/resources/sessions/insights.py new file mode 100644 index 000000000..29d9bb42f --- /dev/null +++ b/python/langsmith_api/resources/sessions/insights.py @@ -0,0 +1,746 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationInsightsClusteringJobs, AsyncOffsetPaginationInsightsClusteringJobs +from ..._base_client import AsyncPaginator, make_request_options +from ...types.sessions import ( + insight_list_params, + insight_create_params, + insight_update_params, + insight_retrieve_runs_params, +) +from ...types.sessions.insight_list_response import InsightListResponse +from ...types.sessions.insight_create_response import InsightCreateResponse +from ...types.sessions.insight_delete_response import InsightDeleteResponse +from ...types.sessions.insight_update_response import InsightUpdateResponse +from ...types.sessions.insight_retrieve_job_response import InsightRetrieveJobResponse +from ...types.sessions.insight_retrieve_runs_response import InsightRetrieveRunsResponse + +__all__ = ["InsightsResource", "AsyncInsightsResource"] + + +class InsightsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InsightsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return InsightsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InsightsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return InsightsResourceWithStreamingResponse(self) + + def create( + self, + session_id: str, + *, + attribute_schemas: Optional[Dict[str, object]] | Omit = omit, + cluster_model: Optional[str] | Omit = omit, + config_id: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + filter: Optional[str] | Omit = omit, + hierarchy: Optional[Iterable[int]] | Omit = omit, + is_scheduled: bool | Omit = omit, + last_n_hours: Optional[int] | Omit = omit, + model: Literal["openai", "anthropic"] | Omit = omit, + name: Optional[str] | Omit = omit, + partitions: Optional[Dict[str, str]] | Omit = omit, + sample: Optional[float] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + summary_model: Optional[str] | Omit = omit, + summary_prompt: Optional[str] | Omit = omit, + user_context: Optional[Dict[str, str]] | Omit = omit, + validate_model_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightCreateResponse: + """ + Create an insights job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._post( + path_template("/api/v1/sessions/{session_id}/insights", session_id=session_id), + body=maybe_transform( + { + "attribute_schemas": attribute_schemas, + "cluster_model": cluster_model, + "config_id": config_id, + "end_time": end_time, + "filter": filter, + "hierarchy": hierarchy, + "is_scheduled": is_scheduled, + "last_n_hours": last_n_hours, + "model": model, + "name": name, + "partitions": partitions, + "sample": sample, + "start_time": start_time, + "summary_model": summary_model, + "summary_prompt": summary_prompt, + "user_context": user_context, + "validate_model_secrets": validate_model_secrets, + }, + insight_create_params.InsightCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightCreateResponse, + ) + + def update( + self, + job_id: str, + *, + session_id: str, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightUpdateResponse: + """ + Update a session cluster job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._patch( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + body=maybe_transform({"name": name}, insight_update_params.InsightUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightUpdateResponse, + ) + + def list( + self, + session_id: str, + *, + config_id: Optional[str] | Omit = omit, + legacy: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationInsightsClusteringJobs[InsightListResponse]: + """ + Get all clusters for a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._get_api_list( + path_template("/api/v1/sessions/{session_id}/insights", session_id=session_id), + page=SyncOffsetPaginationInsightsClusteringJobs[InsightListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "config_id": config_id, + "legacy": legacy, + "limit": limit, + "offset": offset, + }, + insight_list_params.InsightListParams, + ), + ), + model=InsightListResponse, + ) + + def delete( + self, + job_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightDeleteResponse: + """ + Delete a session cluster job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._delete( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightDeleteResponse, + ) + + def retrieve_job( + self, + job_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightRetrieveJobResponse: + """ + Get a specific cluster job for a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._get( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightRetrieveJobResponse, + ) + + def retrieve_runs( + self, + job_id: str, + *, + session_id: str, + attribute_sort_key: Optional[str] | Omit = omit, + attribute_sort_order: Optional[Literal["asc", "desc"]] | Omit = omit, + cluster_id: Optional[str] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightRetrieveRunsResponse: + """ + Get all runs for a cluster job, optionally filtered by cluster. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return self._get( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}/runs", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "attribute_sort_key": attribute_sort_key, + "attribute_sort_order": attribute_sort_order, + "cluster_id": cluster_id, + "limit": limit, + "offset": offset, + }, + insight_retrieve_runs_params.InsightRetrieveRunsParams, + ), + ), + cast_to=InsightRetrieveRunsResponse, + ) + + +class AsyncInsightsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInsightsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncInsightsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInsightsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncInsightsResourceWithStreamingResponse(self) + + async def create( + self, + session_id: str, + *, + attribute_schemas: Optional[Dict[str, object]] | Omit = omit, + cluster_model: Optional[str] | Omit = omit, + config_id: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + filter: Optional[str] | Omit = omit, + hierarchy: Optional[Iterable[int]] | Omit = omit, + is_scheduled: bool | Omit = omit, + last_n_hours: Optional[int] | Omit = omit, + model: Literal["openai", "anthropic"] | Omit = omit, + name: Optional[str] | Omit = omit, + partitions: Optional[Dict[str, str]] | Omit = omit, + sample: Optional[float] | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + summary_model: Optional[str] | Omit = omit, + summary_prompt: Optional[str] | Omit = omit, + user_context: Optional[Dict[str, str]] | Omit = omit, + validate_model_secrets: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightCreateResponse: + """ + Create an insights job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return await self._post( + path_template("/api/v1/sessions/{session_id}/insights", session_id=session_id), + body=await async_maybe_transform( + { + "attribute_schemas": attribute_schemas, + "cluster_model": cluster_model, + "config_id": config_id, + "end_time": end_time, + "filter": filter, + "hierarchy": hierarchy, + "is_scheduled": is_scheduled, + "last_n_hours": last_n_hours, + "model": model, + "name": name, + "partitions": partitions, + "sample": sample, + "start_time": start_time, + "summary_model": summary_model, + "summary_prompt": summary_prompt, + "user_context": user_context, + "validate_model_secrets": validate_model_secrets, + }, + insight_create_params.InsightCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightCreateResponse, + ) + + async def update( + self, + job_id: str, + *, + session_id: str, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightUpdateResponse: + """ + Update a session cluster job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._patch( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + body=await async_maybe_transform({"name": name}, insight_update_params.InsightUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightUpdateResponse, + ) + + def list( + self, + session_id: str, + *, + config_id: Optional[str] | Omit = omit, + legacy: Optional[bool] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[InsightListResponse, AsyncOffsetPaginationInsightsClusteringJobs[InsightListResponse]]: + """ + Get all clusters for a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._get_api_list( + path_template("/api/v1/sessions/{session_id}/insights", session_id=session_id), + page=AsyncOffsetPaginationInsightsClusteringJobs[InsightListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "config_id": config_id, + "legacy": legacy, + "limit": limit, + "offset": offset, + }, + insight_list_params.InsightListParams, + ), + ), + model=InsightListResponse, + ) + + async def delete( + self, + job_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightDeleteResponse: + """ + Delete a session cluster job. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._delete( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightDeleteResponse, + ) + + async def retrieve_job( + self, + job_id: str, + *, + session_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightRetrieveJobResponse: + """ + Get a specific cluster job for a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._get( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InsightRetrieveJobResponse, + ) + + async def retrieve_runs( + self, + job_id: str, + *, + session_id: str, + attribute_sort_key: Optional[str] | Omit = omit, + attribute_sort_order: Optional[Literal["asc", "desc"]] | Omit = omit, + cluster_id: Optional[str] | Omit = omit, + limit: int | Omit = omit, + offset: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InsightRetrieveRunsResponse: + """ + Get all runs for a cluster job, optionally filtered by cluster. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + if not job_id: + raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") + return await self._get( + path_template("/api/v1/sessions/{session_id}/insights/{job_id}/runs", session_id=session_id, job_id=job_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "attribute_sort_key": attribute_sort_key, + "attribute_sort_order": attribute_sort_order, + "cluster_id": cluster_id, + "limit": limit, + "offset": offset, + }, + insight_retrieve_runs_params.InsightRetrieveRunsParams, + ), + ), + cast_to=InsightRetrieveRunsResponse, + ) + + +class InsightsResourceWithRawResponse: + def __init__(self, insights: InsightsResource) -> None: + self._insights = insights + + self.create = to_raw_response_wrapper( + insights.create, + ) + self.update = to_raw_response_wrapper( + insights.update, + ) + self.list = to_raw_response_wrapper( + insights.list, + ) + self.delete = to_raw_response_wrapper( + insights.delete, + ) + self.retrieve_job = to_raw_response_wrapper( + insights.retrieve_job, + ) + self.retrieve_runs = to_raw_response_wrapper( + insights.retrieve_runs, + ) + + +class AsyncInsightsResourceWithRawResponse: + def __init__(self, insights: AsyncInsightsResource) -> None: + self._insights = insights + + self.create = async_to_raw_response_wrapper( + insights.create, + ) + self.update = async_to_raw_response_wrapper( + insights.update, + ) + self.list = async_to_raw_response_wrapper( + insights.list, + ) + self.delete = async_to_raw_response_wrapper( + insights.delete, + ) + self.retrieve_job = async_to_raw_response_wrapper( + insights.retrieve_job, + ) + self.retrieve_runs = async_to_raw_response_wrapper( + insights.retrieve_runs, + ) + + +class InsightsResourceWithStreamingResponse: + def __init__(self, insights: InsightsResource) -> None: + self._insights = insights + + self.create = to_streamed_response_wrapper( + insights.create, + ) + self.update = to_streamed_response_wrapper( + insights.update, + ) + self.list = to_streamed_response_wrapper( + insights.list, + ) + self.delete = to_streamed_response_wrapper( + insights.delete, + ) + self.retrieve_job = to_streamed_response_wrapper( + insights.retrieve_job, + ) + self.retrieve_runs = to_streamed_response_wrapper( + insights.retrieve_runs, + ) + + +class AsyncInsightsResourceWithStreamingResponse: + def __init__(self, insights: AsyncInsightsResource) -> None: + self._insights = insights + + self.create = async_to_streamed_response_wrapper( + insights.create, + ) + self.update = async_to_streamed_response_wrapper( + insights.update, + ) + self.list = async_to_streamed_response_wrapper( + insights.list, + ) + self.delete = async_to_streamed_response_wrapper( + insights.delete, + ) + self.retrieve_job = async_to_streamed_response_wrapper( + insights.retrieve_job, + ) + self.retrieve_runs = async_to_streamed_response_wrapper( + insights.retrieve_runs, + ) diff --git a/python/langsmith_api/resources/sessions/sessions.py b/python/langsmith_api/resources/sessions/sessions.py new file mode 100644 index 000000000..563e31e84 --- /dev/null +++ b/python/langsmith_api/resources/sessions/sessions.py @@ -0,0 +1,878 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ...types import ( + SessionSortableColumns, + session_list_params, + session_create_params, + session_update_params, + session_retrieve_params, + session_dashboard_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform +from .insights import ( + InsightsResource, + AsyncInsightsResource, + InsightsResourceWithRawResponse, + AsyncInsightsResourceWithRawResponse, + InsightsResourceWithStreamingResponse, + AsyncInsightsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncOffsetPaginationTopLevelArray, AsyncOffsetPaginationTopLevelArray +from ..._base_client import AsyncPaginator, make_request_options +from ...types.tracer_session import TracerSession +from ...types.custom_charts_section import CustomChartsSection +from ...types.timedelta_input_param import TimedeltaInputParam +from ...types.run_stats_group_by_param import RunStatsGroupByParam +from ...types.session_sortable_columns import SessionSortableColumns +from ...types.tracer_session_without_virtual_fields import TracerSessionWithoutVirtualFields + +__all__ = ["SessionsResource", "AsyncSessionsResource"] + + +class SessionsResource(SyncAPIResource): + @cached_property + def insights(self) -> InsightsResource: + return InsightsResource(self._client) + + @cached_property + def with_raw_response(self) -> SessionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return SessionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return SessionsResourceWithStreamingResponse(self) + + def create( + self, + *, + upsert: bool | Omit = omit, + id: Optional[str] | Omit = omit, + default_dataset_id: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + evaluator_keys: Optional[SequenceNotStr[str]] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + kicked_off_by: Optional[str] | Omit = omit, + name: str | Omit = omit, + num_examples: Optional[int] | Omit = omit, + num_repetitions: Optional[int] | Omit = omit, + reference_dataset_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime] | Omit = omit, + trace_tier: Optional[Literal["longlived", "shortlived"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSessionWithoutVirtualFields: + """ + Create a new session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/sessions", + body=maybe_transform( + { + "id": id, + "default_dataset_id": default_dataset_id, + "description": description, + "end_time": end_time, + "evaluator_keys": evaluator_keys, + "extra": extra, + "kicked_off_by": kicked_off_by, + "name": name, + "num_examples": num_examples, + "num_repetitions": num_repetitions, + "reference_dataset_id": reference_dataset_id, + "start_time": start_time, + "trace_tier": trace_tier, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"upsert": upsert}, session_create_params.SessionCreateParams), + ), + cast_to=TracerSessionWithoutVirtualFields, + ) + + def retrieve( + self, + session_id: str, + *, + include_stats: bool | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSession: + """ + Get a specific session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._get( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include_stats": include_stats, + "stats_start_time": stats_start_time, + }, + session_retrieve_params.SessionRetrieveParams, + ), + ), + cast_to=TracerSession, + ) + + def update( + self, + session_id: str, + *, + default_dataset_id: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + name: Optional[str] | Omit = omit, + trace_tier: Optional[Literal["longlived", "shortlived"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSessionWithoutVirtualFields: + """ + Update a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._patch( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + body=maybe_transform( + { + "default_dataset_id": default_dataset_id, + "description": description, + "end_time": end_time, + "extra": extra, + "name": name, + "trace_tier": trace_tier, + }, + session_update_params.SessionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TracerSessionWithoutVirtualFields, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_version: Optional[str] | Omit = omit, + facets: bool | Omit = omit, + filter: Optional[str] | Omit = omit, + include_stats: bool | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + reference_dataset: Optional[SequenceNotStr[str]] | Omit = omit, + reference_free: Optional[bool] | Omit = omit, + sort_by: SessionSortableColumns | Omit = omit, + sort_by_desc: bool | Omit = omit, + sort_by_feedback_key: Optional[str] | Omit = omit, + sort_by_feedback_source: Optional[Literal["session", "run"]] | Omit = omit, + stats_filter: Optional[str] | Omit = omit, + stats_select: Optional[SequenceNotStr[str]] | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + use_approx_stats: bool | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncOffsetPaginationTopLevelArray[TracerSession]: + """ + Get all sessions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._get_api_list( + "/api/v1/sessions", + page=SyncOffsetPaginationTopLevelArray[TracerSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "dataset_version": dataset_version, + "facets": facets, + "filter": filter, + "include_stats": include_stats, + "limit": limit, + "metadata": metadata, + "name": name, + "name_contains": name_contains, + "offset": offset, + "reference_dataset": reference_dataset, + "reference_free": reference_free, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "sort_by_feedback_key": sort_by_feedback_key, + "sort_by_feedback_source": sort_by_feedback_source, + "stats_filter": stats_filter, + "stats_select": stats_select, + "stats_start_time": stats_start_time, + "tag_value_id": tag_value_id, + "use_approx_stats": use_approx_stats, + }, + session_list_params.SessionListParams, + ), + ), + model=TracerSession, + ) + + def delete( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return self._delete( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def dashboard( + self, + session_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + group_by: Optional[RunStatsGroupByParam] | Omit = omit, + omit_data: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + stride: TimedeltaInputParam | Omit = omit, + timezone: str | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CustomChartsSection: + """ + Get a prebuilt dashboard for a tracing project. + + Args: + group_by: Group by param for run stats. + + stride: Timedelta input. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._post( + path_template("/api/v1/sessions/{session_id}/dashboard", session_id=session_id), + body=maybe_transform( + { + "end_time": end_time, + "group_by": group_by, + "omit_data": omit_data, + "start_time": start_time, + "stride": stride, + "timezone": timezone, + }, + session_dashboard_params.SessionDashboardParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CustomChartsSection, + ) + + +class AsyncSessionsResource(AsyncAPIResource): + @cached_property + def insights(self) -> AsyncInsightsResource: + return AsyncInsightsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSessionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncSessionsResourceWithStreamingResponse(self) + + async def create( + self, + *, + upsert: bool | Omit = omit, + id: Optional[str] | Omit = omit, + default_dataset_id: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + evaluator_keys: Optional[SequenceNotStr[str]] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + kicked_off_by: Optional[str] | Omit = omit, + name: str | Omit = omit, + num_examples: Optional[int] | Omit = omit, + num_repetitions: Optional[int] | Omit = omit, + reference_dataset_id: Optional[str] | Omit = omit, + start_time: Union[str, datetime] | Omit = omit, + trace_tier: Optional[Literal["longlived", "shortlived"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSessionWithoutVirtualFields: + """ + Create a new session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/sessions", + body=await async_maybe_transform( + { + "id": id, + "default_dataset_id": default_dataset_id, + "description": description, + "end_time": end_time, + "evaluator_keys": evaluator_keys, + "extra": extra, + "kicked_off_by": kicked_off_by, + "name": name, + "num_examples": num_examples, + "num_repetitions": num_repetitions, + "reference_dataset_id": reference_dataset_id, + "start_time": start_time, + "trace_tier": trace_tier, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"upsert": upsert}, session_create_params.SessionCreateParams), + ), + cast_to=TracerSessionWithoutVirtualFields, + ) + + async def retrieve( + self, + session_id: str, + *, + include_stats: bool | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSession: + """ + Get a specific session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return await self._get( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "include_stats": include_stats, + "stats_start_time": stats_start_time, + }, + session_retrieve_params.SessionRetrieveParams, + ), + ), + cast_to=TracerSession, + ) + + async def update( + self, + session_id: str, + *, + default_dataset_id: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + end_time: Union[str, datetime, None] | Omit = omit, + extra: Optional[Dict[str, object]] | Omit = omit, + name: Optional[str] | Omit = omit, + trace_tier: Optional[Literal["longlived", "shortlived"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TracerSessionWithoutVirtualFields: + """ + Update a session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return await self._patch( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + body=await async_maybe_transform( + { + "default_dataset_id": default_dataset_id, + "description": description, + "end_time": end_time, + "extra": extra, + "name": name, + "trace_tier": trace_tier, + }, + session_update_params.SessionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TracerSessionWithoutVirtualFields, + ) + + def list( + self, + *, + id: Optional[SequenceNotStr[str]] | Omit = omit, + dataset_version: Optional[str] | Omit = omit, + facets: bool | Omit = omit, + filter: Optional[str] | Omit = omit, + include_stats: bool | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, + name_contains: Optional[str] | Omit = omit, + offset: int | Omit = omit, + reference_dataset: Optional[SequenceNotStr[str]] | Omit = omit, + reference_free: Optional[bool] | Omit = omit, + sort_by: SessionSortableColumns | Omit = omit, + sort_by_desc: bool | Omit = omit, + sort_by_feedback_key: Optional[str] | Omit = omit, + sort_by_feedback_source: Optional[Literal["session", "run"]] | Omit = omit, + stats_filter: Optional[str] | Omit = omit, + stats_select: Optional[SequenceNotStr[str]] | Omit = omit, + stats_start_time: Union[str, datetime, None] | Omit = omit, + tag_value_id: Optional[SequenceNotStr[str]] | Omit = omit, + use_approx_stats: bool | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[TracerSession, AsyncOffsetPaginationTopLevelArray[TracerSession]]: + """ + Get all sessions. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return self._get_api_list( + "/api/v1/sessions", + page=AsyncOffsetPaginationTopLevelArray[TracerSession], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "id": id, + "dataset_version": dataset_version, + "facets": facets, + "filter": filter, + "include_stats": include_stats, + "limit": limit, + "metadata": metadata, + "name": name, + "name_contains": name_contains, + "offset": offset, + "reference_dataset": reference_dataset, + "reference_free": reference_free, + "sort_by": sort_by, + "sort_by_desc": sort_by_desc, + "sort_by_feedback_key": sort_by_feedback_key, + "sort_by_feedback_source": sort_by_feedback_source, + "stats_filter": stats_filter, + "stats_select": stats_select, + "stats_start_time": stats_start_time, + "tag_value_id": tag_value_id, + "use_approx_stats": use_approx_stats, + }, + session_list_params.SessionListParams, + ), + ), + model=TracerSession, + ) + + async def delete( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete a specific session. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + return await self._delete( + path_template("/api/v1/sessions/{session_id}", session_id=session_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def dashboard( + self, + session_id: str, + *, + end_time: Union[str, datetime, None] | Omit = omit, + group_by: Optional[RunStatsGroupByParam] | Omit = omit, + omit_data: bool | Omit = omit, + start_time: Union[str, datetime, None] | Omit = omit, + stride: TimedeltaInputParam | Omit = omit, + timezone: str | Omit = omit, + accept: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CustomChartsSection: + """ + Get a prebuilt dashboard for a tracing project. + + Args: + group_by: Group by param for run stats. + + stride: Timedelta input. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {**strip_not_given({"accept": accept}), **(extra_headers or {})} + return await self._post( + path_template("/api/v1/sessions/{session_id}/dashboard", session_id=session_id), + body=await async_maybe_transform( + { + "end_time": end_time, + "group_by": group_by, + "omit_data": omit_data, + "start_time": start_time, + "stride": stride, + "timezone": timezone, + }, + session_dashboard_params.SessionDashboardParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CustomChartsSection, + ) + + +class SessionsResourceWithRawResponse: + def __init__(self, sessions: SessionsResource) -> None: + self._sessions = sessions + + self.create = to_raw_response_wrapper( + sessions.create, + ) + self.retrieve = to_raw_response_wrapper( + sessions.retrieve, + ) + self.update = to_raw_response_wrapper( + sessions.update, + ) + self.list = to_raw_response_wrapper( + sessions.list, + ) + self.delete = to_raw_response_wrapper( + sessions.delete, + ) + self.dashboard = to_raw_response_wrapper( + sessions.dashboard, + ) + + @cached_property + def insights(self) -> InsightsResourceWithRawResponse: + return InsightsResourceWithRawResponse(self._sessions.insights) + + +class AsyncSessionsResourceWithRawResponse: + def __init__(self, sessions: AsyncSessionsResource) -> None: + self._sessions = sessions + + self.create = async_to_raw_response_wrapper( + sessions.create, + ) + self.retrieve = async_to_raw_response_wrapper( + sessions.retrieve, + ) + self.update = async_to_raw_response_wrapper( + sessions.update, + ) + self.list = async_to_raw_response_wrapper( + sessions.list, + ) + self.delete = async_to_raw_response_wrapper( + sessions.delete, + ) + self.dashboard = async_to_raw_response_wrapper( + sessions.dashboard, + ) + + @cached_property + def insights(self) -> AsyncInsightsResourceWithRawResponse: + return AsyncInsightsResourceWithRawResponse(self._sessions.insights) + + +class SessionsResourceWithStreamingResponse: + def __init__(self, sessions: SessionsResource) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + self.retrieve = to_streamed_response_wrapper( + sessions.retrieve, + ) + self.update = to_streamed_response_wrapper( + sessions.update, + ) + self.list = to_streamed_response_wrapper( + sessions.list, + ) + self.delete = to_streamed_response_wrapper( + sessions.delete, + ) + self.dashboard = to_streamed_response_wrapper( + sessions.dashboard, + ) + + @cached_property + def insights(self) -> InsightsResourceWithStreamingResponse: + return InsightsResourceWithStreamingResponse(self._sessions.insights) + + +class AsyncSessionsResourceWithStreamingResponse: + def __init__(self, sessions: AsyncSessionsResource) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + sessions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + sessions.update, + ) + self.list = async_to_streamed_response_wrapper( + sessions.list, + ) + self.delete = async_to_streamed_response_wrapper( + sessions.delete, + ) + self.dashboard = async_to_streamed_response_wrapper( + sessions.dashboard, + ) + + @cached_property + def insights(self) -> AsyncInsightsResourceWithStreamingResponse: + return AsyncInsightsResourceWithStreamingResponse(self._sessions.insights) diff --git a/python/langsmith_api/resources/settings.py b/python/langsmith_api/resources/settings.py new file mode 100644 index 000000000..69dd4cf9d --- /dev/null +++ b/python/langsmith_api/resources/settings.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.app_hub_crud_tenants_tenant import AppHubCrudTenantsTenant + +__all__ = ["SettingsResource", "AsyncSettingsResource"] + + +class SettingsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SettingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return SettingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SettingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return SettingsResourceWithStreamingResponse(self) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AppHubCrudTenantsTenant: + """Get settings.""" + return self._get( + "/api/v1/settings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AppHubCrudTenantsTenant, + ) + + +class AsyncSettingsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSettingsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncSettingsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSettingsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncSettingsResourceWithStreamingResponse(self) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AppHubCrudTenantsTenant: + """Get settings.""" + return await self._get( + "/api/v1/settings", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AppHubCrudTenantsTenant, + ) + + +class SettingsResourceWithRawResponse: + def __init__(self, settings: SettingsResource) -> None: + self._settings = settings + + self.list = to_raw_response_wrapper( + settings.list, + ) + + +class AsyncSettingsResourceWithRawResponse: + def __init__(self, settings: AsyncSettingsResource) -> None: + self._settings = settings + + self.list = async_to_raw_response_wrapper( + settings.list, + ) + + +class SettingsResourceWithStreamingResponse: + def __init__(self, settings: SettingsResource) -> None: + self._settings = settings + + self.list = to_streamed_response_wrapper( + settings.list, + ) + + +class AsyncSettingsResourceWithStreamingResponse: + def __init__(self, settings: AsyncSettingsResource) -> None: + self._settings = settings + + self.list = async_to_streamed_response_wrapper( + settings.list, + ) diff --git a/python/langsmith_api/resources/workspaces.py b/python/langsmith_api/resources/workspaces.py new file mode 100644 index 000000000..6b83dcd36 --- /dev/null +++ b/python/langsmith_api/resources/workspaces.py @@ -0,0 +1,447 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..types import workspace_list_params, workspace_create_params, workspace_update_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.workspace_list_response import WorkspaceListResponse +from ..types.workspace_create_response import WorkspaceCreateResponse +from ..types.workspace_update_response import WorkspaceUpdateResponse + +__all__ = ["WorkspacesResource", "AsyncWorkspacesResource"] + + +class WorkspacesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> WorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return WorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return WorkspacesResourceWithStreamingResponse(self) + + def create( + self, + *, + display_name: str, + id: str | Omit = omit, + tenant_handle: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceCreateResponse: + """ + Create a new workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/api/v1/workspaces", + body=maybe_transform( + { + "display_name": display_name, + "id": id, + "tenant_handle": tenant_handle, + }, + workspace_create_params.WorkspaceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceCreateResponse, + ) + + def update( + self, + workspace_id: str, + *, + display_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceUpdateResponse: + """ + Update a workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return self._patch( + path_template("/api/v1/workspaces/{workspace_id}", workspace_id=workspace_id), + body=maybe_transform({"display_name": display_name}, workspace_update_params.WorkspaceUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceUpdateResponse, + ) + + def list( + self, + *, + data_plane_id: Optional[str] | Omit = omit, + include_deleted: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """Get all workspaces visible to this auth in the current org. + + Does not create a + new workspace/org. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/api/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "data_plane_id": data_plane_id, + "include_deleted": include_deleted, + }, + workspace_list_params.WorkspaceListParams, + ), + ), + cast_to=WorkspaceListResponse, + ) + + def delete( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Workspace + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return self._delete( + path_template("/api/v1/workspaces/{workspace_id}", workspace_id=workspace_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncWorkspacesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncWorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#accessing-raw-response-data-eg-headers + """ + return AsyncWorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/langchain-python#with_streaming_response + """ + return AsyncWorkspacesResourceWithStreamingResponse(self) + + async def create( + self, + *, + display_name: str, + id: str | Omit = omit, + tenant_handle: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceCreateResponse: + """ + Create a new workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/api/v1/workspaces", + body=await async_maybe_transform( + { + "display_name": display_name, + "id": id, + "tenant_handle": tenant_handle, + }, + workspace_create_params.WorkspaceCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceCreateResponse, + ) + + async def update( + self, + workspace_id: str, + *, + display_name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceUpdateResponse: + """ + Update a workspace. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return await self._patch( + path_template("/api/v1/workspaces/{workspace_id}", workspace_id=workspace_id), + body=await async_maybe_transform( + {"display_name": display_name}, workspace_update_params.WorkspaceUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceUpdateResponse, + ) + + async def list( + self, + *, + data_plane_id: Optional[str] | Omit = omit, + include_deleted: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """Get all workspaces visible to this auth in the current org. + + Does not create a + new workspace/org. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/api/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "data_plane_id": data_plane_id, + "include_deleted": include_deleted, + }, + workspace_list_params.WorkspaceListParams, + ), + ), + cast_to=WorkspaceListResponse, + ) + + async def delete( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Delete Workspace + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return await self._delete( + path_template("/api/v1/workspaces/{workspace_id}", workspace_id=workspace_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class WorkspacesResourceWithRawResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = to_raw_response_wrapper( + workspaces.create, + ) + self.update = to_raw_response_wrapper( + workspaces.update, + ) + self.list = to_raw_response_wrapper( + workspaces.list, + ) + self.delete = to_raw_response_wrapper( + workspaces.delete, + ) + + +class AsyncWorkspacesResourceWithRawResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = async_to_raw_response_wrapper( + workspaces.create, + ) + self.update = async_to_raw_response_wrapper( + workspaces.update, + ) + self.list = async_to_raw_response_wrapper( + workspaces.list, + ) + self.delete = async_to_raw_response_wrapper( + workspaces.delete, + ) + + +class WorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = to_streamed_response_wrapper( + workspaces.create, + ) + self.update = to_streamed_response_wrapper( + workspaces.update, + ) + self.list = to_streamed_response_wrapper( + workspaces.list, + ) + self.delete = to_streamed_response_wrapper( + workspaces.delete, + ) + + +class AsyncWorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.create = async_to_streamed_response_wrapper( + workspaces.create, + ) + self.update = async_to_streamed_response_wrapper( + workspaces.update, + ) + self.list = async_to_streamed_response_wrapper( + workspaces.list, + ) + self.delete = async_to_streamed_response_wrapper( + workspaces.delete, + ) diff --git a/python/langsmith_api/types/__init__.py b/python/langsmith_api/types/__init__.py new file mode 100644 index 000000000..f06151828 --- /dev/null +++ b/python/langsmith_api/types/__init__.py @@ -0,0 +1,138 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .dataset import Dataset as Dataset +from .example import Example as Example +from .data_type import DataType as DataType +from .evaluator import Evaluator as Evaluator +from .run_param import RunParam as RunParam +from .run_schema import RunSchema as RunSchema +from .source_type import SourceType as SourceType +from .missing_param import MissingParam as MissingParam +from .run_type_enum import RunTypeEnum as RunTypeEnum +from .example_select import ExampleSelect as ExampleSelect +from .feedback_level import FeedbackLevel as FeedbackLevel +from .tracer_session import TracerSession as TracerSession +from .dataset_version import DatasetVersion as DatasetVersion +from .feedback_schema import FeedbackSchema as FeedbackSchema +from .repo_list_params import RepoListParams as RepoListParams +from .run_query_params import RunQueryParams as RunQueryParams +from .run_stats_params import RunStatsParams as RunStatsParams +from .evaluator_webhook import EvaluatorWebhook as EvaluatorWebhook +from .get_repo_response import GetRepoResponse as GetRepoResponse +from .repo_with_lookups import RepoWithLookups as RepoWithLookups +from .run_create_params import RunCreateParams as RunCreateParams +from .run_update_params import RunUpdateParams as RunUpdateParams +from .commit_list_params import CommitListParams as CommitListParams +from .info_list_response import InfoListResponse as InfoListResponse +from .repo_create_params import RepoCreateParams as RepoCreateParams +from .repo_update_params import RepoUpdateParams as RepoUpdateParams +from .run_query_response import RunQueryResponse as RunQueryResponse +from .run_stats_response import RunStatsResponse as RunStatsResponse +from .commit_with_lookups import CommitWithLookups as CommitWithLookups +from .dataset_list_params import DatasetListParams as DatasetListParams +from .evaluator_top_level import EvaluatorTopLevel as EvaluatorTopLevel +from .example_list_params import ExampleListParams as ExampleListParams +from .run_create_response import RunCreateResponse as RunCreateResponse +from .run_retrieve_params import RunRetrieveParams as RunRetrieveParams +from .run_update_response import RunUpdateResponse as RunUpdateResponse +from .session_list_params import SessionListParams as SessionListParams +from .commit_create_params import CommitCreateParams as CommitCreateParams +from .create_repo_response import CreateRepoResponse as CreateRepoResponse +from .dataset_clone_params import DatasetCloneParams as DatasetCloneParams +from .feedback_list_params import FeedbackListParams as FeedbackListParams +from .custom_charts_section import CustomChartsSection as CustomChartsSection +from .dataset_create_params import DatasetCreateParams as DatasetCreateParams +from .dataset_update_params import DatasetUpdateParams as DatasetUpdateParams +from .dataset_upload_params import DatasetUploadParams as DatasetUploadParams +from .evaluator_list_params import EvaluatorListParams as EvaluatorListParams +from .example_create_params import ExampleCreateParams as ExampleCreateParams +from .example_update_params import ExampleUpdateParams as ExampleUpdateParams +from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_update_params import SessionUpdateParams as SessionUpdateParams +from .timedelta_input_param import TimedeltaInputParam as TimedeltaInputParam +from .workspace_list_params import WorkspaceListParams as WorkspaceListParams +from .commit_create_response import CommitCreateResponse as CommitCreateResponse +from .commit_retrieve_params import CommitRetrieveParams as CommitRetrieveParams +from .dataset_clone_response import DatasetCloneResponse as DatasetCloneResponse +from .dataset_transformation import DatasetTransformation as DatasetTransformation +from .feedback_create_params import FeedbackCreateParams as FeedbackCreateParams +from .feedback_update_params import FeedbackUpdateParams as FeedbackUpdateParams +from .sort_by_dataset_column import SortByDatasetColumn as SortByDatasetColumn +from .annotation_queue_schema import AnnotationQueueSchema as AnnotationQueueSchema +from .dataset_update_response import DatasetUpdateResponse as DatasetUpdateResponse +from .evaluator_list_response import EvaluatorListResponse as EvaluatorListResponse +from .example_retrieve_params import ExampleRetrieveParams as ExampleRetrieveParams +from .run_ingest_batch_params import RunIngestBatchParams as RunIngestBatchParams +from .session_retrieve_params import SessionRetrieveParams as SessionRetrieveParams +from .workspace_create_params import WorkspaceCreateParams as WorkspaceCreateParams +from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse +from .workspace_update_params import WorkspaceUpdateParams as WorkspaceUpdateParams +from .code_evaluator_top_level import CodeEvaluatorTopLevel as CodeEvaluatorTopLevel +from .commit_manifest_response import CommitManifestResponse as CommitManifestResponse +from .commit_retrieve_response import CommitRetrieveResponse as CommitRetrieveResponse +from .feedback_retrieve_params import FeedbackRetrieveParams as FeedbackRetrieveParams +from .run_stats_group_by_param import RunStatsGroupByParam as RunStatsGroupByParam +from .session_dashboard_params import SessionDashboardParams as SessionDashboardParams +from .session_sortable_columns import SessionSortableColumns as SessionSortableColumns +from .api_feedback_source_param import APIFeedbackSourceParam as APIFeedbackSourceParam +from .app_feedback_source_param import AppFeedbackSourceParam as AppFeedbackSourceParam +from .evaluator_pagerduty_alert import EvaluatorPagerdutyAlert as EvaluatorPagerdutyAlert +from .example_delete_all_params import ExampleDeleteAllParams as ExampleDeleteAllParams +from .run_ingest_batch_response import RunIngestBatchResponse as RunIngestBatchResponse +from .workspace_create_response import WorkspaceCreateResponse as WorkspaceCreateResponse +from .workspace_update_response import WorkspaceUpdateResponse as WorkspaceUpdateResponse +from .dataset_update_tags_params import DatasetUpdateTagsParams as DatasetUpdateTagsParams +from .app_hub_crud_tenants_tenant import AppHubCrudTenantsTenant as AppHubCrudTenantsTenant +from .dataset_retrieve_csv_params import DatasetRetrieveCsvParams as DatasetRetrieveCsvParams +from .model_feedback_source_param import ModelFeedbackSourceParam as ModelFeedbackSourceParam +from .annotation_queue_size_schema import AnnotationQueueSizeSchema as AnnotationQueueSizeSchema +from .attachments_operations_param import AttachmentsOperationsParam as AttachmentsOperationsParam +from .dataset_transformation_param import DatasetTransformationParam as DatasetTransformationParam +from .dataset_retrieve_jsonl_params import DatasetRetrieveJSONLParams as DatasetRetrieveJSONLParams +from .example_retrieve_count_params import ExampleRetrieveCountParams as ExampleRetrieveCountParams +from .annotation_queue_export_params import AnnotationQueueExportParams as AnnotationQueueExportParams +from .annotation_queue_update_params import AnnotationQueueUpdateParams as AnnotationQueueUpdateParams +from .dataset_retrieve_openai_params import DatasetRetrieveOpenAIParams as DatasetRetrieveOpenAIParams +from .example_upload_from_csv_params import ExampleUploadFromCsvParams as ExampleUploadFromCsvParams +from .auto_eval_feedback_source_param import AutoEvalFeedbackSourceParam as AutoEvalFeedbackSourceParam +from .dataset_retrieve_version_params import DatasetRetrieveVersionParams as DatasetRetrieveVersionParams +from .e_prompt_optimization_algorithm import EPromptOptimizationAlgorithm as EPromptOptimizationAlgorithm +from .example_retrieve_count_response import ExampleRetrieveCountResponse as ExampleRetrieveCountResponse +from .annotation_queue_populate_params import AnnotationQueuePopulateParams as AnnotationQueuePopulateParams +from .example_upload_from_csv_response import ExampleUploadFromCsvResponse as ExampleUploadFromCsvResponse +from .public_retrieve_feedbacks_params import PublicRetrieveFeedbacksParams as PublicRetrieveFeedbacksParams +from .dataset_retrieve_openai_ft_params import DatasetRetrieveOpenAIFtParams as DatasetRetrieveOpenAIFtParams +from .runs_filter_data_source_type_enum import RunsFilterDataSourceTypeEnum as RunsFilterDataSourceTypeEnum +from .annotation_queue_retrieve_response import AnnotationQueueRetrieveResponse as AnnotationQueueRetrieveResponse +from .annotation_queue_rubric_item_schema import AnnotationQueueRubricItemSchema as AnnotationQueueRubricItemSchema +from .annotation_queue_retrieve_run_params import AnnotationQueueRetrieveRunParams as AnnotationQueueRetrieveRunParams +from .annotation_queue_retrieve_size_params import ( + AnnotationQueueRetrieveSizeParams as AnnotationQueueRetrieveSizeParams, +) +from .run_schema_with_annotation_queue_info import RunSchemaWithAnnotationQueueInfo as RunSchemaWithAnnotationQueueInfo +from .tracer_session_without_virtual_fields import ( + TracerSessionWithoutVirtualFields as TracerSessionWithoutVirtualFields, +) +from .annotation_queue_annotation_queues_params import ( + AnnotationQueueAnnotationQueuesParams as AnnotationQueueAnnotationQueuesParams, +) +from .annotation_queue_create_run_status_params import ( + AnnotationQueueCreateRunStatusParams as AnnotationQueueCreateRunStatusParams, +) +from .annotation_queue_retrieve_queues_response import ( + AnnotationQueueRetrieveQueuesResponse as AnnotationQueueRetrieveQueuesResponse, +) +from .annotation_queue_rubric_item_schema_param import ( + AnnotationQueueRubricItemSchemaParam as AnnotationQueueRubricItemSchemaParam, +) +from .annotation_queue_retrieve_total_archived_params import ( + AnnotationQueueRetrieveTotalArchivedParams as AnnotationQueueRetrieveTotalArchivedParams, +) +from .annotation_queue_retrieve_annotation_queues_params import ( + AnnotationQueueRetrieveAnnotationQueuesParams as AnnotationQueueRetrieveAnnotationQueuesParams, +) +from .annotation_queue_retrieve_annotation_queues_response import ( + AnnotationQueueRetrieveAnnotationQueuesResponse as AnnotationQueueRetrieveAnnotationQueuesResponse, +) diff --git a/python/langsmith_api/types/annotation_queue_annotation_queues_params.py b/python/langsmith_api/types/annotation_queue_annotation_queues_params.py new file mode 100644 index 000000000..c54398119 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_annotation_queues_params.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .annotation_queue_rubric_item_schema_param import AnnotationQueueRubricItemSchemaParam + +__all__ = ["AnnotationQueueAnnotationQueuesParams"] + + +class AnnotationQueueAnnotationQueuesParams(TypedDict, total=False): + name: Required[str] + + id: str + + created_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + default_dataset: Optional[str] + + description: Optional[str] + + enable_reservations: Optional[bool] + + metadata: Optional[Dict[str, object]] + + num_reviewers_per_item: Optional[int] + + reservation_minutes: Optional[int] + + reviewer_access_mode: str + + rubric_instructions: Optional[str] + + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] + + session_ids: Optional[SequenceNotStr[str]] + + updated_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] diff --git a/python/langsmith_api/types/annotation_queue_create_run_status_params.py b/python/langsmith_api/types/annotation_queue_create_run_status_params.py new file mode 100644 index 000000000..baa92df94 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_create_run_status_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["AnnotationQueueCreateRunStatusParams"] + + +class AnnotationQueueCreateRunStatusParams(TypedDict, total=False): + override_added_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + status: Optional[str] diff --git a/python/langsmith_api/types/annotation_queue_export_params.py b/python/langsmith_api/types/annotation_queue_export_params.py new file mode 100644 index 000000000..42c85e146 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_export_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["AnnotationQueueExportParams"] + + +class AnnotationQueueExportParams(TypedDict, total=False): + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + include_annotator_detail: bool + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] diff --git a/python/langsmith_api/types/annotation_queue_populate_params.py b/python/langsmith_api/types/annotation_queue_populate_params.py new file mode 100644 index 000000000..1735df0d5 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_populate_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["AnnotationQueuePopulateParams"] + + +class AnnotationQueuePopulateParams(TypedDict, total=False): + queue_id: Required[str] + + session_ids: Required[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_params.py b/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_params.py new file mode 100644 index 000000000..e7614cd93 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["AnnotationQueueRetrieveAnnotationQueuesParams"] + + +class AnnotationQueueRetrieveAnnotationQueuesParams(TypedDict, total=False): + assigned_to_me: bool + + dataset_id: Optional[str] + + ids: Optional[SequenceNotStr[str]] + + limit: int + + name: Optional[str] + + name_contains: Optional[str] + + offset: int + + queue_type: Optional[Literal["single", "pairwise"]] + + sort_by: Optional[str] + + sort_by_desc: bool + + tag_value_id: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_response.py b/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_response.py new file mode 100644 index 000000000..d10c0719b --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_annotation_queues_response.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["AnnotationQueueRetrieveAnnotationQueuesResponse", "AssignedReviewer"] + + +class AssignedReviewer(BaseModel): + """Identity info for an assigned reviewer on an annotation queue.""" + + id: str + + email: Optional[str] = None + + name: Optional[str] = None + + +class AnnotationQueueRetrieveAnnotationQueuesResponse(BaseModel): + """AnnotationQueue schema with size.""" + + id: str + + name: str + + queue_type: Literal["single", "pairwise"] + + tenant_id: str + + total_runs: int + + assigned_reviewers: Optional[List[AssignedReviewer]] = None + + created_at: Optional[datetime] = None + + default_dataset: Optional[str] = None + + description: Optional[str] = None + + enable_reservations: Optional[bool] = None + + metadata: Optional[Dict[str, object]] = None + + num_reviewers_per_item: Optional[int] = None + + reservation_minutes: Optional[int] = None + + reviewer_access_mode: Optional[str] = None + + run_rule_id: Optional[str] = None + + source_rule_id: Optional[str] = None + + updated_at: Optional[datetime] = None diff --git a/python/langsmith_api/types/annotation_queue_retrieve_queues_response.py b/python/langsmith_api/types/annotation_queue_retrieve_queues_response.py new file mode 100644 index 000000000..4cbfe37f3 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_queues_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .annotation_queue_schema import AnnotationQueueSchema + +__all__ = ["AnnotationQueueRetrieveQueuesResponse"] + +AnnotationQueueRetrieveQueuesResponse: TypeAlias = List[AnnotationQueueSchema] diff --git a/python/langsmith_api/types/annotation_queue_retrieve_response.py b/python/langsmith_api/types/annotation_queue_retrieve_response.py new file mode 100644 index 000000000..21c1bf68e --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_response.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .annotation_queue_rubric_item_schema import AnnotationQueueRubricItemSchema + +__all__ = ["AnnotationQueueRetrieveResponse", "AssignedReviewer"] + + +class AssignedReviewer(BaseModel): + """Identity info for an assigned reviewer on an annotation queue.""" + + id: str + + email: Optional[str] = None + + name: Optional[str] = None + + +class AnnotationQueueRetrieveResponse(BaseModel): + """AnnotationQueue schema with rubric.""" + + id: str + + name: str + + queue_type: Literal["single", "pairwise"] + + tenant_id: str + + assigned_reviewers: Optional[List[AssignedReviewer]] = None + + created_at: Optional[datetime] = None + + default_dataset: Optional[str] = None + + description: Optional[str] = None + + enable_reservations: Optional[bool] = None + + metadata: Optional[Dict[str, object]] = None + + num_reviewers_per_item: Optional[int] = None + + reservation_minutes: Optional[int] = None + + reviewer_access_mode: Optional[str] = None + + rubric_instructions: Optional[str] = None + + rubric_items: Optional[List[AnnotationQueueRubricItemSchema]] = None + + run_rule_id: Optional[str] = None + + source_rule_id: Optional[str] = None + + updated_at: Optional[datetime] = None diff --git a/python/langsmith_api/types/annotation_queue_retrieve_run_params.py b/python/langsmith_api/types/annotation_queue_retrieve_run_params.py new file mode 100644 index 000000000..77db06fdf --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_run_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AnnotationQueueRetrieveRunParams"] + + +class AnnotationQueueRetrieveRunParams(TypedDict, total=False): + queue_id: Required[str] + + include_extra: bool diff --git a/python/langsmith_api/types/annotation_queue_retrieve_size_params.py b/python/langsmith_api/types/annotation_queue_retrieve_size_params.py new file mode 100644 index 000000000..c54760e81 --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_size_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AnnotationQueueRetrieveSizeParams"] + + +class AnnotationQueueRetrieveSizeParams(TypedDict, total=False): + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] diff --git a/python/langsmith_api/types/annotation_queue_retrieve_total_archived_params.py b/python/langsmith_api/types/annotation_queue_retrieve_total_archived_params.py new file mode 100644 index 000000000..95ff2411b --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_retrieve_total_archived_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["AnnotationQueueRetrieveTotalArchivedParams"] + + +class AnnotationQueueRetrieveTotalArchivedParams(TypedDict, total=False): + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] diff --git a/python/langsmith_api/types/annotation_queue_rubric_item_schema.py b/python/langsmith_api/types/annotation_queue_rubric_item_schema.py new file mode 100644 index 000000000..7bb8a979b --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_rubric_item_schema.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from .._models import BaseModel + +__all__ = ["AnnotationQueueRubricItemSchema"] + + +class AnnotationQueueRubricItemSchema(BaseModel): + feedback_key: str + + description: Optional[str] = None + + is_assertion: Optional[bool] = None + + is_required: Optional[bool] = None + + score_descriptions: Optional[Dict[str, str]] = None + + value_descriptions: Optional[Dict[str, str]] = None diff --git a/python/langsmith_api/types/annotation_queue_rubric_item_schema_param.py b/python/langsmith_api/types/annotation_queue_rubric_item_schema_param.py new file mode 100644 index 000000000..cafe14d7e --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_rubric_item_schema_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Required, TypedDict + +__all__ = ["AnnotationQueueRubricItemSchemaParam"] + + +class AnnotationQueueRubricItemSchemaParam(TypedDict, total=False): + feedback_key: Required[str] + + description: Optional[str] + + is_assertion: Optional[bool] + + is_required: Optional[bool] + + score_descriptions: Optional[Dict[str, str]] + + value_descriptions: Optional[Dict[str, str]] diff --git a/python/langsmith_api/types/annotation_queue_schema.py b/python/langsmith_api/types/annotation_queue_schema.py new file mode 100644 index 000000000..4c40ac70f --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_schema.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["AnnotationQueueSchema", "AssignedReviewer"] + + +class AssignedReviewer(BaseModel): + """Identity info for an assigned reviewer on an annotation queue.""" + + id: str + + email: Optional[str] = None + + name: Optional[str] = None + + +class AnnotationQueueSchema(BaseModel): + """AnnotationQueue schema.""" + + id: str + + name: str + + queue_type: Literal["single", "pairwise"] + + tenant_id: str + + assigned_reviewers: Optional[List[AssignedReviewer]] = None + + created_at: Optional[datetime] = None + + default_dataset: Optional[str] = None + + description: Optional[str] = None + + enable_reservations: Optional[bool] = None + + metadata: Optional[Dict[str, object]] = None + + num_reviewers_per_item: Optional[int] = None + + reservation_minutes: Optional[int] = None + + reviewer_access_mode: Optional[str] = None + + run_rule_id: Optional[str] = None + + source_rule_id: Optional[str] = None + + updated_at: Optional[datetime] = None diff --git a/python/langsmith_api/types/annotation_queue_size_schema.py b/python/langsmith_api/types/annotation_queue_size_schema.py new file mode 100644 index 000000000..d1f68721c --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_size_schema.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["AnnotationQueueSizeSchema"] + + +class AnnotationQueueSizeSchema(BaseModel): + """Size of an Annotation Queue""" + + size: int diff --git a/python/langsmith_api/types/annotation_queue_update_params.py b/python/langsmith_api/types/annotation_queue_update_params.py new file mode 100644 index 000000000..77b7744fd --- /dev/null +++ b/python/langsmith_api/types/annotation_queue_update_params.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, TypeAlias, TypedDict + +from .missing_param import MissingParam +from .annotation_queue_rubric_item_schema_param import AnnotationQueueRubricItemSchemaParam + +__all__ = ["AnnotationQueueUpdateParams", "Metadata", "NumReviewersPerItem"] + + +class AnnotationQueueUpdateParams(TypedDict, total=False): + default_dataset: Optional[str] + + description: Optional[str] + + enable_reservations: bool + + metadata: Optional[Metadata] + + name: Optional[str] + + num_reviewers_per_item: Optional[NumReviewersPerItem] + + reservation_minutes: Optional[int] + + reviewer_access_mode: Optional[Literal["any", "assigned"]] + + rubric_instructions: Optional[str] + + rubric_items: Optional[Iterable[AnnotationQueueRubricItemSchemaParam]] + + +Metadata: TypeAlias = Union[Dict[str, object], MissingParam] + +NumReviewersPerItem: TypeAlias = Union[int, MissingParam] diff --git a/python/langsmith_api/types/annotation_queues/__init__.py b/python/langsmith_api/types/annotation_queues/__init__.py new file mode 100644 index 000000000..b0ddc1bd5 --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .run_list_params import RunListParams as RunListParams +from .run_create_params import RunCreateParams as RunCreateParams +from .run_list_response import RunListResponse as RunListResponse +from .run_update_params import RunUpdateParams as RunUpdateParams +from .run_create_response import RunCreateResponse as RunCreateResponse +from .run_delete_all_params import RunDeleteAllParams as RunDeleteAllParams diff --git a/python/langsmith_api/types/annotation_queues/run_create_params.py b/python/langsmith_api/types/annotation_queues/run_create_params.py new file mode 100644 index 000000000..25d372473 --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_create_params.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo + +__all__ = [ + "RunCreateParams", + "RunsUuidArray", + "RunsAnnotationQueueRunAddSchemaArray", + "RunsAnnotationQueueRunAddSchemaArrayBody", + "Variant2", + "Variant2Body", +] + + +class RunsUuidArray(TypedDict, total=False): + body: Required[SequenceNotStr[str]] + + +class RunsAnnotationQueueRunAddSchemaArray(TypedDict, total=False): + body: Required[Iterable[RunsAnnotationQueueRunAddSchemaArrayBody]] + + +class RunsAnnotationQueueRunAddSchemaArrayBody(TypedDict, total=False): + """ + Add a single run to AQ (CH path) with an optional back-pointer to the + issues-agent proposal that seeded this add. Use when bulk-adding runs + that come from different proposals — each row carries its own + source_proposed_example_id. For unrelated bulk adds, prefer plain + List[UUID] on the same endpoint. + """ + + run_id: Required[str] + + source_proposed_example_id: Optional[str] + + +class Variant2(TypedDict, total=False): + body: Required[Iterable[Variant2Body]] + + +class Variant2Body(TypedDict, total=False): + """Deprecated: use plain UUID list or AddRunToQueueByKeyRequest instead.""" + + run_id: Required[str] + + parent_run_id: Optional[str] + + session_id: Optional[str] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + trace_id: Optional[str] + + trace_tier: Optional[Literal["longlived", "shortlived"]] + + +RunCreateParams: TypeAlias = Union[RunsUuidArray, RunsAnnotationQueueRunAddSchemaArray, Variant2] diff --git a/python/langsmith_api/types/annotation_queues/run_create_response.py b/python/langsmith_api/types/annotation_queues/run_create_response.py new file mode 100644 index 000000000..63f79d121 --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_create_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from ..._models import BaseModel + +__all__ = ["RunCreateResponse", "RunCreateResponseItem"] + + +class RunCreateResponseItem(BaseModel): + id: str + + queue_id: str + + run_id: str + + added_at: Optional[datetime] = None + + last_reviewed_time: Optional[datetime] = None + + source_proposed_example_id: Optional[str] = None + + +RunCreateResponse: TypeAlias = List[RunCreateResponseItem] diff --git a/python/langsmith_api/types/annotation_queues/run_delete_all_params.py b/python/langsmith_api/types/annotation_queues/run_delete_all_params.py new file mode 100644 index 000000000..7d4557a99 --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_delete_all_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["RunDeleteAllParams"] + + +class RunDeleteAllParams(TypedDict, total=False): + delete_all: bool + + exclude_run_ids: Optional[SequenceNotStr[str]] + + run_ids: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/annotation_queues/run_list_params.py b/python/langsmith_api/types/annotation_queues/run_list_params.py new file mode 100644 index 000000000..82dd7d072 --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_list_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["RunListParams"] + + +class RunListParams(TypedDict, total=False): + archived: Optional[bool] + + include_stats: Optional[bool] + + limit: int + + offset: int + + status: Optional[Literal["needs_my_review", "needs_others_review", "completed"]] diff --git a/python/langsmith_api/types/annotation_queues/run_list_response.py b/python/langsmith_api/types/annotation_queues/run_list_response.py new file mode 100644 index 000000000..c1a7bcb2b --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from ..run_schema_with_annotation_queue_info import RunSchemaWithAnnotationQueueInfo + +__all__ = ["RunListResponse"] + +RunListResponse: TypeAlias = List[RunSchemaWithAnnotationQueueInfo] diff --git a/python/langsmith_api/types/annotation_queues/run_update_params.py b/python/langsmith_api/types/annotation_queues/run_update_params.py new file mode 100644 index 000000000..bec8be9fd --- /dev/null +++ b/python/langsmith_api/types/annotation_queues/run_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["RunUpdateParams"] + + +class RunUpdateParams(TypedDict, total=False): + queue_id: Required[str] + + added_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + last_reviewed_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] diff --git a/python/langsmith_api/types/api_feedback_source_param.py b/python/langsmith_api/types/api_feedback_source_param.py new file mode 100644 index 000000000..2bf4c2801 --- /dev/null +++ b/python/langsmith_api/types/api_feedback_source_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["APIFeedbackSourceParam"] + + +class APIFeedbackSourceParam(TypedDict, total=False): + """API feedback source.""" + + metadata: Optional[Dict[str, object]] + + type: Literal["api"] diff --git a/python/langsmith_api/types/app_feedback_source_param.py b/python/langsmith_api/types/app_feedback_source_param.py new file mode 100644 index 000000000..f463df6e4 --- /dev/null +++ b/python/langsmith_api/types/app_feedback_source_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AppFeedbackSourceParam"] + + +class AppFeedbackSourceParam(TypedDict, total=False): + """Feedback from the LangChainPlus App.""" + + metadata: Optional[Dict[str, object]] + + type: Literal["app"] diff --git a/python/langsmith_api/types/app_hub_crud_tenants_tenant.py b/python/langsmith_api/types/app_hub_crud_tenants_tenant.py new file mode 100644 index 000000000..bddfcb445 --- /dev/null +++ b/python/langsmith_api/types/app_hub_crud_tenants_tenant.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["AppHubCrudTenantsTenant"] + + +class AppHubCrudTenantsTenant(BaseModel): + id: str + + created_at: datetime + + display_name: str + + tenant_handle: Optional[str] = None diff --git a/python/langsmith_api/types/attachments_operations_param.py b/python/langsmith_api/types/attachments_operations_param.py new file mode 100644 index 000000000..9cbe57115 --- /dev/null +++ b/python/langsmith_api/types/attachments_operations_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypedDict + +from .._types import SequenceNotStr + +__all__ = ["AttachmentsOperationsParam"] + + +class AttachmentsOperationsParam(TypedDict, total=False): + rename: Dict[str, str] + """Mapping of old attachment names to new names""" + + retain: SequenceNotStr[str] + """List of attachment names to keep""" diff --git a/python/langsmith_api/types/auto_eval_feedback_source_param.py b/python/langsmith_api/types/auto_eval_feedback_source_param.py new file mode 100644 index 000000000..07d2173ef --- /dev/null +++ b/python/langsmith_api/types/auto_eval_feedback_source_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AutoEvalFeedbackSourceParam"] + + +class AutoEvalFeedbackSourceParam(TypedDict, total=False): + """Auto eval feedback source.""" + + metadata: Optional[Dict[str, object]] + + type: Literal["auto_eval"] diff --git a/python/langsmith_api/types/code_evaluator_top_level.py b/python/langsmith_api/types/code_evaluator_top_level.py new file mode 100644 index 000000000..dba1f39c9 --- /dev/null +++ b/python/langsmith_api/types/code_evaluator_top_level.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["CodeEvaluatorTopLevel"] + + +class CodeEvaluatorTopLevel(BaseModel): + code: str + + language: Optional[Literal["python", "javascript"]] = None diff --git a/python/langsmith_api/types/commit_create_params.py b/python/langsmith_api/types/commit_create_params.py new file mode 100644 index 000000000..68ddd8711 --- /dev/null +++ b/python/langsmith_api/types/commit_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CommitCreateParams"] + + +class CommitCreateParams(TypedDict, total=False): + owner: Required[str] + + description: str + + manifest: object + + parent_commit: str + + skip_webhooks: object + """SkipWebhooks allows skipping webhook notifications. + + Can be true (boolean) to skip all, or an array of webhook UUIDs to skip specific + ones. + """ diff --git a/python/langsmith_api/types/commit_create_response.py b/python/langsmith_api/types/commit_create_response.py new file mode 100644 index 000000000..429b90913 --- /dev/null +++ b/python/langsmith_api/types/commit_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel +from .commit_with_lookups import CommitWithLookups + +__all__ = ["CommitCreateResponse"] + + +class CommitCreateResponse(BaseModel): + commit: Optional[CommitWithLookups] = None diff --git a/python/langsmith_api/types/commit_list_params.py b/python/langsmith_api/types/commit_list_params.py new file mode 100644 index 000000000..6bfeba06b --- /dev/null +++ b/python/langsmith_api/types/commit_list_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CommitListParams"] + + +class CommitListParams(TypedDict, total=False): + owner: Required[str] + + include_stats: bool + """IncludeStats determines whether to compute num_downloads and num_views""" + + limit: int + """Limit is the pagination limit""" + + offset: int + """Offset is the pagination offset""" + + tag: str + """Tag filters commits to only those with a specific tag (e.g. + + "production", "staging") + """ diff --git a/python/langsmith_api/types/commit_manifest_response.py b/python/langsmith_api/types/commit_manifest_response.py new file mode 100644 index 000000000..e6e81914e --- /dev/null +++ b/python/langsmith_api/types/commit_manifest_response.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["CommitManifestResponse", "Example"] + + +class Example(BaseModel): + """Response model for example runs""" + + id: str + + session_id: str + + inputs: Optional[Dict[str, object]] = None + + outputs: Optional[Dict[str, object]] = None + + start_time: Optional[datetime] = None + + +class CommitManifestResponse(BaseModel): + """Response model for get_commit_manifest.""" + + commit_hash: str + + manifest: Dict[str, object] + + examples: Optional[List[Example]] = None diff --git a/python/langsmith_api/types/commit_retrieve_params.py b/python/langsmith_api/types/commit_retrieve_params.py new file mode 100644 index 000000000..878169c5c --- /dev/null +++ b/python/langsmith_api/types/commit_retrieve_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CommitRetrieveParams"] + + +class CommitRetrieveParams(TypedDict, total=False): + owner: Required[str] + + repo: Required[str] + + get_examples: bool + + include: str + """Comma-separated list of optional fields: "model", "is_draft" """ + + include_model: bool + """Deprecated: use Include instead""" + + is_view: bool diff --git a/python/langsmith_api/types/commit_retrieve_response.py b/python/langsmith_api/types/commit_retrieve_response.py new file mode 100644 index 000000000..931e42d7c --- /dev/null +++ b/python/langsmith_api/types/commit_retrieve_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["CommitRetrieveResponse", "Example"] + + +class Example(BaseModel): + id: Optional[str] = None + + inputs: Optional[object] = None + + outputs: Optional[object] = None + + session_id: Optional[str] = None + + start_time: Optional[str] = None + + +class CommitRetrieveResponse(BaseModel): + commit_hash: Optional[str] = None + + description: Optional[str] = None + + examples: Optional[List[Example]] = None + + is_draft: Optional[bool] = None + + manifest: Optional[object] = None + + api_model_config: Optional[object] = FieldInfo(alias="model_config", default=None) + + api_model_provider: Optional[str] = FieldInfo(alias="model_provider", default=None) diff --git a/python/langsmith_api/types/commit_with_lookups.py b/python/langsmith_api/types/commit_with_lookups.py new file mode 100644 index 000000000..ceb648425 --- /dev/null +++ b/python/langsmith_api/types/commit_with_lookups.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["CommitWithLookups"] + + +class CommitWithLookups(BaseModel): + id: Optional[str] = None + """The commit ID""" + + commit_hash: Optional[str] = None + """The hash of the commit""" + + created_at: Optional[datetime] = None + """When the commit was created""" + + description: Optional[str] = None + """Optional human-readable description for the commit""" + + example_run_ids: Optional[List[str]] = None + """Example run IDs associated with the commit""" + + full_name: Optional[str] = None + """Author's full name""" + + manifest: Optional[object] = None + """The manifest of the commit""" + + manifest_sha: Optional[List[int]] = None + """The SHA of the manifest""" + + num_downloads: Optional[int] = None + """Number of API downloads""" + + num_views: Optional[int] = None + """Number of web views""" + + parent_commit_hash: Optional[str] = None + """The hash of the parent commit""" + + parent_id: Optional[str] = None + """The ID of the parent commit""" + + repo_id: Optional[str] = None + """Repository ID""" + + updated_at: Optional[datetime] = None + """When the commit was last updated""" diff --git a/python/langsmith_api/types/create_repo_response.py b/python/langsmith_api/types/create_repo_response.py new file mode 100644 index 000000000..31e518827 --- /dev/null +++ b/python/langsmith_api/types/create_repo_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel +from .repo_with_lookups import RepoWithLookups + +__all__ = ["CreateRepoResponse"] + + +class CreateRepoResponse(BaseModel): + repo: RepoWithLookups + """All database fields for repos, plus helpful computed fields.""" diff --git a/python/langsmith_api/types/custom_charts_section.py b/python/langsmith_api/types/custom_charts_section.py new file mode 100644 index 000000000..fedad148e --- /dev/null +++ b/python/langsmith_api/types/custom_charts_section.py @@ -0,0 +1,304 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = [ + "CustomChartsSection", + "Chart", + "ChartData", + "ChartSeries", + "ChartSeriesFilters", + "ChartSeriesGroupBy", + "ChartCommonFilters", + "SubSection", + "SubSectionChart", + "SubSectionChartData", + "SubSectionChartSeries", + "SubSectionChartSeriesFilters", + "SubSectionChartSeriesGroupBy", + "SubSectionChartCommonFilters", +] + + +class ChartData(BaseModel): + series_id: str + + timestamp: datetime + + value: Union[float, Dict[str, object], None] = None + + group: Optional[str] = None + + +class ChartSeriesFilters(BaseModel): + filter: Optional[str] = None + + session: Optional[List[str]] = None + + trace_filter: Optional[str] = None + + tree_filter: Optional[str] = None + + +class ChartSeriesGroupBy(BaseModel): + """Include additional information about where the group_by param was set.""" + + attribute: Literal["name", "run_type", "tag", "metadata"] + + max_groups: Optional[int] = None + + path: Optional[str] = None + + set_by: Optional[Literal["section", "series"]] = None + + +class ChartSeries(BaseModel): + id: str + + metric: Literal[ + "run_count", + "latency_p50", + "latency_p99", + "latency_avg", + "first_token_p50", + "first_token_p99", + "total_tokens", + "prompt_tokens", + "completion_tokens", + "median_tokens", + "completion_tokens_p50", + "prompt_tokens_p50", + "tokens_p99", + "completion_tokens_p99", + "prompt_tokens_p99", + "feedback", + "feedback_score_avg", + "feedback_values", + "total_cost", + "prompt_cost", + "completion_cost", + "error_rate", + "streaming_rate", + "cost_p50", + "cost_p99", + ] + """Metrics you can chart. + + Feedback metrics are not available for organization-scoped charts. + """ + + name: str + + feedback_key: Optional[str] = None + + filters: Optional[ChartSeriesFilters] = None + + group_by: Optional[ChartSeriesGroupBy] = None + """Include additional information about where the group_by param was set.""" + + project_metric: Optional[ + Literal[ + "memory_usage", + "cpu_usage", + "disk_usage", + "restart_count", + "replica_count", + "worker_count", + "lg_run_count", + "responses_per_second", + "error_responses_per_second", + "p95_latency", + ] + ] = None + """LGP Metrics you can chart.""" + + workspace_id: Optional[str] = None + + +class ChartCommonFilters(BaseModel): + filter: Optional[str] = None + + session: Optional[List[str]] = None + + trace_filter: Optional[str] = None + + tree_filter: Optional[str] = None + + +class Chart(BaseModel): + id: str + + chart_type: Literal["line", "bar"] + """Enum for custom chart types.""" + + data: List[ChartData] + + index: int + + series: List[ChartSeries] + + title: str + + common_filters: Optional[ChartCommonFilters] = None + + description: Optional[str] = None + + metadata: Optional[Dict[str, object]] = None + + +class SubSectionChartData(BaseModel): + series_id: str + + timestamp: datetime + + value: Union[float, Dict[str, object], None] = None + + group: Optional[str] = None + + +class SubSectionChartSeriesFilters(BaseModel): + filter: Optional[str] = None + + session: Optional[List[str]] = None + + trace_filter: Optional[str] = None + + tree_filter: Optional[str] = None + + +class SubSectionChartSeriesGroupBy(BaseModel): + """Include additional information about where the group_by param was set.""" + + attribute: Literal["name", "run_type", "tag", "metadata"] + + max_groups: Optional[int] = None + + path: Optional[str] = None + + set_by: Optional[Literal["section", "series"]] = None + + +class SubSectionChartSeries(BaseModel): + id: str + + metric: Literal[ + "run_count", + "latency_p50", + "latency_p99", + "latency_avg", + "first_token_p50", + "first_token_p99", + "total_tokens", + "prompt_tokens", + "completion_tokens", + "median_tokens", + "completion_tokens_p50", + "prompt_tokens_p50", + "tokens_p99", + "completion_tokens_p99", + "prompt_tokens_p99", + "feedback", + "feedback_score_avg", + "feedback_values", + "total_cost", + "prompt_cost", + "completion_cost", + "error_rate", + "streaming_rate", + "cost_p50", + "cost_p99", + ] + """Metrics you can chart. + + Feedback metrics are not available for organization-scoped charts. + """ + + name: str + + feedback_key: Optional[str] = None + + filters: Optional[SubSectionChartSeriesFilters] = None + + group_by: Optional[SubSectionChartSeriesGroupBy] = None + """Include additional information about where the group_by param was set.""" + + project_metric: Optional[ + Literal[ + "memory_usage", + "cpu_usage", + "disk_usage", + "restart_count", + "replica_count", + "worker_count", + "lg_run_count", + "responses_per_second", + "error_responses_per_second", + "p95_latency", + ] + ] = None + """LGP Metrics you can chart.""" + + workspace_id: Optional[str] = None + + +class SubSectionChartCommonFilters(BaseModel): + filter: Optional[str] = None + + session: Optional[List[str]] = None + + trace_filter: Optional[str] = None + + tree_filter: Optional[str] = None + + +class SubSectionChart(BaseModel): + id: str + + chart_type: Literal["line", "bar"] + """Enum for custom chart types.""" + + data: List[SubSectionChartData] + + index: int + + series: List[SubSectionChartSeries] + + title: str + + common_filters: Optional[SubSectionChartCommonFilters] = None + + description: Optional[str] = None + + metadata: Optional[Dict[str, object]] = None + + +class SubSection(BaseModel): + id: str + + charts: List[SubSectionChart] + + index: int + + title: str + + description: Optional[str] = None + + +class CustomChartsSection(BaseModel): + id: str + + charts: List[Chart] + + title: str + + description: Optional[str] = None + + index: Optional[int] = None + + session_id: Optional[str] = None + + sub_sections: Optional[List[SubSection]] = None diff --git a/python/langsmith_api/types/data_type.py b/python/langsmith_api/types/data_type.py new file mode 100644 index 000000000..02a42c2db --- /dev/null +++ b/python/langsmith_api/types/data_type.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["DataType"] + +DataType: TypeAlias = Literal["kv", "llm", "chat"] diff --git a/python/langsmith_api/types/dataset.py b/python/langsmith_api/types/dataset.py new file mode 100644 index 000000000..70128d91f --- /dev/null +++ b/python/langsmith_api/types/dataset.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from .._models import BaseModel +from .data_type import DataType +from .dataset_transformation import DatasetTransformation + +__all__ = ["Dataset"] + + +class Dataset(BaseModel): + """Dataset schema.""" + + id: str + + modified_at: datetime + + name: str + + session_count: int + + tenant_id: str + + baseline_experiment_id: Optional[str] = None + + created_at: Optional[datetime] = None + + data_type: Optional[DataType] = None + """Enum for dataset data types.""" + + description: Optional[str] = None + + example_count: Optional[int] = None + + externally_managed: Optional[bool] = None + + inputs_schema_definition: Optional[Dict[str, object]] = None + + last_session_start_time: Optional[datetime] = None + + metadata: Optional[Dict[str, object]] = None + + outputs_schema_definition: Optional[Dict[str, object]] = None + + transformations: Optional[List[DatasetTransformation]] = None diff --git a/python/langsmith_api/types/dataset_clone_params.py b/python/langsmith_api/types/dataset_clone_params.py new file mode 100644 index 000000000..8081b98b0 --- /dev/null +++ b/python/langsmith_api/types/dataset_clone_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["DatasetCloneParams"] + + +class DatasetCloneParams(TypedDict, total=False): + source_dataset_id: Required[str] + + target_dataset_id: Required[str] + + as_of: Annotated[Union[Union[str, datetime], str, None], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ + + examples: SequenceNotStr[str] + + split: Union[str, SequenceNotStr[str], None] diff --git a/python/langsmith_api/types/dataset_clone_response.py b/python/langsmith_api/types/dataset_clone_response.py new file mode 100644 index 000000000..d2ac5cc56 --- /dev/null +++ b/python/langsmith_api/types/dataset_clone_response.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List +from typing_extensions import TypeAlias + +__all__ = ["DatasetCloneResponse"] + +DatasetCloneResponse: TypeAlias = List[Dict[str, object]] diff --git a/python/langsmith_api/types/dataset_create_params.py b/python/langsmith_api/types/dataset_create_params.py new file mode 100644 index 000000000..57b4ad01d --- /dev/null +++ b/python/langsmith_api/types/dataset_create_params.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo +from .data_type import DataType +from .dataset_transformation_param import DatasetTransformationParam + +__all__ = ["DatasetCreateParams"] + + +class DatasetCreateParams(TypedDict, total=False): + name: Required[str] + + id: Optional[str] + + created_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + data_type: DataType + """Enum for dataset data types.""" + + description: Optional[str] + + externally_managed: Optional[bool] + + extra: Optional[Dict[str, object]] + + inputs_schema_definition: Optional[Dict[str, object]] + + outputs_schema_definition: Optional[Dict[str, object]] + + transformations: Optional[Iterable[DatasetTransformationParam]] diff --git a/python/langsmith_api/types/dataset_list_params.py b/python/langsmith_api/types/dataset_list_params.py new file mode 100644 index 000000000..ab57de073 --- /dev/null +++ b/python/langsmith_api/types/dataset_list_params.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .data_type import DataType +from .sort_by_dataset_column import SortByDatasetColumn + +__all__ = ["DatasetListParams"] + + +class DatasetListParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + datatype: Annotated[Union[List[DataType], DataType, None], PropertyInfo(alias="data_type")] + """Enum for dataset data types.""" + + exclude: Optional[List[Literal["example_count"]]] + + exclude_corrections_datasets: bool + + limit: int + + metadata: Optional[str] + + name: Optional[str] + + name_contains: Optional[str] + + offset: int + + sort_by: SortByDatasetColumn + """Enum for available dataset columns to sort by.""" + + sort_by_desc: bool + + tag_value_id: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/dataset_retrieve_csv_params.py b/python/langsmith_api/types/dataset_retrieve_csv_params.py new file mode 100644 index 000000000..67bc7c9a8 --- /dev/null +++ b/python/langsmith_api/types/dataset_retrieve_csv_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetRetrieveCsvParams"] + + +class DatasetRetrieveCsvParams(TypedDict, total=False): + as_of: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ diff --git a/python/langsmith_api/types/dataset_retrieve_jsonl_params.py b/python/langsmith_api/types/dataset_retrieve_jsonl_params.py new file mode 100644 index 000000000..79c67b16b --- /dev/null +++ b/python/langsmith_api/types/dataset_retrieve_jsonl_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetRetrieveJSONLParams"] + + +class DatasetRetrieveJSONLParams(TypedDict, total=False): + as_of: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ diff --git a/python/langsmith_api/types/dataset_retrieve_openai_ft_params.py b/python/langsmith_api/types/dataset_retrieve_openai_ft_params.py new file mode 100644 index 000000000..b2e984db4 --- /dev/null +++ b/python/langsmith_api/types/dataset_retrieve_openai_ft_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetRetrieveOpenAIFtParams"] + + +class DatasetRetrieveOpenAIFtParams(TypedDict, total=False): + as_of: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ diff --git a/python/langsmith_api/types/dataset_retrieve_openai_params.py b/python/langsmith_api/types/dataset_retrieve_openai_params.py new file mode 100644 index 000000000..a67426ec7 --- /dev/null +++ b/python/langsmith_api/types/dataset_retrieve_openai_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetRetrieveOpenAIParams"] + + +class DatasetRetrieveOpenAIParams(TypedDict, total=False): + as_of: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ diff --git a/python/langsmith_api/types/dataset_retrieve_version_params.py b/python/langsmith_api/types/dataset_retrieve_version_params.py new file mode 100644 index 000000000..a275e5af9 --- /dev/null +++ b/python/langsmith_api/types/dataset_retrieve_version_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetRetrieveVersionParams"] + + +class DatasetRetrieveVersionParams(TypedDict, total=False): + as_of: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + tag: Optional[str] diff --git a/python/langsmith_api/types/dataset_transformation.py b/python/langsmith_api/types/dataset_transformation.py new file mode 100644 index 000000000..d703a5493 --- /dev/null +++ b/python/langsmith_api/types/dataset_transformation.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["DatasetTransformation"] + + +class DatasetTransformation(BaseModel): + path: List[str] + + transformation_type: Literal[ + "convert_to_openai_message", + "convert_to_openai_tool", + "remove_system_messages", + "remove_extra_fields", + "extract_tools_from_run", + ] + """ + Enum for dataset transformation types. Ordering determines the order in which + transformations are applied if there are multiple transformations on the same + path. + """ diff --git a/python/langsmith_api/types/dataset_transformation_param.py b/python/langsmith_api/types/dataset_transformation_param.py new file mode 100644 index 000000000..af0e6cf18 --- /dev/null +++ b/python/langsmith_api/types/dataset_transformation_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["DatasetTransformationParam"] + + +class DatasetTransformationParam(TypedDict, total=False): + path: Required[SequenceNotStr[str]] + + transformation_type: Required[ + Literal[ + "convert_to_openai_message", + "convert_to_openai_tool", + "remove_system_messages", + "remove_extra_fields", + "extract_tools_from_run", + ] + ] + """ + Enum for dataset transformation types. Ordering determines the order in which + transformations are applied if there are multiple transformations on the same + path. + """ diff --git a/python/langsmith_api/types/dataset_update_params.py b/python/langsmith_api/types/dataset_update_params.py new file mode 100644 index 000000000..a6634d8d1 --- /dev/null +++ b/python/langsmith_api/types/dataset_update_params.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .missing_param import MissingParam +from .attachments_operations_param import AttachmentsOperationsParam +from .dataset_transformation_param import DatasetTransformationParam + +__all__ = [ + "DatasetUpdateParams", + "BaselineExperimentID", + "Description", + "InputsSchemaDefinition", + "Metadata", + "Name", + "OutputsSchemaDefinition", + "PatchExamples", + "Transformations", +] + + +class DatasetUpdateParams(TypedDict, total=False): + baseline_experiment_id: Optional[BaselineExperimentID] + + description: Optional[Description] + + inputs_schema_definition: Optional[InputsSchemaDefinition] + + metadata: Optional[Metadata] + + name: Optional[Name] + + outputs_schema_definition: Optional[OutputsSchemaDefinition] + + patch_examples: Optional[Dict[str, PatchExamples]] + + transformations: Optional[Transformations] + + +BaselineExperimentID: TypeAlias = Union[str, MissingParam] + +Description: TypeAlias = Union[str, MissingParam] + +InputsSchemaDefinition: TypeAlias = Union[Dict[str, object], MissingParam] + +Metadata: TypeAlias = Union[Dict[str, object], MissingParam] + +Name: TypeAlias = Union[str, MissingParam] + +OutputsSchemaDefinition: TypeAlias = Union[Dict[str, object], MissingParam] + + +class PatchExamples(TypedDict, total=False): + """Update class for Example.""" + + attachments_operations: Optional[AttachmentsOperationsParam] + + dataset_id: Optional[str] + + inputs: Optional[Dict[str, object]] + + metadata: Optional[Dict[str, object]] + + outputs: Optional[Dict[str, object]] + + overwrite: bool + + split: Union[SequenceNotStr[str], str, None] + + +Transformations: TypeAlias = Union[Iterable[DatasetTransformationParam], MissingParam] diff --git a/python/langsmith_api/types/dataset_update_response.py b/python/langsmith_api/types/dataset_update_response.py new file mode 100644 index 000000000..4a210dc59 --- /dev/null +++ b/python/langsmith_api/types/dataset_update_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from .._models import BaseModel +from .data_type import DataType +from .dataset_transformation import DatasetTransformation + +__all__ = ["DatasetUpdateResponse"] + + +class DatasetUpdateResponse(BaseModel): + id: str + + name: str + + tenant_id: str + + created_at: Optional[datetime] = None + + data_type: Optional[DataType] = None + """Enum for dataset data types.""" + + description: Optional[str] = None + + externally_managed: Optional[bool] = None + + inputs_schema_definition: Optional[Dict[str, object]] = None + + outputs_schema_definition: Optional[Dict[str, object]] = None + + transformations: Optional[List[DatasetTransformation]] = None diff --git a/python/langsmith_api/types/dataset_update_tags_params.py b/python/langsmith_api/types/dataset_update_tags_params.py new file mode 100644 index 000000000..856c0e6d6 --- /dev/null +++ b/python/langsmith_api/types/dataset_update_tags_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DatasetUpdateTagsParams"] + + +class DatasetUpdateTagsParams(TypedDict, total=False): + as_of: Required[Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")]] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ + + tag: Required[str] diff --git a/python/langsmith_api/types/dataset_upload_params.py b/python/langsmith_api/types/dataset_upload_params.py new file mode 100644 index 000000000..2d1fbb7dd --- /dev/null +++ b/python/langsmith_api/types/dataset_upload_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr +from .data_type import DataType + +__all__ = ["DatasetUploadParams"] + + +class DatasetUploadParams(TypedDict, total=False): + file: Required[FileTypes] + + input_keys: Required[SequenceNotStr[str]] + + data_type: DataType + """Enum for dataset data types.""" + + description: Optional[str] + + input_key_mappings: Optional[str] + + inputs_schema_definition: Optional[str] + + metadata_key_mappings: Optional[str] + + metadata_keys: SequenceNotStr[str] + + name: Optional[str] + + output_key_mappings: Optional[str] + + output_keys: SequenceNotStr[str] + + outputs_schema_definition: Optional[str] + + transformations: Optional[str] diff --git a/python/langsmith_api/types/dataset_version.py b/python/langsmith_api/types/dataset_version.py new file mode 100644 index 000000000..a70c273bf --- /dev/null +++ b/python/langsmith_api/types/dataset_version.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["DatasetVersion"] + + +class DatasetVersion(BaseModel): + """Dataset version schema.""" + + as_of: datetime + + tags: Optional[List[str]] = None diff --git a/python/langsmith_api/types/datasets/__init__.py b/python/langsmith_api/types/datasets/__init__.py new file mode 100644 index 000000000..e66e98396 --- /dev/null +++ b/python/langsmith_api/types/datasets/__init__.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .run_delta_params import RunDeltaParams as RunDeltaParams +from .group_runs_params import GroupRunsParams as GroupRunsParams +from .run_create_params import RunCreateParams as RunCreateParams +from .group_runs_response import GroupRunsResponse as GroupRunsResponse +from .run_create_response import RunCreateResponse as RunCreateResponse +from .runner_context_enum import RunnerContextEnum as RunnerContextEnum +from .share_create_params import ShareCreateParams as ShareCreateParams +from .split_create_params import SplitCreateParams as SplitCreateParams +from .version_list_params import VersionListParams as VersionListParams +from .dataset_share_schema import DatasetShareSchema as DatasetShareSchema +from .example_with_runs_ch import ExampleWithRunsCh as ExampleWithRunsCh +from .runnable_config_param import RunnableConfigParam as RunnableConfigParam +from .split_create_response import SplitCreateResponse as SplitCreateResponse +from .split_retrieve_params import SplitRetrieveParams as SplitRetrieveParams +from .session_feedback_delta import SessionFeedbackDelta as SessionFeedbackDelta +from .simple_experiment_info import SimpleExperimentInfo as SimpleExperimentInfo +from .split_retrieve_response import SplitRetrieveResponse as SplitRetrieveResponse +from .comparative_create_params import ComparativeCreateParams as ComparativeCreateParams +from .experiment_grouped_params import ExperimentGroupedParams as ExperimentGroupedParams +from .comparative_create_response import ComparativeCreateResponse as ComparativeCreateResponse +from .version_retrieve_diff_params import VersionRetrieveDiffParams as VersionRetrieveDiffParams +from .version_retrieve_diff_response import VersionRetrieveDiffResponse as VersionRetrieveDiffResponse +from .playground_experiment_batch_params import PlaygroundExperimentBatchParams as PlaygroundExperimentBatchParams +from .playground_experiment_stream_params import PlaygroundExperimentStreamParams as PlaygroundExperimentStreamParams +from .sort_by_comparative_experiment_column import ( + SortByComparativeExperimentColumn as SortByComparativeExperimentColumn, +) +from .sort_params_for_runs_comparison_view_param import ( + SortParamsForRunsComparisonViewParam as SortParamsForRunsComparisonViewParam, +) diff --git a/python/langsmith_api/types/datasets/comparative_create_params.py b/python/langsmith_api/types/datasets/comparative_create_params.py new file mode 100644 index 000000000..86f5bf657 --- /dev/null +++ b/python/langsmith_api/types/datasets/comparative_create_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo + +__all__ = ["ComparativeCreateParams"] + + +class ComparativeCreateParams(TypedDict, total=False): + experiment_ids: Required[SequenceNotStr[str]] + + id: str + + created_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + description: Optional[str] + + extra: Optional[Dict[str, object]] + + modified_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + name: Optional[str] + + reference_dataset_id: Optional[str] diff --git a/python/langsmith_api/types/datasets/comparative_create_response.py b/python/langsmith_api/types/datasets/comparative_create_response.py new file mode 100644 index 000000000..96bafef6c --- /dev/null +++ b/python/langsmith_api/types/datasets/comparative_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["ComparativeCreateResponse"] + + +class ComparativeCreateResponse(BaseModel): + """ComparativeExperiment schema.""" + + id: str + + created_at: datetime + + modified_at: datetime + + reference_dataset_id: str + + tenant_id: str + + description: Optional[str] = None + + extra: Optional[Dict[str, object]] = None + + name: Optional[str] = None diff --git a/python/langsmith_api/types/datasets/dataset_share_schema.py b/python/langsmith_api/types/datasets/dataset_share_schema.py new file mode 100644 index 000000000..3290a47e8 --- /dev/null +++ b/python/langsmith_api/types/datasets/dataset_share_schema.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["DatasetShareSchema"] + + +class DatasetShareSchema(BaseModel): + dataset_id: str + + share_token: str diff --git a/python/langsmith_api/types/datasets/example_with_runs_ch.py b/python/langsmith_api/types/datasets/example_with_runs_ch.py new file mode 100644 index 000000000..1a496ba3d --- /dev/null +++ b/python/langsmith_api/types/datasets/example_with_runs_ch.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from ..._models import BaseModel +from ..run_type_enum import RunTypeEnum + +__all__ = ["ExampleWithRunsCh", "Run"] + + +class Run(BaseModel): + """Run schema for comparison view.""" + + id: str + + name: str + + run_type: RunTypeEnum + """Enum for run types.""" + + session_id: str + + status: str + + trace_id: str + + app_path: Optional[str] = None + + completion_cost: Optional[str] = None + + completion_tokens: Optional[int] = None + + dotted_order: Optional[str] = None + + end_time: Optional[datetime] = None + + error: Optional[str] = None + + events: Optional[List[Dict[str, object]]] = None + + execution_order: Optional[int] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, Dict[str, object]]] = None + + inputs: Optional[Dict[str, object]] = None + + inputs_preview: Optional[str] = None + + inputs_s3_urls: Optional[Dict[str, object]] = None + + manifest_id: Optional[str] = None + + manifest_s3_id: Optional[str] = None + + outputs: Optional[Dict[str, object]] = None + + outputs_preview: Optional[str] = None + + outputs_s3_urls: Optional[Dict[str, object]] = None + + parent_run_id: Optional[str] = None + + prompt_cost: Optional[str] = None + + prompt_tokens: Optional[int] = None + + reference_example_id: Optional[str] = None + + s3_urls: Optional[Dict[str, object]] = None + + serialized: Optional[Dict[str, object]] = None + + start_time: Optional[datetime] = None + + tags: Optional[List[str]] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + trace_max_start_time: Optional[datetime] = None + + trace_min_start_time: Optional[datetime] = None + + +class ExampleWithRunsCh(BaseModel): + """Example schema with list of runs from ClickHouse. + + For non-grouped endpoint (/datasets/{dataset_id}/runs): runs from single session. + For grouped endpoint (/datasets/{dataset_id}/group/runs): flat array of runs from + all sessions, where each run has a session_id field for frontend to determine column placement. + """ + + id: str + + dataset_id: str + + inputs: Dict[str, object] + + name: str + + runs: List[Run] + + attachment_urls: Optional[Dict[str, object]] = None + + created_at: Optional[datetime] = None + + metadata: Optional[Dict[str, object]] = None + + modified_at: Optional[datetime] = None + + outputs: Optional[Dict[str, object]] = None + + source_run_id: Optional[str] = None diff --git a/python/langsmith_api/types/datasets/experiment_grouped_params.py b/python/langsmith_api/types/datasets/experiment_grouped_params.py new file mode 100644 index 000000000..169b66532 --- /dev/null +++ b/python/langsmith_api/types/datasets/experiment_grouped_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo + +__all__ = ["ExperimentGroupedParams"] + + +class ExperimentGroupedParams(TypedDict, total=False): + metadata_keys: Required[SequenceNotStr[str]] + + dataset_version: Optional[str] + + experiment_limit: int + + filter: Optional[str] + + name_contains: Optional[str] + + stats_start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + tag_value_id: Optional[SequenceNotStr[str]] + + use_approx_stats: bool diff --git a/python/langsmith_api/types/datasets/group_runs_params.py b/python/langsmith_api/types/datasets/group_runs_params.py new file mode 100644 index 000000000..394c2686d --- /dev/null +++ b/python/langsmith_api/types/datasets/group_runs_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["GroupRunsParams"] + + +class GroupRunsParams(TypedDict, total=False): + group_by: Required[Literal["run_metadata", "example_metadata"]] + + metadata_key: Required[str] + + session_ids: Required[SequenceNotStr[str]] + + filters: Optional[Dict[str, SequenceNotStr[str]]] + + limit: int + + offset: int + + per_group_limit: int + + preview: bool diff --git a/python/langsmith_api/types/datasets/group_runs_response.py b/python/langsmith_api/types/datasets/group_runs_response.py new file mode 100644 index 000000000..348e39f44 --- /dev/null +++ b/python/langsmith_api/types/datasets/group_runs_response.py @@ -0,0 +1,174 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from .example_with_runs_ch import ExampleWithRunsCh + +__all__ = ["GroupRunsResponse", "Group", "GroupSession", "GroupSessionExperimentProgress"] + + +class GroupSessionExperimentProgress(BaseModel): + evaluator_progress: Dict[str, float] + + expected_run_count: int + + run_progress: float + + +class GroupSession(BaseModel): + """TracerSession stats filtered to runs matching a specific metadata value. + + Extends TracerSession with: + - example_count: unique examples (vs run_count = total runs including duplicates) + - filter: ClickHouse filter for fetching runs in this session/group + - min/max_start_time: time range for runs in this session/group + """ + + id: str + + filter: str + + tenant_id: str + + completion_cost: Optional[str] = None + + completion_tokens: Optional[int] = None + + default_dataset_id: Optional[str] = None + + description: Optional[str] = None + + end_time: Optional[datetime] = None + + error_rate: Optional[float] = None + + example_count: Optional[int] = None + + experiment_progress: Optional[GroupSessionExperimentProgress] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, object]] = None + + first_token_p50: Optional[float] = None + + first_token_p99: Optional[float] = None + + last_run_start_time: Optional[datetime] = None + + last_run_start_time_live: Optional[datetime] = None + + latency_p50: Optional[float] = None + + latency_p99: Optional[float] = None + + max_start_time: Optional[datetime] = None + + min_start_time: Optional[datetime] = None + + name: Optional[str] = None + + prompt_cost: Optional[str] = None + + prompt_tokens: Optional[int] = None + + reference_dataset_id: Optional[str] = None + + run_count: Optional[int] = None + + run_facets: Optional[List[Dict[str, object]]] = None + + session_feedback_stats: Optional[Dict[str, object]] = None + + start_time: Optional[datetime] = None + + streaming_rate: Optional[float] = None + + test_run_number: Optional[int] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + trace_tier: Optional[Literal["longlived", "shortlived"]] = None + + +class Group(BaseModel): + """Group of examples with a specific metadata value across multiple sessions. + + Extends RunGroupBase with: + - group_key: metadata value that defines this group + - sessions: per-session stats for runs matching this metadata value + - examples: shared examples across all sessions (intersection logic) + with flat array of runs (each run has session_id field for frontend to determine column) + - example_count: unique example count (pagination-aware, same across all sessions due to intersection) + + Inherited from RunGroupBase: + - filter: metadata filter for this group (e.g., "and(eq(is_root, true), and(eq(metadata_key, 'model'), eq(metadata_value, 'gpt-4')))") + - count: total run count across all sessions (includes duplicate runs) + - total_tokens, total_cost: aggregate across sessions + - min_start_time, max_start_time: time range across sessions + - latency_p50, latency_p99: aggregate latency stats across sessions + - feedback_stats: weighted average feedback across sessions + + Additional aggregate stats: + - prompt_tokens, completion_tokens: separate token counts + - prompt_cost, completion_cost: separate costs + - error_rate: average error rate + """ + + example_count: int + + examples: List[ExampleWithRunsCh] + + filter: str + + group_key: Union[str, float] + + sessions: List[GroupSession] + + completion_cost: Optional[str] = None + + completion_tokens: Optional[int] = None + + count: Optional[int] = None + + error_rate: Optional[float] = None + + feedback_stats: Optional[Dict[str, object]] = None + + latency_p50: Optional[float] = None + + latency_p99: Optional[float] = None + + max_start_time: Optional[datetime] = None + + min_start_time: Optional[datetime] = None + + prompt_cost: Optional[str] = None + + prompt_tokens: Optional[int] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + +class GroupRunsResponse(BaseModel): + """Response for grouped comparison view of dataset examples. + + Returns dataset examples grouped by a run metadata value (e.g., model='gpt-4'). + Optional filters are applied to all runs before grouping. + + Shows: + - Which examples were executed with each metadata value + - Per-session aggregate statistics for runs on those examples + - The actual example data with their associated runs + + Used for comparing how different sessions performed on the same set of examples. + """ + + groups: List[Group] diff --git a/python/langsmith_api/types/datasets/playground_experiment_batch_params.py b/python/langsmith_api/types/datasets/playground_experiment_batch_params.py new file mode 100644 index 000000000..435faee39 --- /dev/null +++ b/python/langsmith_api/types/datasets/playground_experiment_batch_params.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr +from .runner_context_enum import RunnerContextEnum +from .runnable_config_param import RunnableConfigParam + +__all__ = ["PlaygroundExperimentBatchParams"] + + +class PlaygroundExperimentBatchParams(TypedDict, total=False): + dataset_id: Required[str] + + manifest: Required[object] + + options: Required[RunnableConfigParam] + """Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + """ + + project_name: Required[str] + + secrets: Required[Dict[str, str]] + + batch_size: Optional[int] + + commit: Optional[str] + + dataset_splits: Optional[SequenceNotStr[str]] + + evaluator_rules: Optional[SequenceNotStr[str]] + + metadata: Optional[Dict[str, object]] + + owner: Optional[str] + + parallel_tool_calls: Optional[bool] + + repetitions: int + + repo_handle: Optional[str] + + repo_id: Optional[str] + + requests_per_second: Optional[int] + + run_id: Optional[str] + + runner_context: Optional[RunnerContextEnum] + + tool_choice: Optional[str] + + tools: Optional[Iterable[object]] + + use_or_fallback_to_workspace_secrets: bool diff --git a/python/langsmith_api/types/datasets/playground_experiment_stream_params.py b/python/langsmith_api/types/datasets/playground_experiment_stream_params.py new file mode 100644 index 000000000..bc622bb5c --- /dev/null +++ b/python/langsmith_api/types/datasets/playground_experiment_stream_params.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr +from .runner_context_enum import RunnerContextEnum +from .runnable_config_param import RunnableConfigParam + +__all__ = ["PlaygroundExperimentStreamParams"] + + +class PlaygroundExperimentStreamParams(TypedDict, total=False): + dataset_id: Required[str] + + manifest: Required[object] + + options: Required[RunnableConfigParam] + """Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + """ + + project_name: Required[str] + + secrets: Required[Dict[str, str]] + + commit: Optional[str] + + dataset_splits: Optional[SequenceNotStr[str]] + + evaluator_rules: Optional[SequenceNotStr[str]] + + metadata: Optional[Dict[str, object]] + + owner: Optional[str] + + parallel_tool_calls: Optional[bool] + + repetitions: int + + repo_handle: Optional[str] + + repo_id: Optional[str] + + requests_per_second: Optional[int] + + run_id: Optional[str] + + runner_context: Optional[RunnerContextEnum] + + tool_choice: Optional[str] + + tools: Optional[Iterable[object]] + + use_or_fallback_to_workspace_secrets: bool diff --git a/python/langsmith_api/types/datasets/run_create_params.py b/python/langsmith_api/types/datasets/run_create_params.py new file mode 100644 index 000000000..1fc9ebd55 --- /dev/null +++ b/python/langsmith_api/types/datasets/run_create_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .sort_params_for_runs_comparison_view_param import SortParamsForRunsComparisonViewParam + +__all__ = ["RunCreateParams"] + + +class RunCreateParams(TypedDict, total=False): + session_ids: Required[SequenceNotStr[str]] + + format: Optional[Literal["csv"]] + """Response format, e.g., 'csv'""" + + comparative_experiment_id: Optional[str] + + example_ids: Optional[SequenceNotStr[str]] + + filters: Optional[Dict[str, SequenceNotStr[str]]] + + include_annotator_detail: bool + + limit: Optional[int] + + offset: int + + preview: bool + + sort_params: Optional[SortParamsForRunsComparisonViewParam] diff --git a/python/langsmith_api/types/datasets/run_create_response.py b/python/langsmith_api/types/datasets/run_create_response.py new file mode 100644 index 000000000..7074116de --- /dev/null +++ b/python/langsmith_api/types/datasets/run_create_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .example_with_runs_ch import ExampleWithRunsCh + +__all__ = ["RunCreateResponse"] + +RunCreateResponse: TypeAlias = List[ExampleWithRunsCh] diff --git a/python/langsmith_api/types/datasets/run_delta_params.py b/python/langsmith_api/types/datasets/run_delta_params.py new file mode 100644 index 000000000..1bee401b5 --- /dev/null +++ b/python/langsmith_api/types/datasets/run_delta_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["RunDeltaParams"] + + +class RunDeltaParams(TypedDict, total=False): + baseline_session_id: Required[str] + + comparison_session_ids: Required[SequenceNotStr[str]] + + feedback_key: Required[str] + + comparative_experiment_id: Optional[str] + + filters: Optional[Dict[str, SequenceNotStr[str]]] + + limit: int + + offset: int diff --git a/python/langsmith_api/types/datasets/runnable_config_param.py b/python/langsmith_api/types/datasets/runnable_config_param.py new file mode 100644 index 000000000..a041ef75d --- /dev/null +++ b/python/langsmith_api/types/datasets/runnable_config_param.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["RunnableConfigParam"] + + +class RunnableConfigParam(TypedDict, total=False): + """Configuration for a `Runnable`. + + !!! note Custom values + + The `TypedDict` has `total=False` set intentionally to: + + - Allow partial configs to be created and merged together via `merge_configs` + - Support config propagation from parent to child runnables via + `var_child_runnable_config` (a `ContextVar` that automatically passes + config down the call stack without explicit parameter passing), where + configs are merged rather than replaced + + !!! example + + ```python + # Parent sets tags + chain.invoke(input, config={"tags": ["parent"]}) + # Child automatically inherits and can add: + # ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]} + ``` + """ + + callbacks: Union[Iterable[object], object, None] + + configurable: Dict[str, object] + + max_concurrency: Optional[int] + + metadata: Dict[str, object] + + recursion_limit: int + + run_id: Optional[str] + + run_name: str + + tags: SequenceNotStr[str] diff --git a/python/langsmith_api/types/datasets/runner_context_enum.py b/python/langsmith_api/types/datasets/runner_context_enum.py new file mode 100644 index 000000000..b407ef0e7 --- /dev/null +++ b/python/langsmith_api/types/datasets/runner_context_enum.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunnerContextEnum"] + +RunnerContextEnum: TypeAlias = Literal["langsmith_ui", "langsmith_align_evals"] diff --git a/python/langsmith_api/types/datasets/session_feedback_delta.py b/python/langsmith_api/types/datasets/session_feedback_delta.py new file mode 100644 index 000000000..90b03db00 --- /dev/null +++ b/python/langsmith_api/types/datasets/session_feedback_delta.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List + +from ..._models import BaseModel + +__all__ = ["SessionFeedbackDelta", "FeedbackDeltas"] + + +class FeedbackDeltas(BaseModel): + """Feedback key with number of improvements and regressions.""" + + improved_examples: List[str] + + regressed_examples: List[str] + + +class SessionFeedbackDelta(BaseModel): + """List of feedback keys with number of improvements and regressions for each.""" + + feedback_deltas: Dict[str, FeedbackDeltas] diff --git a/python/langsmith_api/types/datasets/share_create_params.py b/python/langsmith_api/types/datasets/share_create_params.py new file mode 100644 index 000000000..ad441617d --- /dev/null +++ b/python/langsmith_api/types/datasets/share_create_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ShareCreateParams"] + + +class ShareCreateParams(TypedDict, total=False): + share_projects: bool diff --git a/python/langsmith_api/types/datasets/simple_experiment_info.py b/python/langsmith_api/types/datasets/simple_experiment_info.py new file mode 100644 index 000000000..4b5047b42 --- /dev/null +++ b/python/langsmith_api/types/datasets/simple_experiment_info.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["SimpleExperimentInfo"] + + +class SimpleExperimentInfo(BaseModel): + """Simple experiment info schema for use with comparative experiments""" + + id: str + + name: str diff --git a/python/langsmith_api/types/datasets/sort_by_comparative_experiment_column.py b/python/langsmith_api/types/datasets/sort_by_comparative_experiment_column.py new file mode 100644 index 000000000..389a97239 --- /dev/null +++ b/python/langsmith_api/types/datasets/sort_by_comparative_experiment_column.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SortByComparativeExperimentColumn"] + +SortByComparativeExperimentColumn: TypeAlias = Literal["name", "created_at"] diff --git a/python/langsmith_api/types/datasets/sort_params_for_runs_comparison_view_param.py b/python/langsmith_api/types/datasets/sort_params_for_runs_comparison_view_param.py new file mode 100644 index 000000000..5e23f5dae --- /dev/null +++ b/python/langsmith_api/types/datasets/sort_params_for_runs_comparison_view_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SortParamsForRunsComparisonViewParam"] + + +class SortParamsForRunsComparisonViewParam(TypedDict, total=False): + sort_by: Required[str] + + sort_order: Literal["ASC", "DESC"] diff --git a/python/langsmith_api/types/datasets/split_create_params.py b/python/langsmith_api/types/datasets/split_create_params.py new file mode 100644 index 000000000..6d6a52d25 --- /dev/null +++ b/python/langsmith_api/types/datasets/split_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["SplitCreateParams"] + + +class SplitCreateParams(TypedDict, total=False): + examples: Required[SequenceNotStr[str]] + + split_name: Required[str] + + remove: bool diff --git a/python/langsmith_api/types/datasets/split_create_response.py b/python/langsmith_api/types/datasets/split_create_response.py new file mode 100644 index 000000000..a905f4447 --- /dev/null +++ b/python/langsmith_api/types/datasets/split_create_response.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +__all__ = ["SplitCreateResponse"] + +SplitCreateResponse: TypeAlias = List[str] diff --git a/python/langsmith_api/types/datasets/split_retrieve_params.py b/python/langsmith_api/types/datasets/split_retrieve_params.py new file mode 100644 index 000000000..8eb341a0e --- /dev/null +++ b/python/langsmith_api/types/datasets/split_retrieve_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["SplitRetrieveParams"] + + +class SplitRetrieveParams(TypedDict, total=False): + as_of: Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ diff --git a/python/langsmith_api/types/datasets/split_retrieve_response.py b/python/langsmith_api/types/datasets/split_retrieve_response.py new file mode 100644 index 000000000..21959cf76 --- /dev/null +++ b/python/langsmith_api/types/datasets/split_retrieve_response.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +__all__ = ["SplitRetrieveResponse"] + +SplitRetrieveResponse: TypeAlias = List[str] diff --git a/python/langsmith_api/types/datasets/version_list_params.py b/python/langsmith_api/types/datasets/version_list_params.py new file mode 100644 index 000000000..e22187228 --- /dev/null +++ b/python/langsmith_api/types/datasets/version_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["VersionListParams"] + + +class VersionListParams(TypedDict, total=False): + example: Optional[str] + + limit: int + + offset: int + + search: Optional[str] diff --git a/python/langsmith_api/types/datasets/version_retrieve_diff_params.py b/python/langsmith_api/types/datasets/version_retrieve_diff_params.py new file mode 100644 index 000000000..553c60b7f --- /dev/null +++ b/python/langsmith_api/types/datasets/version_retrieve_diff_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["VersionRetrieveDiffParams"] + + +class VersionRetrieveDiffParams(TypedDict, total=False): + from_version: Required[Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")]] + + to_version: Required[Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")]] diff --git a/python/langsmith_api/types/datasets/version_retrieve_diff_response.py b/python/langsmith_api/types/datasets/version_retrieve_diff_response.py new file mode 100644 index 000000000..114e9f1c1 --- /dev/null +++ b/python/langsmith_api/types/datasets/version_retrieve_diff_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["VersionRetrieveDiffResponse"] + + +class VersionRetrieveDiffResponse(BaseModel): + """Dataset diff schema.""" + + examples_added: List[str] + + examples_modified: List[str] + + examples_removed: List[str] diff --git a/python/langsmith_api/types/e_prompt_optimization_algorithm.py b/python/langsmith_api/types/e_prompt_optimization_algorithm.py new file mode 100644 index 000000000..a4311c9fb --- /dev/null +++ b/python/langsmith_api/types/e_prompt_optimization_algorithm.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["EPromptOptimizationAlgorithm"] + +EPromptOptimizationAlgorithm: TypeAlias = Literal["promptim", "demo"] diff --git a/python/langsmith_api/types/evaluator.py b/python/langsmith_api/types/evaluator.py new file mode 100644 index 000000000..34347fe58 --- /dev/null +++ b/python/langsmith_api/types/evaluator.py @@ -0,0 +1,107 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .evaluator_webhook import EvaluatorWebhook +from .evaluator_top_level import EvaluatorTopLevel +from .code_evaluator_top_level import CodeEvaluatorTopLevel +from .evaluator_pagerduty_alert import EvaluatorPagerdutyAlert + +__all__ = ["Evaluator", "SpendLimit"] + + +class SpendLimit(BaseModel): + limit_usd: str + + window: Literal["weekly"] + + +class Evaluator(BaseModel): + """Run rules schema.""" + + id: str + + created_at: datetime + + display_name: str + + evaluator_version: int + + sampling_rate: float + + tenant_id: str + + updated_at: datetime + + webhooks: Optional[List[EvaluatorWebhook]] = None + + add_to_annotation_queue_id: Optional[str] = None + + add_to_annotation_queue_name: Optional[str] = None + + add_to_dataset_id: Optional[str] = None + + add_to_dataset_name: Optional[str] = None + + add_to_dataset_prefer_correction: Optional[bool] = None + + alerts: Optional[List[EvaluatorPagerdutyAlert]] = None + + alignment_annotation_queue_id: Optional[str] = None + + backfill_completed_at: Optional[datetime] = None + + backfill_error: Optional[str] = None + + backfill_from: Optional[datetime] = None + + backfill_id: Optional[str] = None + + backfill_progress: Optional[float] = None + + backfill_status: Optional[str] = None + + code_evaluators: Optional[List[CodeEvaluatorTopLevel]] = None + + corrections_dataset_id: Optional[str] = None + + dataset_id: Optional[str] = None + + dataset_name: Optional[str] = None + + evaluator_id: Optional[str] = None + + evaluators: Optional[List[EvaluatorTopLevel]] = None + + extend_only: Optional[bool] = None + + filter: Optional[str] = None + + group_by: Optional[Literal["thread_id"]] = None + + include_extended_stats: Optional[bool] = None + + is_enabled: Optional[bool] = None + + num_few_shot_examples: Optional[int] = None + + session_id: Optional[str] = None + + session_name: Optional[str] = None + + spend_limit: Optional[SpendLimit] = None + + spend_usd: Optional[float] = None + + trace_count: Optional[int] = None + + trace_filter: Optional[str] = None + + transient: Optional[bool] = None + + tree_filter: Optional[str] = None + + use_corrections_dataset: Optional[bool] = None diff --git a/python/langsmith_api/types/evaluator_list_params.py b/python/langsmith_api/types/evaluator_list_params.py new file mode 100644 index 000000000..fc243fd6e --- /dev/null +++ b/python/langsmith_api/types/evaluator_list_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["EvaluatorListParams"] + + +class EvaluatorListParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + dataset_id: Optional[str] + + evaluator_id: Optional[str] + + include_backfill_progress: bool + + name_contains: Optional[str] + + session_id: Optional[str] + + tag_value_id: Optional[SequenceNotStr[str]] + + type: Optional[Literal["session", "dataset"]] diff --git a/python/langsmith_api/types/evaluator_list_response.py b/python/langsmith_api/types/evaluator_list_response.py new file mode 100644 index 000000000..2a1109e9c --- /dev/null +++ b/python/langsmith_api/types/evaluator_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .evaluator import Evaluator + +__all__ = ["EvaluatorListResponse"] + +EvaluatorListResponse: TypeAlias = List[Evaluator] diff --git a/python/langsmith_api/types/evaluator_pagerduty_alert.py b/python/langsmith_api/types/evaluator_pagerduty_alert.py new file mode 100644 index 000000000..80472c36b --- /dev/null +++ b/python/langsmith_api/types/evaluator_pagerduty_alert.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EvaluatorPagerdutyAlert"] + + +class EvaluatorPagerdutyAlert(BaseModel): + routing_key: str + + severity: Optional[Literal["critical", "warning", "error", "info"]] = None + """Enum for severity.""" + + summary: Optional[str] = None + + type: Optional[Literal["pagerduty"]] = None + """Enum for alert types.""" diff --git a/python/langsmith_api/types/evaluator_top_level.py b/python/langsmith_api/types/evaluator_top_level.py new file mode 100644 index 000000000..7c15d1c5c --- /dev/null +++ b/python/langsmith_api/types/evaluator_top_level.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EvaluatorTopLevel", "Structured"] + + +class Structured(BaseModel): + """Evaluator structured output schema.""" + + hub_ref: Optional[str] = None + + model: Optional[Dict[str, object]] = None + + prompt: Optional[List[List[object]]] = None + + schema_: Optional[Dict[str, object]] = FieldInfo(alias="schema", default=None) + + template_format: Optional[str] = None + + variable_mapping: Optional[Dict[str, str]] = None + + +class EvaluatorTopLevel(BaseModel): + structured: Structured + """Evaluator structured output schema.""" diff --git a/python/langsmith_api/types/evaluator_webhook.py b/python/langsmith_api/types/evaluator_webhook.py new file mode 100644 index 000000000..2cc2a12fd --- /dev/null +++ b/python/langsmith_api/types/evaluator_webhook.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from .._models import BaseModel + +__all__ = ["EvaluatorWebhook"] + + +class EvaluatorWebhook(BaseModel): + url: str + + headers: Optional[Dict[str, str]] = None diff --git a/python/langsmith_api/types/example.py b/python/langsmith_api/types/example.py new file mode 100644 index 000000000..f3bb06de9 --- /dev/null +++ b/python/langsmith_api/types/example.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["Example"] + + +class Example(BaseModel): + """Example schema.""" + + id: str + + dataset_id: str + + inputs: Dict[str, object] + + name: str + + attachment_urls: Optional[Dict[str, object]] = None + + created_at: Optional[datetime] = None + + metadata: Optional[Dict[str, object]] = None + + modified_at: Optional[datetime] = None + + outputs: Optional[Dict[str, object]] = None + + source_run_id: Optional[str] = None diff --git a/python/langsmith_api/types/example_create_params.py b/python/langsmith_api/types/example_create_params.py new file mode 100644 index 000000000..3451bb1c5 --- /dev/null +++ b/python/langsmith_api/types/example_create_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ExampleCreateParams"] + + +class ExampleCreateParams(TypedDict, total=False): + dataset_id: Required[str] + + id: Optional[str] + + created_at: str + + inputs: Optional[Dict[str, object]] + + metadata: Optional[Dict[str, object]] + + outputs: Optional[Dict[str, object]] + + source_run_id: Optional[str] + + split: Union[SequenceNotStr[str], str, None] + + use_legacy_message_format: bool + """Use Legacy Message Format for LLM runs""" + + use_source_run_attachments: SequenceNotStr[str] + + use_source_run_io: bool diff --git a/python/langsmith_api/types/example_delete_all_params.py b/python/langsmith_api/types/example_delete_all_params.py new file mode 100644 index 000000000..4962832ed --- /dev/null +++ b/python/langsmith_api/types/example_delete_all_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ExampleDeleteAllParams"] + + +class ExampleDeleteAllParams(TypedDict, total=False): + example_ids: Required[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/example_list_params.py b/python/langsmith_api/types/example_list_params.py new file mode 100644 index 000000000..c0718f913 --- /dev/null +++ b/python/langsmith_api/types/example_list_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .example_select import ExampleSelect + +__all__ = ["ExampleListParams"] + + +class ExampleListParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + as_of: Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ + + dataset: Optional[str] + + descending: Optional[bool] + + filter: Optional[str] + + full_text_contains: Optional[SequenceNotStr[str]] + + limit: int + + metadata: Optional[str] + + offset: int + + order: Literal["recent", "random", "recently_created", "id"] + + random_seed: Optional[float] + + select: List[ExampleSelect] + + splits: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/example_retrieve_count_params.py b/python/langsmith_api/types/example_retrieve_count_params.py new file mode 100644 index 000000000..1661adf5b --- /dev/null +++ b/python/langsmith_api/types/example_retrieve_count_params.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["ExampleRetrieveCountParams"] + + +class ExampleRetrieveCountParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + as_of: Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ + + dataset: Optional[str] + + filter: Optional[str] + + full_text_contains: Optional[SequenceNotStr[str]] + + metadata: Optional[str] + + splits: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/example_retrieve_count_response.py b/python/langsmith_api/types/example_retrieve_count_response.py new file mode 100644 index 000000000..4f6509785 --- /dev/null +++ b/python/langsmith_api/types/example_retrieve_count_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ExampleRetrieveCountResponse"] + +ExampleRetrieveCountResponse: TypeAlias = int diff --git a/python/langsmith_api/types/example_retrieve_params.py b/python/langsmith_api/types/example_retrieve_params.py new file mode 100644 index 000000000..c033dbaf3 --- /dev/null +++ b/python/langsmith_api/types/example_retrieve_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExampleRetrieveParams"] + + +class ExampleRetrieveParams(TypedDict, total=False): + as_of: Annotated[Union[Union[str, datetime], str], PropertyInfo(format="iso8601")] + """Only modifications made on or before this time are included. + + If None, the latest version of the dataset is used. + """ + + dataset: Optional[str] diff --git a/python/langsmith_api/types/example_select.py b/python/langsmith_api/types/example_select.py new file mode 100644 index 000000000..085960a53 --- /dev/null +++ b/python/langsmith_api/types/example_select.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ExampleSelect"] + +ExampleSelect: TypeAlias = Literal[ + "id", + "created_at", + "modified_at", + "name", + "dataset_id", + "source_run_id", + "metadata", + "inputs", + "outputs", + "attachment_urls", +] diff --git a/python/langsmith_api/types/example_update_params.py b/python/langsmith_api/types/example_update_params.py new file mode 100644 index 000000000..edcdf1497 --- /dev/null +++ b/python/langsmith_api/types/example_update_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import TypedDict + +from .._types import SequenceNotStr +from .attachments_operations_param import AttachmentsOperationsParam + +__all__ = ["ExampleUpdateParams"] + + +class ExampleUpdateParams(TypedDict, total=False): + attachments_operations: Optional[AttachmentsOperationsParam] + + dataset_id: Optional[str] + + inputs: Optional[Dict[str, object]] + + metadata: Optional[Dict[str, object]] + + outputs: Optional[Dict[str, object]] + + overwrite: bool + + split: Union[SequenceNotStr[str], str, None] diff --git a/python/langsmith_api/types/example_upload_from_csv_params.py b/python/langsmith_api/types/example_upload_from_csv_params.py new file mode 100644 index 000000000..b4a76d96c --- /dev/null +++ b/python/langsmith_api/types/example_upload_from_csv_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["ExampleUploadFromCsvParams"] + + +class ExampleUploadFromCsvParams(TypedDict, total=False): + file: Required[FileTypes] + + input_keys: Required[SequenceNotStr[str]] + + metadata_keys: SequenceNotStr[str] + + output_keys: SequenceNotStr[str] diff --git a/python/langsmith_api/types/example_upload_from_csv_response.py b/python/langsmith_api/types/example_upload_from_csv_response.py new file mode 100644 index 000000000..6b759d2cd --- /dev/null +++ b/python/langsmith_api/types/example_upload_from_csv_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .example import Example + +__all__ = ["ExampleUploadFromCsvResponse"] + +ExampleUploadFromCsvResponse: TypeAlias = List[Example] diff --git a/python/langsmith_api/types/examples/__init__.py b/python/langsmith_api/types/examples/__init__.py new file mode 100644 index 000000000..b168c78ff --- /dev/null +++ b/python/langsmith_api/types/examples/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .bulk_create_params import BulkCreateParams as BulkCreateParams +from .bulk_create_response import BulkCreateResponse as BulkCreateResponse +from .bulk_patch_all_params import BulkPatchAllParams as BulkPatchAllParams +from .validate_bulk_response import ValidateBulkResponse as ValidateBulkResponse +from .example_validation_result import ExampleValidationResult as ExampleValidationResult diff --git a/python/langsmith_api/types/examples/bulk_create_params.py b/python/langsmith_api/types/examples/bulk_create_params.py new file mode 100644 index 000000000..07f791209 --- /dev/null +++ b/python/langsmith_api/types/examples/bulk_create_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["BulkCreateParams", "Body"] + + +class BulkCreateParams(TypedDict, total=False): + body: Required[Iterable[Body]] + """Schema for a batch of examples to be created.""" + + +class Body(TypedDict, total=False): + """ + Example with optional created_at to prevent duplicate versions in bulk operations. + """ + + dataset_id: Required[str] + + id: Optional[str] + + created_at: Optional[str] + + inputs: Optional[Dict[str, object]] + + metadata: Optional[Dict[str, object]] + + outputs: Optional[Dict[str, object]] + + source_run_id: Optional[str] + + split: Union[SequenceNotStr[str], str, None] + + use_legacy_message_format: bool + """Use Legacy Message Format for LLM runs""" + + use_source_run_attachments: SequenceNotStr[str] + + use_source_run_io: bool diff --git a/python/langsmith_api/types/examples/bulk_create_response.py b/python/langsmith_api/types/examples/bulk_create_response.py new file mode 100644 index 000000000..e3f5c32c1 --- /dev/null +++ b/python/langsmith_api/types/examples/bulk_create_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from ..example import Example + +__all__ = ["BulkCreateResponse"] + +BulkCreateResponse: TypeAlias = List[Example] diff --git a/python/langsmith_api/types/examples/bulk_patch_all_params.py b/python/langsmith_api/types/examples/bulk_patch_all_params.py new file mode 100644 index 000000000..d1004b792 --- /dev/null +++ b/python/langsmith_api/types/examples/bulk_patch_all_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr +from ..attachments_operations_param import AttachmentsOperationsParam + +__all__ = ["BulkPatchAllParams", "Body"] + + +class BulkPatchAllParams(TypedDict, total=False): + body: Required[Iterable[Body]] + + +class Body(TypedDict, total=False): + """Bulk update class for Example (includes example id).""" + + id: Required[str] + + attachments_operations: Optional[AttachmentsOperationsParam] + + dataset_id: Optional[str] + + inputs: Optional[Dict[str, object]] + + metadata: Optional[Dict[str, object]] + + outputs: Optional[Dict[str, object]] + + overwrite: bool + + split: Union[SequenceNotStr[str], str, None] diff --git a/python/langsmith_api/types/examples/example_validation_result.py b/python/langsmith_api/types/examples/example_validation_result.py new file mode 100644 index 000000000..4ccfbe76e --- /dev/null +++ b/python/langsmith_api/types/examples/example_validation_result.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["ExampleValidationResult"] + + +class ExampleValidationResult(BaseModel): + """ + Validation result for Example, combining fields from Create/Base/Update schemas. + """ + + id: Optional[str] = None + + created_at: Optional[datetime] = None + + dataset_id: Optional[str] = None + + inputs: Optional[Dict[str, object]] = None + + metadata: Optional[Dict[str, object]] = None + + outputs: Optional[Dict[str, object]] = None + + overwrite: Optional[bool] = None + + source_run_id: Optional[str] = None + + split: Union[List[str], str, None] = None + + use_source_run_io: Optional[bool] = None diff --git a/python/langsmith_api/types/examples/validate_bulk_response.py b/python/langsmith_api/types/examples/validate_bulk_response.py new file mode 100644 index 000000000..fc463248e --- /dev/null +++ b/python/langsmith_api/types/examples/validate_bulk_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .example_validation_result import ExampleValidationResult + +__all__ = ["ValidateBulkResponse"] + +ValidateBulkResponse: TypeAlias = List[ExampleValidationResult] diff --git a/python/langsmith_api/types/feedback/__init__.py b/python/langsmith_api/types/feedback/__init__.py new file mode 100644 index 000000000..5081df5e9 --- /dev/null +++ b/python/langsmith_api/types/feedback/__init__.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .token_list_params import TokenListParams as TokenListParams +from .token_create_params import TokenCreateParams as TokenCreateParams +from .token_list_response import TokenListResponse as TokenListResponse +from .token_update_params import TokenUpdateParams as TokenUpdateParams +from .config_delete_params import ConfigDeleteParams as ConfigDeleteParams +from .token_create_response import TokenCreateResponse as TokenCreateResponse +from .token_retrieve_params import TokenRetrieveParams as TokenRetrieveParams +from .feedback_ingest_token_schema import FeedbackIngestTokenSchema as FeedbackIngestTokenSchema +from .feedback_ingest_token_create_schema_param import ( + FeedbackIngestTokenCreateSchemaParam as FeedbackIngestTokenCreateSchemaParam, +) diff --git a/python/langsmith_api/types/feedback/config_delete_params.py b/python/langsmith_api/types/feedback/config_delete_params.py new file mode 100644 index 000000000..9cdad1909 --- /dev/null +++ b/python/langsmith_api/types/feedback/config_delete_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ConfigDeleteParams"] + + +class ConfigDeleteParams(TypedDict, total=False): + feedback_key: Required[str] diff --git a/python/langsmith_api/types/feedback/feedback_ingest_token_create_schema_param.py b/python/langsmith_api/types/feedback/feedback_ingest_token_create_schema_param.py new file mode 100644 index 000000000..6074dc356 --- /dev/null +++ b/python/langsmith_api/types/feedback/feedback_ingest_token_create_schema_param.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from ..timedelta_input_param import TimedeltaInputParam + +__all__ = ["FeedbackIngestTokenCreateSchemaParam", "FeedbackConfig", "FeedbackConfigCategory"] + + +class FeedbackConfigCategory(TypedDict, total=False): + """Specific value and label pair for feedback""" + + value: Required[float] + + label: Optional[str] + + +class FeedbackConfig(TypedDict, total=False): + type: Required[Literal["continuous", "categorical", "freeform"]] + """Enum for feedback types.""" + + categories: Optional[Iterable[FeedbackConfigCategory]] + + max: Optional[float] + + min: Optional[float] + + +class FeedbackIngestTokenCreateSchemaParam(TypedDict, total=False): + """Feedback ingest token create schema.""" + + feedback_key: Required[str] + + run_id: Required[str] + + expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + expires_in: Optional[TimedeltaInputParam] + """Timedelta input.""" + + feedback_config: Optional[FeedbackConfig] diff --git a/python/langsmith_api/types/feedback/feedback_ingest_token_schema.py b/python/langsmith_api/types/feedback/feedback_ingest_token_schema.py new file mode 100644 index 000000000..f5de6f1d5 --- /dev/null +++ b/python/langsmith_api/types/feedback/feedback_ingest_token_schema.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["FeedbackIngestTokenSchema"] + + +class FeedbackIngestTokenSchema(BaseModel): + """Feedback ingest token schema.""" + + id: str + + expires_at: datetime + + feedback_key: str + + url: str diff --git a/python/langsmith_api/types/feedback/token_create_params.py b/python/langsmith_api/types/feedback/token_create_params.py new file mode 100644 index 000000000..56675f6f0 --- /dev/null +++ b/python/langsmith_api/types/feedback/token_create_params.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from ..timedelta_input_param import TimedeltaInputParam +from .feedback_ingest_token_create_schema_param import FeedbackIngestTokenCreateSchemaParam + +__all__ = [ + "TokenCreateParams", + "FeedbackIngestTokenCreateSchema", + "FeedbackIngestTokenCreateSchemaFeedbackConfig", + "FeedbackIngestTokenCreateSchemaFeedbackConfigCategory", + "Variant1", +] + + +class FeedbackIngestTokenCreateSchema(TypedDict, total=False): + feedback_key: Required[str] + + run_id: Required[str] + + expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + expires_in: Optional[TimedeltaInputParam] + """Timedelta input.""" + + feedback_config: Optional[FeedbackIngestTokenCreateSchemaFeedbackConfig] + + +class FeedbackIngestTokenCreateSchemaFeedbackConfigCategory(TypedDict, total=False): + """Specific value and label pair for feedback""" + + value: Required[float] + + label: Optional[str] + + +class FeedbackIngestTokenCreateSchemaFeedbackConfig(TypedDict, total=False): + type: Required[Literal["continuous", "categorical", "freeform"]] + """Enum for feedback types.""" + + categories: Optional[Iterable[FeedbackIngestTokenCreateSchemaFeedbackConfigCategory]] + + max: Optional[float] + + min: Optional[float] + + +class Variant1(TypedDict, total=False): + body: Required[Iterable[FeedbackIngestTokenCreateSchemaParam]] + + +TokenCreateParams: TypeAlias = Union[FeedbackIngestTokenCreateSchema, Variant1] diff --git a/python/langsmith_api/types/feedback/token_create_response.py b/python/langsmith_api/types/feedback/token_create_response.py new file mode 100644 index 000000000..7641d7b8f --- /dev/null +++ b/python/langsmith_api/types/feedback/token_create_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import TypeAlias + +from .feedback_ingest_token_schema import FeedbackIngestTokenSchema + +__all__ = ["TokenCreateResponse"] + +TokenCreateResponse: TypeAlias = Union[FeedbackIngestTokenSchema, List[FeedbackIngestTokenSchema]] diff --git a/python/langsmith_api/types/feedback/token_list_params.py b/python/langsmith_api/types/feedback/token_list_params.py new file mode 100644 index 000000000..f136fa6c8 --- /dev/null +++ b/python/langsmith_api/types/feedback/token_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["TokenListParams"] + + +class TokenListParams(TypedDict, total=False): + run_id: Required[str] diff --git a/python/langsmith_api/types/feedback/token_list_response.py b/python/langsmith_api/types/feedback/token_list_response.py new file mode 100644 index 000000000..07415e4b4 --- /dev/null +++ b/python/langsmith_api/types/feedback/token_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .feedback_ingest_token_schema import FeedbackIngestTokenSchema + +__all__ = ["TokenListResponse"] + +TokenListResponse: TypeAlias = List[FeedbackIngestTokenSchema] diff --git a/python/langsmith_api/types/feedback/token_retrieve_params.py b/python/langsmith_api/types/feedback/token_retrieve_params.py new file mode 100644 index 000000000..acea5ae25 --- /dev/null +++ b/python/langsmith_api/types/feedback/token_retrieve_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import TypedDict + +__all__ = ["TokenRetrieveParams"] + + +class TokenRetrieveParams(TypedDict, total=False): + comment: Optional[str] + + correction: Optional[str] + + score: Union[float, bool, None] + + value: Union[float, bool, str, None] diff --git a/python/langsmith_api/types/feedback/token_update_params.py b/python/langsmith_api/types/feedback/token_update_params.py new file mode 100644 index 000000000..738fac9c9 --- /dev/null +++ b/python/langsmith_api/types/feedback/token_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import TypedDict + +__all__ = ["TokenUpdateParams"] + + +class TokenUpdateParams(TypedDict, total=False): + comment: Optional[str] + + correction: Union[Dict[str, object], str, None] + + metadata: Optional[Dict[str, object]] + + score: Union[float, bool, None] + + value: Union[float, bool, str, None] diff --git a/python/langsmith_api/types/feedback_create_params.py b/python/langsmith_api/types/feedback_create_params.py new file mode 100644 index 000000000..41ff9d3d4 --- /dev/null +++ b/python/langsmith_api/types/feedback_create_params.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo +from .api_feedback_source_param import APIFeedbackSourceParam +from .app_feedback_source_param import AppFeedbackSourceParam +from .model_feedback_source_param import ModelFeedbackSourceParam +from .auto_eval_feedback_source_param import AutoEvalFeedbackSourceParam + +__all__ = ["FeedbackCreateParams", "FeedbackConfig", "FeedbackConfigCategory", "FeedbackSource"] + + +class FeedbackCreateParams(TypedDict, total=False): + key: Required[str] + + id: str + + comment: Optional[str] + + comparative_experiment_id: Optional[str] + + correction: Union[Dict[str, object], str, None] + + created_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + error: Optional[bool] + + feedback_config: Optional[FeedbackConfig] + + feedback_group_id: Optional[str] + + feedback_source: Optional[FeedbackSource] + """Feedback from the LangChainPlus App.""" + + modified_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + run_id: Optional[str] + + score: Union[float, bool, None] + + session_id: Optional[str] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + trace_id: Optional[str] + + value: Union[float, bool, str, Dict[str, object], None] + + +class FeedbackConfigCategory(TypedDict, total=False): + """Specific value and label pair for feedback""" + + value: Required[float] + + label: Optional[str] + + +class FeedbackConfig(TypedDict, total=False): + type: Required[Literal["continuous", "categorical", "freeform"]] + """Enum for feedback types.""" + + categories: Optional[Iterable[FeedbackConfigCategory]] + + max: Optional[float] + + min: Optional[float] + + +FeedbackSource: TypeAlias = Union[ + AppFeedbackSourceParam, APIFeedbackSourceParam, ModelFeedbackSourceParam, AutoEvalFeedbackSourceParam +] diff --git a/python/langsmith_api/types/feedback_level.py b/python/langsmith_api/types/feedback_level.py new file mode 100644 index 000000000..c713c9fb0 --- /dev/null +++ b/python/langsmith_api/types/feedback_level.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["FeedbackLevel"] + +FeedbackLevel: TypeAlias = Literal["run", "session"] diff --git a/python/langsmith_api/types/feedback_list_params.py b/python/langsmith_api/types/feedback_list_params.py new file mode 100644 index 000000000..095a912b4 --- /dev/null +++ b/python/langsmith_api/types/feedback_list_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .source_type import SourceType +from .feedback_level import FeedbackLevel + +__all__ = ["FeedbackListParams"] + + +class FeedbackListParams(TypedDict, total=False): + comparative_experiment_id: Optional[str] + + has_comment: Optional[bool] + + has_score: Optional[bool] + + include_user_names: Optional[bool] + + key: Optional[SequenceNotStr[str]] + + level: Optional[FeedbackLevel] + """Enum for feedback levels.""" + + limit: int + + max_created_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + min_created_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + offset: int + + run: Union[SequenceNotStr[str], str, None] + + session: Union[SequenceNotStr[str], str, None] + + source: Optional[List[SourceType]] + + user: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/feedback_retrieve_params.py b/python/langsmith_api/types/feedback_retrieve_params.py new file mode 100644 index 000000000..d4d0bc937 --- /dev/null +++ b/python/langsmith_api/types/feedback_retrieve_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["FeedbackRetrieveParams"] + + +class FeedbackRetrieveParams(TypedDict, total=False): + include_user_names: Optional[bool] diff --git a/python/langsmith_api/types/feedback_schema.py b/python/langsmith_api/types/feedback_schema.py new file mode 100644 index 000000000..c7e95417b --- /dev/null +++ b/python/langsmith_api/types/feedback_schema.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["FeedbackSchema", "FeedbackSource"] + + +class FeedbackSource(BaseModel): + """The feedback source loaded from the database.""" + + ls_user_id: Optional[str] = None + + metadata: Optional[Dict[str, object]] = None + + type: Optional[str] = None + + user_id: Optional[str] = None + + user_name: Optional[str] = None + + +class FeedbackSchema(BaseModel): + """Schema for getting feedback.""" + + id: str + + key: str + + comment: Optional[str] = None + + comparative_experiment_id: Optional[str] = None + + correction: Union[Dict[str, object], str, None] = None + + created_at: Optional[datetime] = None + + extra: Optional[Dict[str, object]] = None + + feedback_group_id: Optional[str] = None + + feedback_source: Optional[FeedbackSource] = None + """The feedback source loaded from the database.""" + + feedback_thread_id: Optional[str] = None + + is_root: Optional[bool] = None + + modified_at: Optional[datetime] = None + + run_id: Optional[str] = None + + score: Union[float, bool, None] = None + + session_id: Optional[str] = None + + start_time: Optional[datetime] = None + + trace_id: Optional[str] = None + + value: Union[float, bool, str, Dict[str, object], None] = None diff --git a/python/langsmith_api/types/feedback_update_params.py b/python/langsmith_api/types/feedback_update_params.py new file mode 100644 index 000000000..62b74994f --- /dev/null +++ b/python/langsmith_api/types/feedback_update_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FeedbackUpdateParams", "FeedbackConfig", "FeedbackConfigCategory"] + + +class FeedbackUpdateParams(TypedDict, total=False): + comment: Optional[str] + + correction: Union[Dict[str, object], str, None] + + feedback_config: Optional[FeedbackConfig] + + score: Union[float, bool, None] + + value: Union[float, bool, str, Dict[str, object], None] + + +class FeedbackConfigCategory(TypedDict, total=False): + """Specific value and label pair for feedback""" + + value: Required[float] + + label: Optional[str] + + +class FeedbackConfig(TypedDict, total=False): + type: Required[Literal["continuous", "categorical", "freeform"]] + """Enum for feedback types.""" + + categories: Optional[Iterable[FeedbackConfigCategory]] + + max: Optional[float] + + min: Optional[float] diff --git a/python/langsmith_api/types/get_repo_response.py b/python/langsmith_api/types/get_repo_response.py new file mode 100644 index 000000000..ada4c8203 --- /dev/null +++ b/python/langsmith_api/types/get_repo_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel +from .repo_with_lookups import RepoWithLookups + +__all__ = ["GetRepoResponse"] + + +class GetRepoResponse(BaseModel): + repo: RepoWithLookups + """All database fields for repos, plus helpful computed fields.""" diff --git a/python/langsmith_api/types/info_list_response.py b/python/langsmith_api/types/info_list_response.py new file mode 100644 index 000000000..10e52d140 --- /dev/null +++ b/python/langsmith_api/types/info_list_response.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["InfoListResponse", "BatchIngestConfig", "CustomerInfo"] + + +class BatchIngestConfig(BaseModel): + """Batch ingest config.""" + + scale_down_nempty_trigger: Optional[int] = None + + scale_up_nthreads_limit: Optional[int] = None + + scale_up_qsize_trigger: Optional[int] = None + + size_limit: Optional[int] = None + + size_limit_bytes: Optional[int] = None + + use_multipart_endpoint: Optional[bool] = None + + +class CustomerInfo(BaseModel): + """Customer info.""" + + customer_id: str + + customer_name: str + + +class InfoListResponse(BaseModel): + """The LangSmith server info.""" + + version: str + + batch_ingest_config: Optional[BatchIngestConfig] = None + """Batch ingest config.""" + + customer_info: Optional[CustomerInfo] = None + """Customer info.""" + + git_sha: Optional[str] = None + + instance_flags: Optional[Dict[str, object]] = None + + license_expiration_time: Optional[datetime] = None diff --git a/python/langsmith_api/types/missing_param.py b/python/langsmith_api/types/missing_param.py new file mode 100644 index 000000000..eb346a12a --- /dev/null +++ b/python/langsmith_api/types/missing_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["MissingParam"] + + +class MissingParam(TypedDict, total=False): + _missing: Required[Annotated[Literal["__missing__"], PropertyInfo(alias="__missing__")]] diff --git a/python/langsmith_api/types/model_feedback_source_param.py b/python/langsmith_api/types/model_feedback_source_param.py new file mode 100644 index 000000000..bbcc62856 --- /dev/null +++ b/python/langsmith_api/types/model_feedback_source_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["ModelFeedbackSourceParam"] + + +class ModelFeedbackSourceParam(TypedDict, total=False): + """Model feedback source.""" + + metadata: Optional[Dict[str, object]] + + type: Literal["model"] diff --git a/python/langsmith_api/types/public/__init__.py b/python/langsmith_api/types/public/__init__.py new file mode 100644 index 000000000..15a0050f0 --- /dev/null +++ b/python/langsmith_api/types/public/__init__.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .dataset_list_params import DatasetListParams as DatasetListParams +from .dataset_list_response import DatasetListResponse as DatasetListResponse +from .dataset_list_feedback_params import DatasetListFeedbackParams as DatasetListFeedbackParams +from .dataset_list_sessions_params import DatasetListSessionsParams as DatasetListSessionsParams +from .dataset_list_comparative_params import DatasetListComparativeParams as DatasetListComparativeParams +from .dataset_list_comparative_response import DatasetListComparativeResponse as DatasetListComparativeResponse +from .dataset_retrieve_sessions_bulk_params import ( + DatasetRetrieveSessionsBulkParams as DatasetRetrieveSessionsBulkParams, +) +from .dataset_retrieve_sessions_bulk_response import ( + DatasetRetrieveSessionsBulkResponse as DatasetRetrieveSessionsBulkResponse, +) diff --git a/python/langsmith_api/types/public/dataset_list_comparative_params.py b/python/langsmith_api/types/public/dataset_list_comparative_params.py new file mode 100644 index 000000000..9acf2642c --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_comparative_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ..datasets.sort_by_comparative_experiment_column import SortByComparativeExperimentColumn + +__all__ = ["DatasetListComparativeParams"] + + +class DatasetListComparativeParams(TypedDict, total=False): + limit: int + + name: Optional[str] + + name_contains: Optional[str] + + offset: int + + sort_by: SortByComparativeExperimentColumn + """Enum for available comparative experiment columns to sort by.""" + + sort_by_desc: bool diff --git a/python/langsmith_api/types/public/dataset_list_comparative_response.py b/python/langsmith_api/types/public/dataset_list_comparative_response.py new file mode 100644 index 000000000..6e3b2b0e9 --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_comparative_response.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from ..._models import BaseModel +from ..datasets.simple_experiment_info import SimpleExperimentInfo + +__all__ = ["DatasetListComparativeResponse"] + + +class DatasetListComparativeResponse(BaseModel): + """Publicly-shared ComparativeExperiment schema.""" + + id: str + + created_at: datetime + + experiments_info: List[SimpleExperimentInfo] + + modified_at: datetime + + description: Optional[str] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, object]] = None + + name: Optional[str] = None diff --git a/python/langsmith_api/types/public/dataset_list_feedback_params.py b/python/langsmith_api/types/public/dataset_list_feedback_params.py new file mode 100644 index 000000000..8f853f519 --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_feedback_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr +from ..source_type import SourceType +from ..feedback_level import FeedbackLevel + +__all__ = ["DatasetListFeedbackParams"] + + +class DatasetListFeedbackParams(TypedDict, total=False): + has_comment: Optional[bool] + + has_score: Optional[bool] + + key: Optional[SequenceNotStr[str]] + + level: Optional[FeedbackLevel] + """Enum for feedback levels.""" + + limit: int + + offset: int + + run: Optional[SequenceNotStr[str]] + + session: Optional[SequenceNotStr[str]] + + source: Optional[List[SourceType]] + + user: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/public/dataset_list_params.py b/python/langsmith_api/types/public/dataset_list_params.py new file mode 100644 index 000000000..3e9bf9114 --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ..sort_by_dataset_column import SortByDatasetColumn + +__all__ = ["DatasetListParams"] + + +class DatasetListParams(TypedDict, total=False): + limit: int + + offset: int + + sort_by: SortByDatasetColumn + """Enum for available dataset columns to sort by.""" + + sort_by_desc: bool diff --git a/python/langsmith_api/types/public/dataset_list_response.py b/python/langsmith_api/types/public/dataset_list_response.py new file mode 100644 index 000000000..afdc94e8f --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_response.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from ..._models import BaseModel +from ..data_type import DataType +from ..dataset_transformation import DatasetTransformation + +__all__ = ["DatasetListResponse"] + + +class DatasetListResponse(BaseModel): + """Public schema for datasets. + + Doesn't currently include session counts/stats + since public test project sharing is not yet shipped + """ + + id: str + + example_count: int + + name: str + + created_at: Optional[datetime] = None + + data_type: Optional[DataType] = None + """Enum for dataset data types.""" + + description: Optional[str] = None + + externally_managed: Optional[bool] = None + + inputs_schema_definition: Optional[Dict[str, object]] = None + + outputs_schema_definition: Optional[Dict[str, object]] = None + + transformations: Optional[List[DatasetTransformation]] = None diff --git a/python/langsmith_api/types/public/dataset_list_sessions_params.py b/python/langsmith_api/types/public/dataset_list_sessions_params.py new file mode 100644 index 000000000..70930fc43 --- /dev/null +++ b/python/langsmith_api/types/public/dataset_list_sessions_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ..._types import SequenceNotStr +from ..session_sortable_columns import SessionSortableColumns + +__all__ = ["DatasetListSessionsParams"] + + +class DatasetListSessionsParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + dataset_version: Optional[str] + + facets: bool + + limit: int + + name: Optional[str] + + name_contains: Optional[str] + + offset: int + + sort_by: SessionSortableColumns + + sort_by_desc: bool + + sort_by_feedback_key: Optional[str] + + accept: str diff --git a/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_params.py b/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_params.py new file mode 100644 index 000000000..8567687c9 --- /dev/null +++ b/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["DatasetRetrieveSessionsBulkParams"] + + +class DatasetRetrieveSessionsBulkParams(TypedDict, total=False): + share_tokens: Required[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_response.py b/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_response.py new file mode 100644 index 000000000..417dcf04e --- /dev/null +++ b/python/langsmith_api/types/public/dataset_retrieve_sessions_bulk_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from ..tracer_session import TracerSession + +__all__ = ["DatasetRetrieveSessionsBulkResponse"] + +DatasetRetrieveSessionsBulkResponse: TypeAlias = List[TracerSession] diff --git a/python/langsmith_api/types/public_retrieve_feedbacks_params.py b/python/langsmith_api/types/public_retrieve_feedbacks_params.py new file mode 100644 index 000000000..7e258ee98 --- /dev/null +++ b/python/langsmith_api/types/public_retrieve_feedbacks_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import TypedDict + +from .._types import SequenceNotStr +from .source_type import SourceType +from .feedback_level import FeedbackLevel + +__all__ = ["PublicRetrieveFeedbacksParams"] + + +class PublicRetrieveFeedbacksParams(TypedDict, total=False): + has_comment: Optional[bool] + + has_score: Optional[bool] + + key: Optional[SequenceNotStr[str]] + + level: Optional[FeedbackLevel] + """Enum for feedback levels.""" + + limit: int + + offset: int + + run: Optional[SequenceNotStr[str]] + + session: Optional[SequenceNotStr[str]] + + source: Optional[List[SourceType]] + + user: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/repo_create_params.py b/python/langsmith_api/types/repo_create_params.py new file mode 100644 index 000000000..8b5ba929b --- /dev/null +++ b/python/langsmith_api/types/repo_create_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RepoCreateParams"] + + +class RepoCreateParams(TypedDict, total=False): + is_public: Required[bool] + + repo_handle: Required[str] + + description: Optional[str] + + readme: Optional[str] + + repo_type: Literal["prompt", "file", "agent", "skill"] + + restricted_mode: Optional[bool] + + source: Optional[Literal["internal", "external"]] + + tags: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/repo_list_params.py b/python/langsmith_api/types/repo_list_params.py new file mode 100644 index 000000000..05d1ea4a4 --- /dev/null +++ b/python/langsmith_api/types/repo_list_params.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["RepoListParams"] + + +class RepoListParams(TypedDict, total=False): + has_commits: Optional[bool] + + is_archived: Optional[Literal["true", "allow", "false"]] + + is_public: Optional[Literal["true", "false"]] + + limit: int + + offset: int + + query: Optional[str] + + single_repo_type: Annotated[Optional[Literal["prompt", "file", "agent", "skill"]], PropertyInfo(alias="repo_type")] + + repo_types: Optional[List[Literal["prompt", "file", "agent", "skill"]]] + + sort_direction: Optional[Literal["asc", "desc"]] + + sort_field: Optional[Literal["num_likes", "num_downloads", "num_views", "updated_at", "relevance"]] + + source: Optional[Literal["internal", "external"]] + + tag_value_id: Optional[SequenceNotStr[str]] + + tags: Optional[SequenceNotStr[str]] + + tenant_handle: Optional[str] + + tenant_id: Optional[str] + + upstream_repo_handle: Optional[str] + + upstream_repo_owner: Optional[str] + + with_latest_manifest: bool diff --git a/python/langsmith_api/types/repo_update_params.py b/python/langsmith_api/types/repo_update_params.py new file mode 100644 index 000000000..fa646f9d8 --- /dev/null +++ b/python/langsmith_api/types/repo_update_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RepoUpdateParams"] + + +class RepoUpdateParams(TypedDict, total=False): + owner: Required[str] + + description: Optional[str] + + is_archived: Optional[bool] + + is_public: Optional[bool] + + readme: Optional[str] + + restricted_mode: Optional[bool] + + tags: Optional[SequenceNotStr[str]] diff --git a/python/langsmith_api/types/repo_with_lookups.py b/python/langsmith_api/types/repo_with_lookups.py new file mode 100644 index 000000000..dce371a53 --- /dev/null +++ b/python/langsmith_api/types/repo_with_lookups.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .commit_manifest_response import CommitManifestResponse + +__all__ = ["RepoWithLookups"] + + +class RepoWithLookups(BaseModel): + """All database fields for repos, plus helpful computed fields.""" + + id: str + + created_at: datetime + + full_name: str + + is_archived: bool + + is_public: bool + + num_commits: int + + num_downloads: int + + num_likes: int + + num_views: int + + owner: Optional[str] = None + + repo_handle: str + + repo_type: Literal["prompt", "file", "agent", "skill"] + + tags: List[str] + + tenant_id: str + + updated_at: datetime + + commit_tags: Optional[List[str]] = None + + created_by: Optional[str] = None + + description: Optional[str] = None + + last_commit_hash: Optional[str] = None + + latest_commit_manifest: Optional[CommitManifestResponse] = None + """Response model for get_commit_manifest.""" + + liked_by_auth_user: Optional[bool] = None + + original_repo_full_name: Optional[str] = None + + original_repo_id: Optional[str] = None + + readme: Optional[str] = None + + restricted_mode: Optional[bool] = None + + source: Optional[Literal["internal", "external"]] = None + + upstream_repo_full_name: Optional[str] = None + + upstream_repo_id: Optional[str] = None diff --git a/python/langsmith_api/types/repos/__init__.py b/python/langsmith_api/types/repos/__init__.py new file mode 100644 index 000000000..f0b467245 --- /dev/null +++ b/python/langsmith_api/types/repos/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .directory_list_params import DirectoryListParams as DirectoryListParams +from .directory_commit_params import DirectoryCommitParams as DirectoryCommitParams +from .directory_list_response import DirectoryListResponse as DirectoryListResponse +from .directory_commit_response import DirectoryCommitResponse as DirectoryCommitResponse diff --git a/python/langsmith_api/types/repos/directory_commit_params.py b/python/langsmith_api/types/repos/directory_commit_params.py new file mode 100644 index 000000000..dd1a3bc2a --- /dev/null +++ b/python/langsmith_api/types/repos/directory_commit_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, TypedDict + +__all__ = ["DirectoryCommitParams"] + + +class DirectoryCommitParams(TypedDict, total=False): + owner: Required[str] + + files: Dict[str, object] + """ + Files maps path to an Entry (object = create/update/link, null = delete/unlink). + """ + + parent_commit: str diff --git a/python/langsmith_api/types/repos/directory_commit_response.py b/python/langsmith_api/types/repos/directory_commit_response.py new file mode 100644 index 000000000..eba1bcf91 --- /dev/null +++ b/python/langsmith_api/types/repos/directory_commit_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["DirectoryCommitResponse", "Commit"] + + +class Commit(BaseModel): + id: Optional[str] = None + + commit_hash: Optional[str] = None + + created_at: Optional[str] = None + + +class DirectoryCommitResponse(BaseModel): + commit: Optional[Commit] = None diff --git a/python/langsmith_api/types/repos/directory_list_params.py b/python/langsmith_api/types/repos/directory_list_params.py new file mode 100644 index 000000000..906c84e7c --- /dev/null +++ b/python/langsmith_api/types/repos/directory_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["DirectoryListParams"] + + +class DirectoryListParams(TypedDict, total=False): + owner: Required[str] + + commit: str + """Commit hash/tag to resolve (defaults to latest)""" diff --git a/python/langsmith_api/types/repos/directory_list_response.py b/python/langsmith_api/types/repos/directory_list_response.py new file mode 100644 index 000000000..9ab33d5b6 --- /dev/null +++ b/python/langsmith_api/types/repos/directory_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from ..._models import BaseModel + +__all__ = ["DirectoryListResponse"] + + +class DirectoryListResponse(BaseModel): + commit_hash: Optional[str] = None + + commit_id: Optional[str] = None + + files: Optional[Dict[str, object]] = None diff --git a/python/langsmith_api/types/run_create_params.py b/python/langsmith_api/types/run_create_params.py new file mode 100644 index 000000000..cea485708 --- /dev/null +++ b/python/langsmith_api/types/run_create_params.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RunCreateParams"] + + +class RunCreateParams(TypedDict, total=False): + id: str + + dotted_order: str + + end_time: str + + error: str + + events: Iterable[Dict[str, object]] + + extra: Dict[str, object] + + input_attachments: Dict[str, object] + + inputs: Dict[str, object] + + name: str + + output_attachments: Dict[str, object] + + outputs: Dict[str, object] + + parent_run_id: str + + reference_example_id: str + + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] + + serialized: Dict[str, object] + + session_id: str + + session_name: str + + start_time: str + + status: str + + tags: SequenceNotStr[str] + + trace_id: str diff --git a/python/langsmith_api/types/run_create_response.py b/python/langsmith_api/types/run_create_response.py new file mode 100644 index 000000000..27ae46e9f --- /dev/null +++ b/python/langsmith_api/types/run_create_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["RunCreateResponse", "RunCreateResponseItem"] + + +class RunCreateResponseItem: + pass + + +RunCreateResponse: TypeAlias = Dict[str, RunCreateResponseItem] diff --git a/python/langsmith_api/types/run_ingest_batch_params.py b/python/langsmith_api/types/run_ingest_batch_params.py new file mode 100644 index 000000000..689e557ce --- /dev/null +++ b/python/langsmith_api/types/run_ingest_batch_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import TypedDict + +from .run_param import RunParam + +__all__ = ["RunIngestBatchParams"] + + +class RunIngestBatchParams(TypedDict, total=False): + patch: Iterable[RunParam] + + post: Iterable[RunParam] diff --git a/python/langsmith_api/types/run_ingest_batch_response.py b/python/langsmith_api/types/run_ingest_batch_response.py new file mode 100644 index 000000000..733a114bc --- /dev/null +++ b/python/langsmith_api/types/run_ingest_batch_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["RunIngestBatchResponse", "RunIngestBatchResponseItem"] + + +class RunIngestBatchResponseItem: + pass + + +RunIngestBatchResponse: TypeAlias = Dict[str, RunIngestBatchResponseItem] diff --git a/python/langsmith_api/types/run_param.py b/python/langsmith_api/types/run_param.py new file mode 100644 index 000000000..5b5892c10 --- /dev/null +++ b/python/langsmith_api/types/run_param.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RunParam"] + + +class RunParam(TypedDict, total=False): + id: str + + dotted_order: str + + end_time: str + + error: str + + events: Iterable[Dict[str, object]] + + extra: Dict[str, object] + + input_attachments: Dict[str, object] + + inputs: Dict[str, object] + + name: str + + output_attachments: Dict[str, object] + + outputs: Dict[str, object] + + parent_run_id: str + + reference_example_id: str + + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] + + serialized: Dict[str, object] + + session_id: str + + session_name: str + + start_time: str + + status: str + + tags: SequenceNotStr[str] + + trace_id: str diff --git a/python/langsmith_api/types/run_query_params.py b/python/langsmith_api/types/run_query_params.py new file mode 100644 index 000000000..f9787f5bf --- /dev/null +++ b/python/langsmith_api/types/run_query_params.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .run_type_enum import RunTypeEnum +from .runs_filter_data_source_type_enum import RunsFilterDataSourceTypeEnum + +__all__ = ["RunQueryParams"] + + +class RunQueryParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + cursor: Optional[str] + + data_source_type: Optional[RunsFilterDataSourceTypeEnum] + """Enum for run data source types.""" + + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + error: Optional[bool] + + execution_order: Optional[int] + + filter: Optional[str] + + is_root: Optional[bool] + + limit: int + + order: Literal["asc", "desc"] + """Enum for run start date order.""" + + parent_run: Optional[str] + + query: Optional[str] + + reference_example: Optional[SequenceNotStr[str]] + + run_type: Optional[RunTypeEnum] + """Enum for run types.""" + + search_filter: Optional[str] + + select: List[ + Literal[ + "id", + "name", + "run_type", + "start_time", + "end_time", + "status", + "error", + "extra", + "events", + "inputs", + "inputs_preview", + "inputs_s3_urls", + "inputs_or_signed_url", + "outputs", + "outputs_preview", + "outputs_s3_urls", + "outputs_or_signed_url", + "s3_urls", + "error_or_signed_url", + "events_or_signed_url", + "extra_or_signed_url", + "serialized_or_signed_url", + "parent_run_id", + "manifest_id", + "manifest_s3_id", + "manifest", + "session_id", + "serialized", + "reference_example_id", + "reference_dataset_id", + "total_tokens", + "prompt_tokens", + "prompt_token_details", + "completion_tokens", + "completion_token_details", + "total_cost", + "prompt_cost", + "prompt_cost_details", + "completion_cost", + "completion_cost_details", + "price_model_id", + "first_token_time", + "trace_id", + "dotted_order", + "last_queued_at", + "feedback_stats", + "child_run_ids", + "parent_run_ids", + "tags", + "in_dataset", + "app_path", + "share_token", + "trace_tier", + "trace_first_received_at", + "ttl_seconds", + "trace_upgrade", + "thread_id", + "trace_min_max_start_time", + "messages", + "inserted_at", + ] + ] + + session: Optional[SequenceNotStr[str]] + + skip_pagination: Optional[bool] + + skip_prev_cursor: bool + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + trace: Optional[str] + + trace_filter: Optional[str] + + tree_filter: Optional[str] + + use_experimental_search: bool diff --git a/python/langsmith_api/types/run_query_response.py b/python/langsmith_api/types/run_query_response.py new file mode 100644 index 000000000..a848eb79e --- /dev/null +++ b/python/langsmith_api/types/run_query_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from .._models import BaseModel +from .run_schema import RunSchema + +__all__ = ["RunQueryResponse"] + + +class RunQueryResponse(BaseModel): + cursors: Dict[str, Optional[str]] + + runs: List[RunSchema] + + parsed_query: Optional[str] = None + + search_cursors: Optional[Dict[str, Optional[object]]] = None diff --git a/python/langsmith_api/types/run_retrieve_params.py b/python/langsmith_api/types/run_retrieve_params.py new file mode 100644 index 000000000..ac77f76b3 --- /dev/null +++ b/python/langsmith_api/types/run_retrieve_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["RunRetrieveParams"] + + +class RunRetrieveParams(TypedDict, total=False): + exclude_s3_stored_attributes: bool + + exclude_serialized: bool + + include_messages: bool + + session_id: Optional[str] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] diff --git a/python/langsmith_api/types/run_schema.py b/python/langsmith_api/types/run_schema.py new file mode 100644 index 000000000..9db0b9f02 --- /dev/null +++ b/python/langsmith_api/types/run_schema.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .run_type_enum import RunTypeEnum + +__all__ = ["RunSchema"] + + +class RunSchema(BaseModel): + """Run schema.""" + + id: str + + app_path: str + + dotted_order: str + + name: str + + run_type: RunTypeEnum + """Enum for run types.""" + + session_id: str + + status: str + + trace_id: str + + child_run_ids: Optional[List[str]] = None + + completion_cost: Optional[str] = None + + completion_cost_details: Optional[Dict[str, str]] = None + + completion_token_details: Optional[Dict[str, int]] = None + + completion_tokens: Optional[int] = None + + direct_child_run_ids: Optional[List[str]] = None + + end_time: Optional[datetime] = None + + error: Optional[str] = None + + events: Optional[List[Dict[str, object]]] = None + + execution_order: Optional[int] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, Dict[str, object]]] = None + + first_token_time: Optional[datetime] = None + + in_dataset: Optional[bool] = None + + inputs: Optional[Dict[str, object]] = None + + inputs_preview: Optional[str] = None + + inputs_s3_urls: Optional[Dict[str, object]] = None + + last_queued_at: Optional[datetime] = None + + manifest_id: Optional[str] = None + + manifest_s3_id: Optional[str] = None + + messages: Optional[List[Dict[str, object]]] = None + + outputs: Optional[Dict[str, object]] = None + + outputs_preview: Optional[str] = None + + outputs_s3_urls: Optional[Dict[str, object]] = None + + parent_run_id: Optional[str] = None + + parent_run_ids: Optional[List[str]] = None + + price_model_id: Optional[str] = None + + prompt_cost: Optional[str] = None + + prompt_cost_details: Optional[Dict[str, str]] = None + + prompt_token_details: Optional[Dict[str, int]] = None + + prompt_tokens: Optional[int] = None + + reference_dataset_id: Optional[str] = None + + reference_example_id: Optional[str] = None + + s3_urls: Optional[Dict[str, object]] = None + + serialized: Optional[Dict[str, object]] = None + + share_token: Optional[str] = None + + start_time: Optional[datetime] = None + + tags: Optional[List[str]] = None + + thread_id: Optional[str] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + trace_first_received_at: Optional[datetime] = None + + trace_max_start_time: Optional[datetime] = None + + trace_min_start_time: Optional[datetime] = None + + trace_tier: Optional[Literal["longlived", "shortlived"]] = None + + trace_upgrade: Optional[bool] = None + + ttl_seconds: Optional[int] = None diff --git a/python/langsmith_api/types/run_schema_with_annotation_queue_info.py b/python/langsmith_api/types/run_schema_with_annotation_queue_info.py new file mode 100644 index 000000000..387549132 --- /dev/null +++ b/python/langsmith_api/types/run_schema_with_annotation_queue_info.py @@ -0,0 +1,139 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel +from .run_type_enum import RunTypeEnum + +__all__ = ["RunSchemaWithAnnotationQueueInfo"] + + +class RunSchemaWithAnnotationQueueInfo(BaseModel): + """Run schema with annotation queue info.""" + + id: str + + app_path: str + + dotted_order: str + + name: str + + queue_run_id: str + + run_type: RunTypeEnum + """Enum for run types.""" + + session_id: str + + status: str + + trace_id: str + + added_at: Optional[datetime] = None + + child_run_ids: Optional[List[str]] = None + + completed_by: Optional[List[str]] = None + + completion_cost: Optional[str] = None + + completion_cost_details: Optional[Dict[str, str]] = None + + completion_token_details: Optional[Dict[str, int]] = None + + completion_tokens: Optional[int] = None + + direct_child_run_ids: Optional[List[str]] = None + + effective_added_at: Optional[datetime] = None + + end_time: Optional[datetime] = None + + error: Optional[str] = None + + events: Optional[List[Dict[str, object]]] = None + + execution_order: Optional[int] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, Dict[str, object]]] = None + + first_token_time: Optional[datetime] = None + + in_dataset: Optional[bool] = None + + inputs: Optional[Dict[str, object]] = None + + inputs_preview: Optional[str] = None + + inputs_s3_urls: Optional[Dict[str, object]] = None + + last_queued_at: Optional[datetime] = None + + last_reviewed_time: Optional[datetime] = None + + manifest_id: Optional[str] = None + + manifest_s3_id: Optional[str] = None + + messages: Optional[List[Dict[str, object]]] = None + + outputs: Optional[Dict[str, object]] = None + + outputs_preview: Optional[str] = None + + outputs_s3_urls: Optional[Dict[str, object]] = None + + parent_run_id: Optional[str] = None + + parent_run_ids: Optional[List[str]] = None + + price_model_id: Optional[str] = None + + prompt_cost: Optional[str] = None + + prompt_cost_details: Optional[Dict[str, str]] = None + + prompt_token_details: Optional[Dict[str, int]] = None + + prompt_tokens: Optional[int] = None + + reference_dataset_id: Optional[str] = None + + reference_example_id: Optional[str] = None + + reserved_by: Optional[List[str]] = None + + s3_urls: Optional[Dict[str, object]] = None + + serialized: Optional[Dict[str, object]] = None + + share_token: Optional[str] = None + + source_proposed_example_id: Optional[str] = None + + start_time: Optional[datetime] = None + + tags: Optional[List[str]] = None + + thread_id: Optional[str] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + trace_first_received_at: Optional[datetime] = None + + trace_max_start_time: Optional[datetime] = None + + trace_min_start_time: Optional[datetime] = None + + trace_tier: Optional[Literal["longlived", "shortlived"]] = None + + trace_upgrade: Optional[bool] = None + + ttl_seconds: Optional[int] = None diff --git a/python/langsmith_api/types/run_stats_group_by_param.py b/python/langsmith_api/types/run_stats_group_by_param.py new file mode 100644 index 000000000..964c22940 --- /dev/null +++ b/python/langsmith_api/types/run_stats_group_by_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RunStatsGroupByParam"] + + +class RunStatsGroupByParam(TypedDict, total=False): + """Group by param for run stats.""" + + attribute: Required[Literal["name", "run_type", "tag", "metadata"]] + + max_groups: int + + path: Optional[str] diff --git a/python/langsmith_api/types/run_stats_params.py b/python/langsmith_api/types/run_stats_params.py new file mode 100644 index 000000000..e6f481fe2 --- /dev/null +++ b/python/langsmith_api/types/run_stats_params.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .run_type_enum import RunTypeEnum +from .run_stats_group_by_param import RunStatsGroupByParam +from .runs_filter_data_source_type_enum import RunsFilterDataSourceTypeEnum + +__all__ = ["RunStatsParams"] + + +class RunStatsParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + data_source_type: Optional[RunsFilterDataSourceTypeEnum] + """Enum for run data source types.""" + + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + error: Optional[bool] + + execution_order: Optional[int] + + filter: Optional[str] + + group_by: Optional[RunStatsGroupByParam] + """Group by param for run stats.""" + + groups: Optional[SequenceNotStr[Optional[str]]] + + is_root: Optional[bool] + + parent_run: Optional[str] + + query: Optional[str] + + reference_example: Optional[SequenceNotStr[str]] + + run_type: Optional[RunTypeEnum] + """Enum for run types.""" + + search_filter: Optional[str] + + select: Optional[ + List[ + Literal[ + "run_count", + "latency_p50", + "latency_p99", + "latency_avg", + "first_token_p50", + "first_token_p99", + "total_tokens", + "prompt_tokens", + "completion_tokens", + "median_tokens", + "completion_tokens_p50", + "prompt_tokens_p50", + "tokens_p99", + "completion_tokens_p99", + "prompt_tokens_p99", + "last_run_start_time", + "feedback_stats", + "thread_feedback_stats", + "run_facets", + "error_rate", + "streaming_rate", + "total_cost", + "prompt_cost", + "completion_cost", + "cost_p50", + "cost_p99", + "session_feedback_stats", + "all_run_stats", + "all_token_stats", + "prompt_token_details", + "completion_token_details", + "prompt_cost_details", + "completion_cost_details", + ] + ] + ] + + session: Optional[SequenceNotStr[str]] + + skip_pagination: Optional[bool] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + trace: Optional[str] + + trace_filter: Optional[str] + + tree_filter: Optional[str] + + use_experimental_search: bool diff --git a/python/langsmith_api/types/run_stats_response.py b/python/langsmith_api/types/run_stats_response.py new file mode 100644 index 000000000..18c2f6d3b --- /dev/null +++ b/python/langsmith_api/types/run_stats_response.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from .._models import BaseModel + +__all__ = ["RunStatsResponse", "RunStats", "UnionMember1UnionMember1Item"] + + +class RunStats(BaseModel): + completion_cost: Optional[float] = None + + completion_cost_details: Optional[Dict[str, object]] = None + + completion_token_details: Optional[Dict[str, object]] = None + + completion_tokens: Optional[int] = None + + completion_tokens_p50: Optional[int] = None + + completion_tokens_p99: Optional[int] = None + + cost_p50: Optional[float] = None + + cost_p99: Optional[float] = None + + error_rate: Optional[float] = None + + feedback_stats: Optional[Dict[str, object]] = None + + first_token_p50: Optional[float] = None + + first_token_p99: Optional[float] = None + + last_run_start_time: Optional[datetime] = None + + latency_p50: Optional[float] = None + + latency_p99: Optional[float] = None + + median_tokens: Optional[int] = None + + prompt_cost: Optional[float] = None + + prompt_cost_details: Optional[Dict[str, object]] = None + + prompt_token_details: Optional[Dict[str, object]] = None + + prompt_tokens: Optional[int] = None + + prompt_tokens_p50: Optional[int] = None + + prompt_tokens_p99: Optional[int] = None + + run_count: Optional[int] = None + + run_facets: Optional[List[Dict[str, object]]] = None + + streaming_rate: Optional[float] = None + + tokens_p99: Optional[int] = None + + total_cost: Optional[float] = None + + total_tokens: Optional[int] = None + + +class UnionMember1UnionMember1Item(BaseModel): + completion_cost: Optional[float] = None + + completion_cost_details: Optional[Dict[str, object]] = None + + completion_token_details: Optional[Dict[str, object]] = None + + completion_tokens: Optional[int] = None + + completion_tokens_p50: Optional[int] = None + + completion_tokens_p99: Optional[int] = None + + cost_p50: Optional[float] = None + + cost_p99: Optional[float] = None + + error_rate: Optional[float] = None + + feedback_stats: Optional[Dict[str, object]] = None + + first_token_p50: Optional[float] = None + + first_token_p99: Optional[float] = None + + last_run_start_time: Optional[datetime] = None + + latency_p50: Optional[float] = None + + latency_p99: Optional[float] = None + + median_tokens: Optional[int] = None + + prompt_cost: Optional[float] = None + + prompt_cost_details: Optional[Dict[str, object]] = None + + prompt_token_details: Optional[Dict[str, object]] = None + + prompt_tokens: Optional[int] = None + + prompt_tokens_p50: Optional[int] = None + + prompt_tokens_p99: Optional[int] = None + + run_count: Optional[int] = None + + run_facets: Optional[List[Dict[str, object]]] = None + + streaming_rate: Optional[float] = None + + tokens_p99: Optional[int] = None + + total_cost: Optional[float] = None + + total_tokens: Optional[int] = None + + +RunStatsResponse: TypeAlias = Union[RunStats, Dict[str, UnionMember1UnionMember1Item]] diff --git a/python/langsmith_api/types/run_type_enum.py b/python/langsmith_api/types/run_type_enum.py new file mode 100644 index 000000000..fed71c2c3 --- /dev/null +++ b/python/langsmith_api/types/run_type_enum.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunTypeEnum"] + +RunTypeEnum: TypeAlias = Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] diff --git a/python/langsmith_api/types/run_update_params.py b/python/langsmith_api/types/run_update_params.py new file mode 100644 index 000000000..a0dbdc889 --- /dev/null +++ b/python/langsmith_api/types/run_update_params.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["RunUpdateParams"] + + +class RunUpdateParams(TypedDict, total=False): + id: str + + dotted_order: str + + end_time: str + + error: str + + events: Iterable[Dict[str, object]] + + extra: Dict[str, object] + + input_attachments: Dict[str, object] + + inputs: Dict[str, object] + + name: str + + output_attachments: Dict[str, object] + + outputs: Dict[str, object] + + parent_run_id: str + + reference_example_id: str + + run_type: Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"] + + serialized: Dict[str, object] + + session_id: str + + session_name: str + + start_time: str + + status: str + + tags: SequenceNotStr[str] + + trace_id: str diff --git a/python/langsmith_api/types/run_update_response.py b/python/langsmith_api/types/run_update_response.py new file mode 100644 index 000000000..a57068014 --- /dev/null +++ b/python/langsmith_api/types/run_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import TypeAlias + +__all__ = ["RunUpdateResponse", "RunUpdateResponseItem"] + + +class RunUpdateResponseItem: + pass + + +RunUpdateResponse: TypeAlias = Dict[str, RunUpdateResponseItem] diff --git a/python/langsmith_api/types/runs/__init__.py b/python/langsmith_api/types/runs/__init__.py new file mode 100644 index 000000000..f8ee8b14b --- /dev/null +++ b/python/langsmith_api/types/runs/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/python/langsmith_api/types/runs_filter_data_source_type_enum.py b/python/langsmith_api/types/runs_filter_data_source_type_enum.py new file mode 100644 index 000000000..bdab4e90e --- /dev/null +++ b/python/langsmith_api/types/runs_filter_data_source_type_enum.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RunsFilterDataSourceTypeEnum"] + +RunsFilterDataSourceTypeEnum: TypeAlias = Literal[ + "current", "historical", "lite", "root_lite", "runs_feedbacks_rmt_wide" +] diff --git a/python/langsmith_api/types/sandboxes/__init__.py b/python/langsmith_api/types/sandboxes/__init__.py new file mode 100644 index 000000000..804c50cbf --- /dev/null +++ b/python/langsmith_api/types/sandboxes/__init__.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .box_list_params import BoxListParams as BoxListParams +from .box_create_params import BoxCreateParams as BoxCreateParams +from .box_list_response import BoxListResponse as BoxListResponse +from .box_update_params import BoxUpdateParams as BoxUpdateParams +from .box_start_response import BoxStartResponse as BoxStartResponse +from .box_create_response import BoxCreateResponse as BoxCreateResponse +from .box_update_response import BoxUpdateResponse as BoxUpdateResponse +from .snapshot_list_params import SnapshotListParams as SnapshotListParams +from .box_retrieve_response import BoxRetrieveResponse as BoxRetrieveResponse +from .snapshot_create_params import SnapshotCreateParams as SnapshotCreateParams +from .snapshot_list_response import SnapshotListResponse as SnapshotListResponse +from .box_get_status_response import BoxGetStatusResponse as BoxGetStatusResponse +from .snapshot_create_response import SnapshotCreateResponse as SnapshotCreateResponse +from .box_create_snapshot_params import BoxCreateSnapshotParams as BoxCreateSnapshotParams +from .snapshot_retrieve_response import SnapshotRetrieveResponse as SnapshotRetrieveResponse +from .box_create_snapshot_response import BoxCreateSnapshotResponse as BoxCreateSnapshotResponse +from .box_generate_service_url_params import BoxGenerateServiceURLParams as BoxGenerateServiceURLParams +from .box_generate_service_url_response import BoxGenerateServiceURLResponse as BoxGenerateServiceURLResponse diff --git a/python/langsmith_api/types/sandboxes/box_create_params.py b/python/langsmith_api/types/sandboxes/box_create_params.py new file mode 100644 index 000000000..b19b4188d --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_create_params.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BoxCreateParams", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class BoxCreateParams(TypedDict, total=False): + delete_after_stop_seconds: int + + env_vars: Dict[str, str] + + fs_capacity_bytes: int + + idle_ttl_seconds: int + + mem_bytes: int + + name: str + + proxy_config: ProxyConfig + + restore_memory: bool + """ + RestoreMemory, when non-nil, overrides the server default for whether to resume + the sandbox from its captured memory snapshot. + + true → resume from the memory snapshot if it exists; cold-boot the sandbox + otherwise. false → always cold-boot, even if a memory snapshot exists. nil → use + the server default. + + Applies to this request only; a later stop+start of the same sandbox reverts to + the server default. + """ + + snapshot_id: str + + snapshot_name: str + + tag_value_ids: SequenceNotStr[str] + + vcpus: int + + +class ProxyConfigAccessControl(TypedDict, total=False): + allow_list: SequenceNotStr[str] + + deny_list: SequenceNotStr[str] + + +class ProxyConfigCallbackRequestHeader(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["plaintext", "opaque", "workspace_secret"]] + + is_set: bool + + value: str + + +class ProxyConfigCallback(TypedDict, total=False): + match_hosts: Required[SequenceNotStr[str]] + + ttl_seconds: Required[int] + + url: Required[str] + + full_request: bool + + request_headers: Iterable[ProxyConfigCallbackRequestHeader] + + +class ProxyConfigRuleAws(TypedDict, total=False): + access_key_id: str + + secret_access_key: str + + +class ProxyConfigRuleHeader(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["plaintext", "opaque", "workspace_secret"]] + + is_set: bool + + value: str + + +class ProxyConfigRule(TypedDict, total=False): + name: Required[str] + + aws: ProxyConfigRuleAws + + enabled: bool + + headers: Iterable[ProxyConfigRuleHeader] + + match_hosts: SequenceNotStr[str] + + match_paths: SequenceNotStr[str] + + type: str + + +class ProxyConfig(TypedDict, total=False): + access_control: ProxyConfigAccessControl + + callbacks: Iterable[ProxyConfigCallback] + + no_proxy: SequenceNotStr[str] + + rules: Iterable[ProxyConfigRule] diff --git a/python/langsmith_api/types/sandboxes/box_create_response.py b/python/langsmith_api/types/sandboxes/box_create_response.py new file mode 100644 index 000000000..b03456976 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_create_response.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "BoxCreateResponse", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class ProxyConfigAccessControl(BaseModel): + allow_list: Optional[List[str]] = None + + deny_list: Optional[List[str]] = None + + +class ProxyConfigCallbackRequestHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigCallback(BaseModel): + match_hosts: List[str] + + ttl_seconds: int + + url: str + + full_request: Optional[bool] = None + + request_headers: Optional[List[ProxyConfigCallbackRequestHeader]] = None + + +class ProxyConfigRuleAws(BaseModel): + access_key_id: Optional[str] = None + + secret_access_key: Optional[str] = None + + +class ProxyConfigRuleHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigRule(BaseModel): + name: str + + aws: Optional[ProxyConfigRuleAws] = None + + enabled: Optional[bool] = None + + headers: Optional[List[ProxyConfigRuleHeader]] = None + + match_hosts: Optional[List[str]] = None + + match_paths: Optional[List[str]] = None + + type: Optional[str] = None + + +class ProxyConfig(BaseModel): + access_control: Optional[ProxyConfigAccessControl] = None + + callbacks: Optional[List[ProxyConfigCallback]] = None + + no_proxy: Optional[List[str]] = None + + rules: Optional[List[ProxyConfigRule]] = None + + +class BoxCreateResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + dataplane_url: Optional[str] = None + + delete_after_stop_seconds: Optional[int] = None + + fs_capacity_bytes: Optional[int] = None + + idle_ttl_seconds: Optional[int] = None + + mem_bytes: Optional[int] = None + + name: Optional[str] = None + + proxy_config: Optional[ProxyConfig] = None + + size_class: Optional[str] = None + + snapshot_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + stopped_at: Optional[str] = None + + updated_at: Optional[str] = None + + updated_by: Optional[str] = None + + vcpus: Optional[int] = None diff --git a/python/langsmith_api/types/sandboxes/box_create_snapshot_params.py b/python/langsmith_api/types/sandboxes/box_create_snapshot_params.py new file mode 100644 index 000000000..0d3b7d140 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_create_snapshot_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["BoxCreateSnapshotParams"] + + +class BoxCreateSnapshotParams(TypedDict, total=False): + body_name: Required[Annotated[str, PropertyInfo(alias="name")]] + + checkpoint: str + """if omitted, creates a fresh checkpoint from the running VM""" + + docker_image: str + """sandbox-local Docker image to export""" + + fs_capacity_bytes: int + """required for Docker image export unless the sandbox has a capacity""" + + include_memory: bool + """ + IncludeMemory, when true, captures a full VM memory snapshot alongside the + filesystem clone. Only honored when the sandbox is running AND Checkpoint is + omitted (i.e. a fresh in-VM checkpoint is requested). Defaults to false to keep + snapshots small unless memory restore is explicitly desired. + """ diff --git a/python/langsmith_api/types/sandboxes/box_create_snapshot_response.py b/python/langsmith_api/types/sandboxes/box_create_snapshot_response.py new file mode 100644 index 000000000..47dab642c --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_create_snapshot_response.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["BoxCreateSnapshotResponse"] + + +class BoxCreateSnapshotResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + docker_image: Optional[str] = None + + fs_capacity_bytes: Optional[int] = None + + fs_used_bytes: Optional[int] = None + + image_digest: Optional[str] = None + + memory_snapshot_size_bytes: Optional[int] = None + """ + MemorySnapshotSizeBytes is non-nil iff the snapshot was captured with VM memory + state. A non-nil value is the canonical signal that this snapshot can + warm-restore from memory; nil means rootfs only. + """ + + name: Optional[str] = None + + registry_id: Optional[str] = None + + source_sandbox_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + updated_at: Optional[str] = None diff --git a/python/langsmith_api/types/sandboxes/box_generate_service_url_params.py b/python/langsmith_api/types/sandboxes/box_generate_service_url_params.py new file mode 100644 index 000000000..848176465 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_generate_service_url_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BoxGenerateServiceURLParams"] + + +class BoxGenerateServiceURLParams(TypedDict, total=False): + expires_in_seconds: int + + port: int diff --git a/python/langsmith_api/types/sandboxes/box_generate_service_url_response.py b/python/langsmith_api/types/sandboxes/box_generate_service_url_response.py new file mode 100644 index 000000000..0ff6d5e6d --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_generate_service_url_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["BoxGenerateServiceURLResponse"] + + +class BoxGenerateServiceURLResponse(BaseModel): + token: Optional[str] = None + + browser_url: Optional[str] = None + + expires_at: Optional[str] = None + + service_url: Optional[str] = None diff --git a/python/langsmith_api/types/sandboxes/box_get_status_response.py b/python/langsmith_api/types/sandboxes/box_get_status_response.py new file mode 100644 index 000000000..31a54d910 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_get_status_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["BoxGetStatusResponse"] + + +class BoxGetStatusResponse(BaseModel): + status: Optional[str] = None + + status_message: Optional[str] = None diff --git a/python/langsmith_api/types/sandboxes/box_list_params.py b/python/langsmith_api/types/sandboxes/box_list_params.py new file mode 100644 index 000000000..d0778d958 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BoxListParams"] + + +class BoxListParams(TypedDict, total=False): + created_by: str + """Filter by creator identity. Only 'me' is supported.""" + + limit: int + """Maximum number of results""" + + name_contains: str + """Filter by name substring""" + + offset: int + """Pagination offset""" + + sort_by: str + """Sort column (name, status, created_at)""" + + sort_direction: str + """Sort direction (asc, desc)""" + + status: str + """Filter by status (provisioning, ready, failed, stopped, deleting)""" diff --git a/python/langsmith_api/types/sandboxes/box_list_response.py b/python/langsmith_api/types/sandboxes/box_list_response.py new file mode 100644 index 000000000..9476b3428 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_list_response.py @@ -0,0 +1,132 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "BoxListResponse", + "Sandbox", + "SandboxProxyConfig", + "SandboxProxyConfigAccessControl", + "SandboxProxyConfigCallback", + "SandboxProxyConfigCallbackRequestHeader", + "SandboxProxyConfigRule", + "SandboxProxyConfigRuleAws", + "SandboxProxyConfigRuleHeader", +] + + +class SandboxProxyConfigAccessControl(BaseModel): + allow_list: Optional[List[str]] = None + + deny_list: Optional[List[str]] = None + + +class SandboxProxyConfigCallbackRequestHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class SandboxProxyConfigCallback(BaseModel): + match_hosts: List[str] + + ttl_seconds: int + + url: str + + full_request: Optional[bool] = None + + request_headers: Optional[List[SandboxProxyConfigCallbackRequestHeader]] = None + + +class SandboxProxyConfigRuleAws(BaseModel): + access_key_id: Optional[str] = None + + secret_access_key: Optional[str] = None + + +class SandboxProxyConfigRuleHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class SandboxProxyConfigRule(BaseModel): + name: str + + aws: Optional[SandboxProxyConfigRuleAws] = None + + enabled: Optional[bool] = None + + headers: Optional[List[SandboxProxyConfigRuleHeader]] = None + + match_hosts: Optional[List[str]] = None + + match_paths: Optional[List[str]] = None + + type: Optional[str] = None + + +class SandboxProxyConfig(BaseModel): + access_control: Optional[SandboxProxyConfigAccessControl] = None + + callbacks: Optional[List[SandboxProxyConfigCallback]] = None + + no_proxy: Optional[List[str]] = None + + rules: Optional[List[SandboxProxyConfigRule]] = None + + +class Sandbox(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + dataplane_url: Optional[str] = None + + delete_after_stop_seconds: Optional[int] = None + + fs_capacity_bytes: Optional[int] = None + + idle_ttl_seconds: Optional[int] = None + + mem_bytes: Optional[int] = None + + name: Optional[str] = None + + proxy_config: Optional[SandboxProxyConfig] = None + + size_class: Optional[str] = None + + snapshot_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + stopped_at: Optional[str] = None + + updated_at: Optional[str] = None + + updated_by: Optional[str] = None + + vcpus: Optional[int] = None + + +class BoxListResponse(BaseModel): + offset: Optional[int] = None + + sandboxes: Optional[List[Sandbox]] = None diff --git a/python/langsmith_api/types/sandboxes/box_retrieve_response.py b/python/langsmith_api/types/sandboxes/box_retrieve_response.py new file mode 100644 index 000000000..c6de0922c --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_retrieve_response.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "BoxRetrieveResponse", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class ProxyConfigAccessControl(BaseModel): + allow_list: Optional[List[str]] = None + + deny_list: Optional[List[str]] = None + + +class ProxyConfigCallbackRequestHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigCallback(BaseModel): + match_hosts: List[str] + + ttl_seconds: int + + url: str + + full_request: Optional[bool] = None + + request_headers: Optional[List[ProxyConfigCallbackRequestHeader]] = None + + +class ProxyConfigRuleAws(BaseModel): + access_key_id: Optional[str] = None + + secret_access_key: Optional[str] = None + + +class ProxyConfigRuleHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigRule(BaseModel): + name: str + + aws: Optional[ProxyConfigRuleAws] = None + + enabled: Optional[bool] = None + + headers: Optional[List[ProxyConfigRuleHeader]] = None + + match_hosts: Optional[List[str]] = None + + match_paths: Optional[List[str]] = None + + type: Optional[str] = None + + +class ProxyConfig(BaseModel): + access_control: Optional[ProxyConfigAccessControl] = None + + callbacks: Optional[List[ProxyConfigCallback]] = None + + no_proxy: Optional[List[str]] = None + + rules: Optional[List[ProxyConfigRule]] = None + + +class BoxRetrieveResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + dataplane_url: Optional[str] = None + + delete_after_stop_seconds: Optional[int] = None + + fs_capacity_bytes: Optional[int] = None + + idle_ttl_seconds: Optional[int] = None + + mem_bytes: Optional[int] = None + + name: Optional[str] = None + + proxy_config: Optional[ProxyConfig] = None + + size_class: Optional[str] = None + + snapshot_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + stopped_at: Optional[str] = None + + updated_at: Optional[str] = None + + updated_by: Optional[str] = None + + vcpus: Optional[int] = None diff --git a/python/langsmith_api/types/sandboxes/box_start_response.py b/python/langsmith_api/types/sandboxes/box_start_response.py new file mode 100644 index 000000000..9fc80dfa6 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_start_response.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "BoxStartResponse", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class ProxyConfigAccessControl(BaseModel): + allow_list: Optional[List[str]] = None + + deny_list: Optional[List[str]] = None + + +class ProxyConfigCallbackRequestHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigCallback(BaseModel): + match_hosts: List[str] + + ttl_seconds: int + + url: str + + full_request: Optional[bool] = None + + request_headers: Optional[List[ProxyConfigCallbackRequestHeader]] = None + + +class ProxyConfigRuleAws(BaseModel): + access_key_id: Optional[str] = None + + secret_access_key: Optional[str] = None + + +class ProxyConfigRuleHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigRule(BaseModel): + name: str + + aws: Optional[ProxyConfigRuleAws] = None + + enabled: Optional[bool] = None + + headers: Optional[List[ProxyConfigRuleHeader]] = None + + match_hosts: Optional[List[str]] = None + + match_paths: Optional[List[str]] = None + + type: Optional[str] = None + + +class ProxyConfig(BaseModel): + access_control: Optional[ProxyConfigAccessControl] = None + + callbacks: Optional[List[ProxyConfigCallback]] = None + + no_proxy: Optional[List[str]] = None + + rules: Optional[List[ProxyConfigRule]] = None + + +class BoxStartResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + dataplane_url: Optional[str] = None + + delete_after_stop_seconds: Optional[int] = None + + fs_capacity_bytes: Optional[int] = None + + idle_ttl_seconds: Optional[int] = None + + mem_bytes: Optional[int] = None + + name: Optional[str] = None + + proxy_config: Optional[ProxyConfig] = None + + size_class: Optional[str] = None + + snapshot_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + stopped_at: Optional[str] = None + + updated_at: Optional[str] = None + + updated_by: Optional[str] = None + + vcpus: Optional[int] = None diff --git a/python/langsmith_api/types/sandboxes/box_update_params.py b/python/langsmith_api/types/sandboxes/box_update_params.py new file mode 100644 index 000000000..da3828132 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_update_params.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo + +__all__ = [ + "BoxUpdateParams", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class BoxUpdateParams(TypedDict, total=False): + delete_after_stop_seconds: int + + fs_capacity_bytes: int + + idle_ttl_seconds: int + + mem_bytes: int + + body_name: Annotated[str, PropertyInfo(alias="name")] + + proxy_config: ProxyConfig + + tag_value_ids: SequenceNotStr[str] + + vcpus: int + + +class ProxyConfigAccessControl(TypedDict, total=False): + allow_list: SequenceNotStr[str] + + deny_list: SequenceNotStr[str] + + +class ProxyConfigCallbackRequestHeader(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["plaintext", "opaque", "workspace_secret"]] + + is_set: bool + + value: str + + +class ProxyConfigCallback(TypedDict, total=False): + match_hosts: Required[SequenceNotStr[str]] + + ttl_seconds: Required[int] + + url: Required[str] + + full_request: bool + + request_headers: Iterable[ProxyConfigCallbackRequestHeader] + + +class ProxyConfigRuleAws(TypedDict, total=False): + access_key_id: str + + secret_access_key: str + + +class ProxyConfigRuleHeader(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["plaintext", "opaque", "workspace_secret"]] + + is_set: bool + + value: str + + +class ProxyConfigRule(TypedDict, total=False): + name: Required[str] + + aws: ProxyConfigRuleAws + + enabled: bool + + headers: Iterable[ProxyConfigRuleHeader] + + match_hosts: SequenceNotStr[str] + + match_paths: SequenceNotStr[str] + + type: str + + +class ProxyConfig(TypedDict, total=False): + access_control: ProxyConfigAccessControl + + callbacks: Iterable[ProxyConfigCallback] + + no_proxy: SequenceNotStr[str] + + rules: Iterable[ProxyConfigRule] diff --git a/python/langsmith_api/types/sandboxes/box_update_response.py b/python/langsmith_api/types/sandboxes/box_update_response.py new file mode 100644 index 000000000..e1e122db9 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/box_update_response.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "BoxUpdateResponse", + "ProxyConfig", + "ProxyConfigAccessControl", + "ProxyConfigCallback", + "ProxyConfigCallbackRequestHeader", + "ProxyConfigRule", + "ProxyConfigRuleAws", + "ProxyConfigRuleHeader", +] + + +class ProxyConfigAccessControl(BaseModel): + allow_list: Optional[List[str]] = None + + deny_list: Optional[List[str]] = None + + +class ProxyConfigCallbackRequestHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigCallback(BaseModel): + match_hosts: List[str] + + ttl_seconds: int + + url: str + + full_request: Optional[bool] = None + + request_headers: Optional[List[ProxyConfigCallbackRequestHeader]] = None + + +class ProxyConfigRuleAws(BaseModel): + access_key_id: Optional[str] = None + + secret_access_key: Optional[str] = None + + +class ProxyConfigRuleHeader(BaseModel): + name: str + + type: Literal["plaintext", "opaque", "workspace_secret"] + + is_set: Optional[bool] = None + + value: Optional[str] = None + + +class ProxyConfigRule(BaseModel): + name: str + + aws: Optional[ProxyConfigRuleAws] = None + + enabled: Optional[bool] = None + + headers: Optional[List[ProxyConfigRuleHeader]] = None + + match_hosts: Optional[List[str]] = None + + match_paths: Optional[List[str]] = None + + type: Optional[str] = None + + +class ProxyConfig(BaseModel): + access_control: Optional[ProxyConfigAccessControl] = None + + callbacks: Optional[List[ProxyConfigCallback]] = None + + no_proxy: Optional[List[str]] = None + + rules: Optional[List[ProxyConfigRule]] = None + + +class BoxUpdateResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + dataplane_url: Optional[str] = None + + delete_after_stop_seconds: Optional[int] = None + + fs_capacity_bytes: Optional[int] = None + + idle_ttl_seconds: Optional[int] = None + + mem_bytes: Optional[int] = None + + name: Optional[str] = None + + proxy_config: Optional[ProxyConfig] = None + + size_class: Optional[str] = None + + snapshot_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + stopped_at: Optional[str] = None + + updated_at: Optional[str] = None + + updated_by: Optional[str] = None + + vcpus: Optional[int] = None diff --git a/python/langsmith_api/types/sandboxes/snapshot_create_params.py b/python/langsmith_api/types/sandboxes/snapshot_create_params.py new file mode 100644 index 000000000..3dfd79538 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/snapshot_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SnapshotCreateParams"] + + +class SnapshotCreateParams(TypedDict, total=False): + docker_image: Required[str] + + fs_capacity_bytes: Required[int] + + name: Required[str] + + registry_id: str diff --git a/python/langsmith_api/types/sandboxes/snapshot_create_response.py b/python/langsmith_api/types/sandboxes/snapshot_create_response.py new file mode 100644 index 000000000..13256eb13 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/snapshot_create_response.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SnapshotCreateResponse"] + + +class SnapshotCreateResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + docker_image: Optional[str] = None + + fs_capacity_bytes: Optional[int] = None + + fs_used_bytes: Optional[int] = None + + image_digest: Optional[str] = None + + memory_snapshot_size_bytes: Optional[int] = None + """ + MemorySnapshotSizeBytes is non-nil iff the snapshot was captured with VM memory + state. A non-nil value is the canonical signal that this snapshot can + warm-restore from memory; nil means rootfs only. + """ + + name: Optional[str] = None + + registry_id: Optional[str] = None + + source_sandbox_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + updated_at: Optional[str] = None diff --git a/python/langsmith_api/types/sandboxes/snapshot_list_params.py b/python/langsmith_api/types/sandboxes/snapshot_list_params.py new file mode 100644 index 000000000..aa200b688 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/snapshot_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SnapshotListParams"] + + +class SnapshotListParams(TypedDict, total=False): + created_by: str + """Filter by creator identity. Only 'me' is supported.""" + + limit: int + """Maximum number of results""" + + name_contains: str + """Filter by name substring""" + + offset: int + """Pagination offset""" + + sort_by: str + """Sort column (name, status, created_at)""" + + sort_direction: str + """Sort direction (asc, desc)""" + + status: str + """Filter by status (building, ready, failed, deleting)""" diff --git a/python/langsmith_api/types/sandboxes/snapshot_list_response.py b/python/langsmith_api/types/sandboxes/snapshot_list_response.py new file mode 100644 index 000000000..a6c803c35 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/snapshot_list_response.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from ..._models import BaseModel + +__all__ = ["SnapshotListResponse", "Snapshot"] + + +class Snapshot(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + docker_image: Optional[str] = None + + fs_capacity_bytes: Optional[int] = None + + fs_used_bytes: Optional[int] = None + + image_digest: Optional[str] = None + + memory_snapshot_size_bytes: Optional[int] = None + """ + MemorySnapshotSizeBytes is non-nil iff the snapshot was captured with VM memory + state. A non-nil value is the canonical signal that this snapshot can + warm-restore from memory; nil means rootfs only. + """ + + name: Optional[str] = None + + registry_id: Optional[str] = None + + source_sandbox_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + updated_at: Optional[str] = None + + +class SnapshotListResponse(BaseModel): + offset: Optional[int] = None + + snapshots: Optional[List[Snapshot]] = None diff --git a/python/langsmith_api/types/sandboxes/snapshot_retrieve_response.py b/python/langsmith_api/types/sandboxes/snapshot_retrieve_response.py new file mode 100644 index 000000000..7e3c4aff7 --- /dev/null +++ b/python/langsmith_api/types/sandboxes/snapshot_retrieve_response.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SnapshotRetrieveResponse"] + + +class SnapshotRetrieveResponse(BaseModel): + id: Optional[str] = None + + created_at: Optional[str] = None + + created_by: Optional[str] = None + + docker_image: Optional[str] = None + + fs_capacity_bytes: Optional[int] = None + + fs_used_bytes: Optional[int] = None + + image_digest: Optional[str] = None + + memory_snapshot_size_bytes: Optional[int] = None + """ + MemorySnapshotSizeBytes is non-nil iff the snapshot was captured with VM memory + state. A non-nil value is the canonical signal that this snapshot can + warm-restore from memory; nil means rootfs only. + """ + + name: Optional[str] = None + + registry_id: Optional[str] = None + + source_sandbox_id: Optional[str] = None + + status: Optional[str] = None + + status_message: Optional[str] = None + + updated_at: Optional[str] = None diff --git a/python/langsmith_api/types/session_create_params.py b/python/langsmith_api/types/session_create_params.py new file mode 100644 index 000000000..a76bbf3d0 --- /dev/null +++ b/python/langsmith_api/types/session_create_params.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["SessionCreateParams"] + + +class SessionCreateParams(TypedDict, total=False): + upsert: bool + + id: Optional[str] + + default_dataset_id: Optional[str] + + description: Optional[str] + + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + evaluator_keys: Optional[SequenceNotStr[str]] + + extra: Optional[Dict[str, object]] + + kicked_off_by: Optional[str] + + name: str + + num_examples: Optional[int] + + num_repetitions: Optional[int] + + reference_dataset_id: Optional[str] + + start_time: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + + trace_tier: Optional[Literal["longlived", "shortlived"]] diff --git a/python/langsmith_api/types/session_dashboard_params.py b/python/langsmith_api/types/session_dashboard_params.py new file mode 100644 index 000000000..802506470 --- /dev/null +++ b/python/langsmith_api/types/session_dashboard_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo +from .timedelta_input_param import TimedeltaInputParam +from .run_stats_group_by_param import RunStatsGroupByParam + +__all__ = ["SessionDashboardParams"] + + +class SessionDashboardParams(TypedDict, total=False): + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + group_by: Optional[RunStatsGroupByParam] + """Group by param for run stats.""" + + omit_data: bool + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + stride: TimedeltaInputParam + """Timedelta input.""" + + timezone: str + + accept: str diff --git a/python/langsmith_api/types/session_list_params.py b/python/langsmith_api/types/session_list_params.py new file mode 100644 index 000000000..1f30e512f --- /dev/null +++ b/python/langsmith_api/types/session_list_params.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo +from .session_sortable_columns import SessionSortableColumns + +__all__ = ["SessionListParams"] + + +class SessionListParams(TypedDict, total=False): + id: Optional[SequenceNotStr[str]] + + dataset_version: Optional[str] + + facets: bool + + filter: Optional[str] + + include_stats: bool + + limit: int + + metadata: Optional[str] + + name: Optional[str] + + name_contains: Optional[str] + + offset: int + + reference_dataset: Optional[SequenceNotStr[str]] + + reference_free: Optional[bool] + + sort_by: SessionSortableColumns + + sort_by_desc: bool + + sort_by_feedback_key: Optional[str] + + sort_by_feedback_source: Optional[Literal["session", "run"]] + + stats_filter: Optional[str] + + stats_select: Optional[SequenceNotStr[str]] + + stats_start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + tag_value_id: Optional[SequenceNotStr[str]] + + use_approx_stats: bool + + accept: str diff --git a/python/langsmith_api/types/session_retrieve_params.py b/python/langsmith_api/types/session_retrieve_params.py new file mode 100644 index 000000000..a4aaf4901 --- /dev/null +++ b/python/langsmith_api/types/session_retrieve_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["SessionRetrieveParams"] + + +class SessionRetrieveParams(TypedDict, total=False): + include_stats: bool + + stats_start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + accept: str diff --git a/python/langsmith_api/types/session_sortable_columns.py b/python/langsmith_api/types/session_sortable_columns.py new file mode 100644 index 000000000..327e50625 --- /dev/null +++ b/python/langsmith_api/types/session_sortable_columns.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SessionSortableColumns"] + +SessionSortableColumns: TypeAlias = Literal[ + "name", "start_time", "last_run_start_time", "latency_p50", "latency_p99", "error_rate", "feedback", "runs_count" +] diff --git a/python/langsmith_api/types/session_update_params.py b/python/langsmith_api/types/session_update_params.py new file mode 100644 index 000000000..7c420be6f --- /dev/null +++ b/python/langsmith_api/types/session_update_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["SessionUpdateParams"] + + +class SessionUpdateParams(TypedDict, total=False): + default_dataset_id: Optional[str] + + description: Optional[str] + + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + extra: Optional[Dict[str, object]] + + name: Optional[str] + + trace_tier: Optional[Literal["longlived", "shortlived"]] diff --git a/python/langsmith_api/types/sessions/__init__.py b/python/langsmith_api/types/sessions/__init__.py new file mode 100644 index 000000000..567b461b8 --- /dev/null +++ b/python/langsmith_api/types/sessions/__init__.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .insight_list_params import InsightListParams as InsightListParams +from .insight_create_params import InsightCreateParams as InsightCreateParams +from .insight_list_response import InsightListResponse as InsightListResponse +from .insight_update_params import InsightUpdateParams as InsightUpdateParams +from .insight_create_response import InsightCreateResponse as InsightCreateResponse +from .insight_delete_response import InsightDeleteResponse as InsightDeleteResponse +from .insight_update_response import InsightUpdateResponse as InsightUpdateResponse +from .insight_retrieve_runs_params import InsightRetrieveRunsParams as InsightRetrieveRunsParams +from .insight_retrieve_job_response import InsightRetrieveJobResponse as InsightRetrieveJobResponse +from .insight_retrieve_runs_response import InsightRetrieveRunsResponse as InsightRetrieveRunsResponse diff --git a/python/langsmith_api/types/sessions/insight_create_params.py b/python/langsmith_api/types/sessions/insight_create_params.py new file mode 100644 index 000000000..063e9c624 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_create_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["InsightCreateParams"] + + +class InsightCreateParams(TypedDict, total=False): + attribute_schemas: Optional[Dict[str, object]] + + cluster_model: Optional[str] + + config_id: Optional[str] + + end_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + filter: Optional[str] + + hierarchy: Optional[Iterable[int]] + + is_scheduled: bool + + last_n_hours: Optional[int] + + model: Literal["openai", "anthropic"] + + name: Optional[str] + + partitions: Optional[Dict[str, str]] + + sample: Optional[float] + + start_time: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] + + summary_model: Optional[str] + + summary_prompt: Optional[str] + + user_context: Optional[Dict[str, str]] + + validate_model_secrets: bool diff --git a/python/langsmith_api/types/sessions/insight_create_response.py b/python/langsmith_api/types/sessions/insight_create_response.py new file mode 100644 index 000000000..a0d65001f --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_create_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["InsightCreateResponse"] + + +class InsightCreateResponse(BaseModel): + """Response to creating a run clustering job.""" + + id: str + + name: str + + status: str + + error: Optional[str] = None diff --git a/python/langsmith_api/types/sessions/insight_delete_response.py b/python/langsmith_api/types/sessions/insight_delete_response.py new file mode 100644 index 000000000..628857a81 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["InsightDeleteResponse"] + + +class InsightDeleteResponse(BaseModel): + """Response to delete a session cluster job.""" + + id: str + + message: str diff --git a/python/langsmith_api/types/sessions/insight_list_params.py b/python/langsmith_api/types/sessions/insight_list_params.py new file mode 100644 index 000000000..13c6c246d --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["InsightListParams"] + + +class InsightListParams(TypedDict, total=False): + config_id: Optional[str] + + legacy: Optional[bool] + + limit: int + + offset: int diff --git a/python/langsmith_api/types/sessions/insight_list_response.py b/python/langsmith_api/types/sessions/insight_list_response.py new file mode 100644 index 000000000..0c0045fbe --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_list_response.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["InsightListResponse"] + + +class InsightListResponse(BaseModel): + """Session cluster job""" + + id: str + + created_at: datetime + + name: str + + status: str + + config_id: Optional[str] = None + + end_time: Optional[datetime] = None + + error: Optional[str] = None + + metadata: Optional[Dict[str, object]] = None + + shape: Optional[Dict[str, int]] = None + + start_time: Optional[datetime] = None diff --git a/python/langsmith_api/types/sessions/insight_retrieve_job_response.py b/python/langsmith_api/types/sessions/insight_retrieve_job_response.py new file mode 100644 index 000000000..e76ac9d07 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_retrieve_job_response.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["InsightRetrieveJobResponse", "Cluster", "Report", "ReportHighlightedTrace"] + + +class Cluster(BaseModel): + """A single cluster of runs.""" + + id: str + + description: str + + level: int + + name: str + + num_runs: int + + stats: Optional[Dict[str, object]] = None + + parent_id: Optional[str] = None + + parent_name: Optional[str] = None + + +class ReportHighlightedTrace(BaseModel): + """A trace highlighted in an insights report summary. Up to 10 per insights job.""" + + highlight_reason: str + + rank: int + + run_id: str + + cluster_id: Optional[str] = None + + cluster_name: Optional[str] = None + + summary: Optional[str] = None + + +class Report(BaseModel): + """ + High level summary of an insights job that pulls out patterns and specific traces. + """ + + created_at: Optional[datetime] = None + + highlighted_traces: Optional[List[ReportHighlightedTrace]] = None + + key_points: Optional[List[str]] = None + + title: Optional[str] = None + + +class InsightRetrieveJobResponse(BaseModel): + """Response to get a specific cluster job for a session.""" + + id: str + + clusters: List[Cluster] + + created_at: datetime + + name: str + + status: str + + config_id: Optional[str] = None + + end_time: Optional[datetime] = None + + error: Optional[str] = None + + metadata: Optional[Dict[str, object]] = None + + report: Optional[Report] = None + """ + High level summary of an insights job that pulls out patterns and specific + traces. + """ + + shape: Optional[Dict[str, int]] = None + + start_time: Optional[datetime] = None diff --git a/python/langsmith_api/types/sessions/insight_retrieve_runs_params.py b/python/langsmith_api/types/sessions/insight_retrieve_runs_params.py new file mode 100644 index 000000000..6e97bf9f6 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_retrieve_runs_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InsightRetrieveRunsParams"] + + +class InsightRetrieveRunsParams(TypedDict, total=False): + session_id: Required[str] + + attribute_sort_key: Optional[str] + + attribute_sort_order: Optional[Literal["asc", "desc"]] + + cluster_id: Optional[str] + + limit: int + + offset: int diff --git a/python/langsmith_api/types/sessions/insight_retrieve_runs_response.py b/python/langsmith_api/types/sessions/insight_retrieve_runs_response.py new file mode 100644 index 000000000..eb8a943d3 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_retrieve_runs_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ..._models import BaseModel + +__all__ = ["InsightRetrieveRunsResponse"] + + +class InsightRetrieveRunsResponse(BaseModel): + offset: Optional[int] = None + + runs: List[Dict[str, object]] diff --git a/python/langsmith_api/types/sessions/insight_update_params.py b/python/langsmith_api/types/sessions/insight_update_params.py new file mode 100644 index 000000000..ff569d9df --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_update_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["InsightUpdateParams"] + + +class InsightUpdateParams(TypedDict, total=False): + session_id: Required[str] + + name: Required[str] diff --git a/python/langsmith_api/types/sessions/insight_update_response.py b/python/langsmith_api/types/sessions/insight_update_response.py new file mode 100644 index 000000000..c41e36a44 --- /dev/null +++ b/python/langsmith_api/types/sessions/insight_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["InsightUpdateResponse"] + + +class InsightUpdateResponse(BaseModel): + """Response to update a session cluster job.""" + + name: str + + status: str diff --git a/python/langsmith_api/types/sort_by_dataset_column.py b/python/langsmith_api/types/sort_by_dataset_column.py new file mode 100644 index 000000000..8c9ee520d --- /dev/null +++ b/python/langsmith_api/types/sort_by_dataset_column.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SortByDatasetColumn"] + +SortByDatasetColumn: TypeAlias = Literal[ + "name", "created_at", "last_session_start_time", "example_count", "session_count", "modified_at" +] diff --git a/python/langsmith_api/types/source_type.py b/python/langsmith_api/types/source_type.py new file mode 100644 index 000000000..adcc65f47 --- /dev/null +++ b/python/langsmith_api/types/source_type.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SourceType"] + +SourceType: TypeAlias = Literal["api", "model", "app", "auto_eval"] diff --git a/python/langsmith_api/types/timedelta_input_param.py b/python/langsmith_api/types/timedelta_input_param.py new file mode 100644 index 000000000..7a79aa5af --- /dev/null +++ b/python/langsmith_api/types/timedelta_input_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TimedeltaInputParam"] + + +class TimedeltaInputParam(TypedDict, total=False): + """Timedelta input.""" + + days: int + + hours: int + + minutes: int diff --git a/python/langsmith_api/types/tracer_session.py b/python/langsmith_api/types/tracer_session.py new file mode 100644 index 000000000..7c4006368 --- /dev/null +++ b/python/langsmith_api/types/tracer_session.py @@ -0,0 +1,81 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["TracerSession", "ExperimentProgress"] + + +class ExperimentProgress(BaseModel): + evaluator_progress: Dict[str, float] + + expected_run_count: int + + run_progress: float + + +class TracerSession(BaseModel): + """TracerSession schema.""" + + id: str + + tenant_id: str + + completion_cost: Optional[str] = None + + completion_tokens: Optional[int] = None + + default_dataset_id: Optional[str] = None + + description: Optional[str] = None + + end_time: Optional[datetime] = None + + error_rate: Optional[float] = None + + experiment_progress: Optional[ExperimentProgress] = None + + extra: Optional[Dict[str, object]] = None + + feedback_stats: Optional[Dict[str, object]] = None + + first_token_p50: Optional[float] = None + + first_token_p99: Optional[float] = None + + last_run_start_time: Optional[datetime] = None + + last_run_start_time_live: Optional[datetime] = None + + latency_p50: Optional[float] = None + + latency_p99: Optional[float] = None + + name: Optional[str] = None + + prompt_cost: Optional[str] = None + + prompt_tokens: Optional[int] = None + + reference_dataset_id: Optional[str] = None + + run_count: Optional[int] = None + + run_facets: Optional[List[Dict[str, object]]] = None + + session_feedback_stats: Optional[Dict[str, object]] = None + + start_time: Optional[datetime] = None + + streaming_rate: Optional[float] = None + + test_run_number: Optional[int] = None + + total_cost: Optional[str] = None + + total_tokens: Optional[int] = None + + trace_tier: Optional[Literal["longlived", "shortlived"]] = None diff --git a/python/langsmith_api/types/tracer_session_without_virtual_fields.py b/python/langsmith_api/types/tracer_session_without_virtual_fields.py new file mode 100644 index 000000000..069ec5e91 --- /dev/null +++ b/python/langsmith_api/types/tracer_session_without_virtual_fields.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["TracerSessionWithoutVirtualFields"] + + +class TracerSessionWithoutVirtualFields(BaseModel): + """TracerSession schema.""" + + id: str + + tenant_id: str + + default_dataset_id: Optional[str] = None + + description: Optional[str] = None + + end_time: Optional[datetime] = None + + extra: Optional[Dict[str, object]] = None + + last_run_start_time_live: Optional[datetime] = None + + name: Optional[str] = None + + reference_dataset_id: Optional[str] = None + + start_time: Optional[datetime] = None + + trace_tier: Optional[Literal["longlived", "shortlived"]] = None diff --git a/python/langsmith_api/types/workspace_create_params.py b/python/langsmith_api/types/workspace_create_params.py new file mode 100644 index 000000000..0064b5fc6 --- /dev/null +++ b/python/langsmith_api/types/workspace_create_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["WorkspaceCreateParams"] + + +class WorkspaceCreateParams(TypedDict, total=False): + display_name: Required[str] + + id: str + + tenant_handle: Optional[str] diff --git a/python/langsmith_api/types/workspace_create_response.py b/python/langsmith_api/types/workspace_create_response.py new file mode 100644 index 000000000..fa621689a --- /dev/null +++ b/python/langsmith_api/types/workspace_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["WorkspaceCreateResponse"] + + +class WorkspaceCreateResponse(BaseModel): + """Tenant schema.""" + + id: str + + created_at: datetime + + display_name: str + + is_deleted: bool + + is_personal: bool + + data_plane_url: Optional[str] = None + + organization_id: Optional[str] = None + + tenant_handle: Optional[str] = None diff --git a/python/langsmith_api/types/workspace_list_params.py b/python/langsmith_api/types/workspace_list_params.py new file mode 100644 index 000000000..1451ab0c4 --- /dev/null +++ b/python/langsmith_api/types/workspace_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["WorkspaceListParams"] + + +class WorkspaceListParams(TypedDict, total=False): + data_plane_id: Optional[str] + + include_deleted: bool diff --git a/python/langsmith_api/types/workspace_list_response.py b/python/langsmith_api/types/workspace_list_response.py new file mode 100644 index 000000000..3df5b1ead --- /dev/null +++ b/python/langsmith_api/types/workspace_list_response.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from .._models import BaseModel + +__all__ = ["WorkspaceListResponse", "WorkspaceListResponseItem"] + + +class WorkspaceListResponseItem(BaseModel): + id: str + + created_at: datetime + + display_name: str + + is_deleted: bool + + is_personal: bool + + data_plane_url: Optional[str] = None + + organization_id: Optional[str] = None + + permissions: Optional[List[str]] = None + + read_only: Optional[bool] = None + + role_id: Optional[str] = None + + role_name: Optional[str] = None + + tenant_handle: Optional[str] = None + + +WorkspaceListResponse: TypeAlias = List[WorkspaceListResponseItem] diff --git a/python/langsmith_api/types/workspace_update_params.py b/python/langsmith_api/types/workspace_update_params.py new file mode 100644 index 000000000..04b10546a --- /dev/null +++ b/python/langsmith_api/types/workspace_update_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["WorkspaceUpdateParams"] + + +class WorkspaceUpdateParams(TypedDict, total=False): + display_name: Required[str] diff --git a/python/langsmith_api/types/workspace_update_response.py b/python/langsmith_api/types/workspace_update_response.py new file mode 100644 index 000000000..a53e1be33 --- /dev/null +++ b/python/langsmith_api/types/workspace_update_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["WorkspaceUpdateResponse"] + + +class WorkspaceUpdateResponse(BaseModel): + """Tenant schema.""" + + id: str + + created_at: datetime + + display_name: str + + is_deleted: bool + + is_personal: bool + + data_plane_url: Optional[str] = None + + organization_id: Optional[str] = None + + tenant_handle: Optional[str] = None