diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f04d0896..57dc0c3d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.32.0" + ".": "0.33.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index dc3e6d98..cc27019e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 21 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/profound%2Fprofound-85c2cc542ebd87adbf37b0e66894b4854b2e828f88bcafed4ea77f68a97355ce.yml -openapi_spec_hash: fb8408a3cfee3614593fdedee497c1d7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/profound%2Fprofound-7df5a2dba52b52d7fc3d225a28c738eb92c2fbf695805a97a93b58f5c5c5e32f.yml +openapi_spec_hash: 09da3937c866985048c50f8864207d7f config_hash: f683a29859356d6968a19ae3e6063b63 diff --git a/CHANGELOG.md b/CHANGELOG.md index 05e7add1..7134e9ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.33.0 (2026-03-19) + +Full Changelog: [v0.32.0...v0.33.0](https://github.com/cooper-square-technologies/profound-python-sdk/compare/v0.32.0...v0.33.0) + +### Features + +* **api:** api update ([509d95f](https://github.com/cooper-square-technologies/profound-python-sdk/commit/509d95f4de2f04aa7d87a675acb09314ef854877)) +* **api:** api update ([b050c07](https://github.com/cooper-square-technologies/profound-python-sdk/commit/b050c070b9ce850723cef0f2175141c6ed09ec36)) + + +### Bug Fixes + +* sanitize endpoint path params ([6ad08f2](https://github.com/cooper-square-technologies/profound-python-sdk/commit/6ad08f271d90cee4cd405a916fd81e031ebe28ed)) + ## 0.32.0 (2026-03-17) Full Changelog: [v0.31.1...v0.32.0](https://github.com/cooper-square-technologies/profound-python-sdk/compare/v0.31.1...v0.32.0) diff --git a/api.md b/api.md index 4fb07693..f3ea013c 100644 --- a/api.md +++ b/api.md @@ -1,17 +1,7 @@ # Shared Types ```python -from profound.types import ( - AssetNameFilter, - ModelIDFilter, - Pagination, - PathFilter, - PersonaIDFilter, - PromptFilter, - RegionIDFilter, - TagIDFilter, - TopicIDFilter, -) +from profound.types import AssetNameFilter, Pagination, PathFilter, PromptFilter ``` # Organizations diff --git a/pyproject.toml b/pyproject.toml index 403354d3..694df9dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "profound" -version = "0.32.0" +version = "0.33.0" description = "The official Python library for the profound API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/profound/_utils/__init__.py b/src/profound/_utils/__init__.py index dc64e29a..10cb66d2 100644 --- a/src/profound/_utils/__init__.py +++ b/src/profound/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/profound/_utils/_path.py b/src/profound/_utils/_path.py new file mode 100644 index 00000000..4d6e1e4c --- /dev/null +++ b/src/profound/_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/src/profound/_version.py b/src/profound/_version.py index ecaf4f08..2c1a2679 100644 --- a/src/profound/_version.py +++ b/src/profound/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "profound" -__version__ = "0.32.0" # x-release-please-version +__version__ = "0.33.0" # x-release-please-version diff --git a/src/profound/resources/content/optimization.py b/src/profound/resources/content/optimization.py index 877192fb..6a173d99 100644 --- a/src/profound/resources/content/optimization.py +++ b/src/profound/resources/content/optimization.py @@ -5,7 +5,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -71,7 +71,7 @@ def retrieve( if not content_id: raise ValueError(f"Expected a non-empty value for `content_id` but received {content_id!r}") return self._get( - f"/v1/content/{asset_id}/optimization/{content_id}", + path_template("/v1/content/{asset_id}/optimization/{content_id}", asset_id=asset_id, content_id=content_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -110,7 +110,7 @@ def list( if not asset_id: raise ValueError(f"Expected a non-empty value for `asset_id` but received {asset_id!r}") return self._get( - f"/v1/content/{asset_id}/optimization", + path_template("/v1/content/{asset_id}/optimization", asset_id=asset_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -177,7 +177,7 @@ async def retrieve( if not content_id: raise ValueError(f"Expected a non-empty value for `content_id` but received {content_id!r}") return await self._get( - f"/v1/content/{asset_id}/optimization/{content_id}", + path_template("/v1/content/{asset_id}/optimization/{content_id}", asset_id=asset_id, content_id=content_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -216,7 +216,7 @@ async def list( if not asset_id: raise ValueError(f"Expected a non-empty value for `asset_id` but received {asset_id!r}") return await self._get( - f"/v1/content/{asset_id}/optimization", + path_template("/v1/content/{asset_id}/optimization", asset_id=asset_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/profound/resources/logs/raw.py b/src/profound/resources/logs/raw.py index 6d653ec3..c90fa43c 100644 --- a/src/profound/resources/logs/raw.py +++ b/src/profound/resources/logs/raw.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Dict, List, Union, Iterable, cast +from typing import Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime from typing_extensions import Literal @@ -76,6 +76,7 @@ def bots( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[raw_bots_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -137,6 +138,7 @@ def bots( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, raw_bots_params.RawBotsParams, @@ -174,6 +176,7 @@ def logs( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[raw_logs_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -235,6 +238,7 @@ def logs( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, raw_logs_params.RawLogsParams, @@ -296,6 +300,7 @@ async def bots( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[raw_bots_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -357,6 +362,7 @@ async def bots( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, raw_bots_params.RawBotsParams, @@ -394,6 +400,7 @@ async def logs( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[raw_logs_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -455,6 +462,7 @@ async def logs( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, raw_logs_params.RawLogsParams, diff --git a/src/profound/resources/organizations/categories.py b/src/profound/resources/organizations/categories.py index 2fca0124..a16b11be 100644 --- a/src/profound/resources/organizations/categories.py +++ b/src/profound/resources/organizations/categories.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -94,7 +94,7 @@ def assets( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return self._get( - f"/v1/org/categories/{category_id}/assets", + path_template("/v1/org/categories/{category_id}/assets", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -127,7 +127,7 @@ def get_category_personas( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return self._get( - f"/v1/org/categories/{category_id}/personas", + path_template("/v1/org/categories/{category_id}/personas", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -168,7 +168,7 @@ def prompts( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return self._get( - f"/v1/org/categories/{category_id}/prompts", + path_template("/v1/org/categories/{category_id}/prompts", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -217,7 +217,7 @@ def tags( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return self._get( - f"/v1/org/categories/{category_id}/tags", + path_template("/v1/org/categories/{category_id}/tags", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -250,7 +250,7 @@ def topics( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return self._get( - f"/v1/org/categories/{category_id}/topics", + path_template("/v1/org/categories/{category_id}/topics", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -323,7 +323,7 @@ async def assets( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return await self._get( - f"/v1/org/categories/{category_id}/assets", + path_template("/v1/org/categories/{category_id}/assets", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -356,7 +356,7 @@ async def get_category_personas( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return await self._get( - f"/v1/org/categories/{category_id}/personas", + path_template("/v1/org/categories/{category_id}/personas", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -397,7 +397,7 @@ async def prompts( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return await self._get( - f"/v1/org/categories/{category_id}/prompts", + path_template("/v1/org/categories/{category_id}/prompts", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -446,7 +446,7 @@ async def tags( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return await self._get( - f"/v1/org/categories/{category_id}/tags", + path_template("/v1/org/categories/{category_id}/tags", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -479,7 +479,7 @@ async def topics( if not category_id: raise ValueError(f"Expected a non-empty value for `category_id` but received {category_id!r}") return await self._get( - f"/v1/org/categories/{category_id}/topics", + path_template("/v1/org/categories/{category_id}/topics", category_id=category_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/profound/resources/reports.py b/src/profound/resources/reports.py index c14eefdd..6e9cbc50 100644 --- a/src/profound/resources/reports.py +++ b/src/profound/resources/reports.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal @@ -161,6 +161,7 @@ def get_bots_report( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[report_get_bots_report_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -231,6 +232,7 @@ def get_bots_report( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, report_get_bots_report_params.ReportGetBotsReportParams, @@ -252,6 +254,7 @@ def get_referrals_report( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[report_get_referrals_report_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -314,6 +317,7 @@ def get_referrals_report( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, report_get_referrals_report_params.ReportGetReferralsReportParams, @@ -645,6 +649,7 @@ async def get_bots_report( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[report_get_bots_report_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -715,6 +720,7 @@ async def get_bots_report( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, report_get_bots_report_params.ReportGetBotsReportParams, @@ -736,6 +742,7 @@ async def get_referrals_report( end_date: Union[str, datetime] | Omit = omit, filters: Iterable[report_get_referrals_report_params.Filter] | Omit = omit, order_by: Dict[str, Literal["asc", "desc"]] | Omit = omit, + organization_id: Optional[str] | Omit = omit, pagination: Pagination | 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. @@ -798,6 +805,7 @@ async def get_referrals_report( "end_date": end_date, "filters": filters, "order_by": order_by, + "organization_id": organization_id, "pagination": pagination, }, report_get_referrals_report_params.ReportGetReferralsReportParams, diff --git a/src/profound/types/__init__.py b/src/profound/types/__init__.py index c5d6127a..4c8368dd 100644 --- a/src/profound/types/__init__.py +++ b/src/profound/types/__init__.py @@ -5,13 +5,8 @@ from .shared import ( Pagination as Pagination, PathFilter as PathFilter, - TagIDFilter as TagIDFilter, PromptFilter as PromptFilter, - ModelIDFilter as ModelIDFilter, - TopicIDFilter as TopicIDFilter, - RegionIDFilter as RegionIDFilter, AssetNameFilter as AssetNameFilter, - PersonaIDFilter as PersonaIDFilter, ) from .report_info import ReportInfo as ReportInfo from .report_result import ReportResult as ReportResult diff --git a/src/profound/types/logs/raw_bots_params.py b/src/profound/types/logs/raw_bots_params.py index ec086535..01f89c2f 100644 --- a/src/profound/types/logs/raw_bots_params.py +++ b/src/profound/types/logs/raw_bots_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict @@ -84,6 +84,8 @@ class RawBotsParams(TypedDict, total=False): descending. """ + organization_id: Optional[str] + pagination: Pagination """Pagination settings for the report results.""" diff --git a/src/profound/types/logs/raw_logs_params.py b/src/profound/types/logs/raw_logs_params.py index cdb3c3f6..3dfe90fd 100644 --- a/src/profound/types/logs/raw_logs_params.py +++ b/src/profound/types/logs/raw_logs_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict @@ -82,6 +82,8 @@ class RawLogsParams(TypedDict, total=False): descending. """ + organization_id: Optional[str] + pagination: Pagination """Pagination settings for the report results.""" diff --git a/src/profound/types/prompt_answers_params.py b/src/profound/types/prompt_answers_params.py index bcad562b..6ee86f63 100644 --- a/src/profound/types/prompt_answers_params.py +++ b/src/profound/types/prompt_answers_params.py @@ -10,17 +10,17 @@ from .._utils import PropertyInfo from .shared_params.pagination import Pagination from .shared_params.prompt_filter import PromptFilter -from .shared_params.tag_id_filter import TagIDFilter -from .shared_params.model_id_filter import ModelIDFilter -from .shared_params.topic_id_filter import TopicIDFilter -from .shared_params.region_id_filter import RegionIDFilter from .shared_params.asset_name_filter import AssetNameFilter -from .shared_params.persona_id_filter import PersonaIDFilter __all__ = [ "PromptAnswersParams", "Filter", + "FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersModelIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTagIDFilter", "FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter", + "FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter", "FilterAssetIDFilter", "Include", ] @@ -42,6 +42,33 @@ class PromptAnswersParams(TypedDict, total=False): """Pagination parameters for the results. Default is 10,000 rows with no offset.""" +class FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter(TypedDict, total=False): + field: Required[Literal["region_id", "region"]] + """- `region` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersModelIDFilter(TypedDict, total=False): + field: Required[Literal["model_id", "model"]] + """- `model` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTagIDFilter(TypedDict, total=False): + field: Required[Literal["tag_id", "tag"]] + """- `tag` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + class FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter(TypedDict, total=False): """Filter by prompt type (visibility or sentiment)""" @@ -64,6 +91,23 @@ class FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter(TypedDict, total value: Required[Union[Literal["visibility", "sentiment"], List[Literal["visibility", "sentiment"]]]] +class FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter(TypedDict, total=False): + field: Required[Literal["persona_id"]] + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter(TypedDict, total=False): + field: Required[Literal["topic_id", "topic"]] + """- `topic` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + class FilterAssetIDFilter(TypedDict, total=False): field: Required[Literal["asset_id"]] @@ -73,13 +117,13 @@ class FilterAssetIDFilter(TypedDict, total=False): Filter: TypeAlias = Union[ - RegionIDFilter, - ModelIDFilter, - TagIDFilter, + FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter, + FilterProfoundAnswerEngineInsightsFiltersModelIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTagIDFilter, FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter, PromptFilter, - PersonaIDFilter, - TopicIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter, FilterAssetIDFilter, AssetNameFilter, ] diff --git a/src/profound/types/report_citations_params.py b/src/profound/types/report_citations_params.py index dfaea523..9b953200 100644 --- a/src/profound/types/report_citations_params.py +++ b/src/profound/types/report_citations_params.py @@ -12,21 +12,21 @@ from .shared_params.pagination import Pagination from .shared_params.path_filter import PathFilter from .shared_params.prompt_filter import PromptFilter -from .shared_params.tag_id_filter import TagIDFilter -from .shared_params.model_id_filter import ModelIDFilter -from .shared_params.topic_id_filter import TopicIDFilter -from .shared_params.region_id_filter import RegionIDFilter -from .shared_params.persona_id_filter import PersonaIDFilter __all__ = [ "ReportCitationsParams", "Filter", "FilterHostnameFilter", + "FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersModelIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTagIDFilter", "FilterURLFilter", "FilterRootDomainFilter", "FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter", + "FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter", "FilterCitationCategoryFilter", - "FilterPromptIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter", ] @@ -113,6 +113,42 @@ class FilterHostnameFilter(TypedDict, total=False): value: Required[Union[str, SequenceNotStr[str]]] +class FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter(TypedDict, total=False): + field: Required[Literal["region_id", "region"]] + """- `region` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter(TypedDict, total=False): + field: Required[Literal["topic_id", "topic"]] + """- `topic` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersModelIDFilter(TypedDict, total=False): + field: Required[Literal["model_id", "model"]] + """- `model` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTagIDFilter(TypedDict, total=False): + field: Required[Literal["tag_id", "tag"]] + """- `tag` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + class FilterURLFilter(TypedDict, total=False): """Filter by URL""" @@ -179,6 +215,14 @@ class FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter(TypedDict, total value: Required[Union[Literal["visibility", "sentiment"], List[Literal["visibility", "sentiment"]]]] +class FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter(TypedDict, total=False): + field: Required[Literal["persona_id"]] + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + class FilterCitationCategoryFilter(TypedDict, total=False): """Filter by citation category""" @@ -201,7 +245,7 @@ class FilterCitationCategoryFilter(TypedDict, total=False): value: Required[Union[str, SequenceNotStr[str]]] -class FilterPromptIDFilter(TypedDict, total=False): +class FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter(TypedDict, total=False): field: Required[Literal["prompt_id"]] operator: Required[Literal["is", "not_is", "in", "not_in"]] @@ -212,16 +256,16 @@ class FilterPromptIDFilter(TypedDict, total=False): Filter: TypeAlias = Union[ FilterHostnameFilter, PathFilter, - RegionIDFilter, - TopicIDFilter, + FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter, TopicNameFilterParam, - ModelIDFilter, - TagIDFilter, + FilterProfoundAnswerEngineInsightsFiltersModelIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTagIDFilter, FilterURLFilter, FilterRootDomainFilter, FilterProfoundAnswerEngineInsightsFiltersPromptTypeFilter, - PersonaIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter, FilterCitationCategoryFilter, PromptFilter, - FilterPromptIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter, ] diff --git a/src/profound/types/report_get_bots_report_params.py b/src/profound/types/report_get_bots_report_params.py index 9c7ab5a7..ffb128c5 100644 --- a/src/profound/types/report_get_bots_report_params.py +++ b/src/profound/types/report_get_bots_report_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict @@ -54,6 +54,8 @@ class ReportGetBotsReportParams(TypedDict, total=False): descending. """ + organization_id: Optional[str] + pagination: Pagination """Pagination settings for the report results.""" diff --git a/src/profound/types/report_get_referrals_report_params.py b/src/profound/types/report_get_referrals_report_params.py index e8ebf7c7..6c470398 100644 --- a/src/profound/types/report_get_referrals_report_params.py +++ b/src/profound/types/report_get_referrals_report_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict @@ -54,6 +54,8 @@ class ReportGetReferralsReportParams(TypedDict, total=False): descending. """ + organization_id: Optional[str] + pagination: Pagination """Pagination settings for the report results.""" diff --git a/src/profound/types/report_sentiment_params.py b/src/profound/types/report_sentiment_params.py index 622bb1e5..1a02d94f 100644 --- a/src/profound/types/report_sentiment_params.py +++ b/src/profound/types/report_sentiment_params.py @@ -11,14 +11,19 @@ from .topic_name_filter_param import TopicNameFilterParam from .shared_params.pagination import Pagination from .shared_params.prompt_filter import PromptFilter -from .shared_params.tag_id_filter import TagIDFilter -from .shared_params.model_id_filter import ModelIDFilter -from .shared_params.topic_id_filter import TopicIDFilter -from .shared_params.region_id_filter import RegionIDFilter from .shared_params.asset_name_filter import AssetNameFilter -from .shared_params.persona_id_filter import PersonaIDFilter -__all__ = ["ReportSentimentParams", "Filter", "FilterAssetIDFilter", "FilterThemeFilter"] +__all__ = [ + "ReportSentimentParams", + "Filter", + "FilterAssetIDFilter", + "FilterThemeFilter", + "FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersModelIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTagIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter", +] class ReportSentimentParams(TypedDict, total=False): @@ -110,15 +115,59 @@ class FilterThemeFilter(TypedDict, total=False): value: Required[Union[str, SequenceNotStr[str]]] +class FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter(TypedDict, total=False): + field: Required[Literal["region_id", "region"]] + """- `region` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter(TypedDict, total=False): + field: Required[Literal["topic_id", "topic"]] + """- `topic` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersModelIDFilter(TypedDict, total=False): + field: Required[Literal["model_id", "model"]] + """- `model` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTagIDFilter(TypedDict, total=False): + field: Required[Literal["tag_id", "tag"]] + """- `tag` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter(TypedDict, total=False): + field: Required[Literal["persona_id"]] + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + Filter: TypeAlias = Union[ FilterAssetIDFilter, AssetNameFilter, FilterThemeFilter, - RegionIDFilter, - TopicIDFilter, + FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter, TopicNameFilterParam, - ModelIDFilter, - TagIDFilter, + FilterProfoundAnswerEngineInsightsFiltersModelIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTagIDFilter, PromptFilter, - PersonaIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter, ] diff --git a/src/profound/types/report_visibility_params.py b/src/profound/types/report_visibility_params.py index 015b699d..c04203dd 100644 --- a/src/profound/types/report_visibility_params.py +++ b/src/profound/types/report_visibility_params.py @@ -11,14 +11,18 @@ from .topic_name_filter_param import TopicNameFilterParam from .shared_params.pagination import Pagination from .shared_params.prompt_filter import PromptFilter -from .shared_params.tag_id_filter import TagIDFilter -from .shared_params.model_id_filter import ModelIDFilter -from .shared_params.topic_id_filter import TopicIDFilter -from .shared_params.region_id_filter import RegionIDFilter from .shared_params.asset_name_filter import AssetNameFilter -from .shared_params.persona_id_filter import PersonaIDFilter -__all__ = ["ReportVisibilityParams", "Filter", "FilterPromptIDFilter"] +__all__ = [ + "ReportVisibilityParams", + "Filter", + "FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersModelIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersTagIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter", + "FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter", +] class ReportVisibilityParams(TypedDict, total=False): @@ -80,7 +84,43 @@ class ReportVisibilityParams(TypedDict, total=False): """Pagination settings for the report results.""" -class FilterPromptIDFilter(TypedDict, total=False): +class FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter(TypedDict, total=False): + field: Required[Literal["region_id", "region"]] + """- `region` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersModelIDFilter(TypedDict, total=False): + field: Required[Literal["model_id", "model"]] + """- `model` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter(TypedDict, total=False): + field: Required[Literal["topic_id", "topic"]] + """- `topic` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersTagIDFilter(TypedDict, total=False): + field: Required[Literal["tag_id", "tag"]] + """- `tag` - Deprecated""" + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + +class FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter(TypedDict, total=False): field: Required[Literal["prompt_id"]] operator: Required[Literal["is", "not_is", "in", "not_in"]] @@ -88,14 +128,22 @@ class FilterPromptIDFilter(TypedDict, total=False): value: Required[Union[str, SequenceNotStr[str]]] +class FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter(TypedDict, total=False): + field: Required[Literal["persona_id"]] + + operator: Required[Literal["is", "not_is", "in", "not_in"]] + + value: Required[Union[str, SequenceNotStr[str]]] + + Filter: TypeAlias = Union[ - RegionIDFilter, - ModelIDFilter, - TopicIDFilter, + FilterProfoundAnswerEngineInsightsFiltersRegionIDFilter, + FilterProfoundAnswerEngineInsightsFiltersModelIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTopicIDFilter, TopicNameFilterParam, AssetNameFilter, - TagIDFilter, - FilterPromptIDFilter, + FilterProfoundAnswerEngineInsightsFiltersTagIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPromptIDFilter, PromptFilter, - PersonaIDFilter, + FilterProfoundAnswerEngineInsightsFiltersPersonaIDFilter, ] diff --git a/src/profound/types/shared/__init__.py b/src/profound/types/shared/__init__.py index 334ae146..b1725edc 100644 --- a/src/profound/types/shared/__init__.py +++ b/src/profound/types/shared/__init__.py @@ -3,9 +3,4 @@ from .pagination import Pagination as Pagination from .path_filter import PathFilter as PathFilter from .prompt_filter import PromptFilter as PromptFilter -from .tag_id_filter import TagIDFilter as TagIDFilter -from .model_id_filter import ModelIDFilter as ModelIDFilter -from .topic_id_filter import TopicIDFilter as TopicIDFilter -from .region_id_filter import RegionIDFilter as RegionIDFilter from .asset_name_filter import AssetNameFilter as AssetNameFilter -from .persona_id_filter import PersonaIDFilter as PersonaIDFilter diff --git a/src/profound/types/shared/model_id_filter.py b/src/profound/types/shared/model_id_filter.py deleted file mode 100644 index 44076970..00000000 --- a/src/profound/types/shared/model_id_filter.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ModelIDFilter"] - - -class ModelIDFilter(BaseModel): - field: Literal["model_id", "model"] - """- `model` - Deprecated""" - - operator: Literal["is", "not_is", "in", "not_in"] - - value: Union[str, List[str]] diff --git a/src/profound/types/shared/persona_id_filter.py b/src/profound/types/shared/persona_id_filter.py deleted file mode 100644 index e3203a3e..00000000 --- a/src/profound/types/shared/persona_id_filter.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["PersonaIDFilter"] - - -class PersonaIDFilter(BaseModel): - field: Literal["persona_id"] - - operator: Literal["is", "not_is", "in", "not_in"] - - value: Union[str, List[str]] diff --git a/src/profound/types/shared/region_id_filter.py b/src/profound/types/shared/region_id_filter.py deleted file mode 100644 index 9c1440a0..00000000 --- a/src/profound/types/shared/region_id_filter.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["RegionIDFilter"] - - -class RegionIDFilter(BaseModel): - field: Literal["region_id", "region"] - """- `region` - Deprecated""" - - operator: Literal["is", "not_is", "in", "not_in"] - - value: Union[str, List[str]] diff --git a/src/profound/types/shared/tag_id_filter.py b/src/profound/types/shared/tag_id_filter.py deleted file mode 100644 index 8ad0f2ab..00000000 --- a/src/profound/types/shared/tag_id_filter.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["TagIDFilter"] - - -class TagIDFilter(BaseModel): - field: Literal["tag_id", "tag"] - """- `tag` - Deprecated""" - - operator: Literal["is", "not_is", "in", "not_in"] - - value: Union[str, List[str]] diff --git a/src/profound/types/shared/topic_id_filter.py b/src/profound/types/shared/topic_id_filter.py deleted file mode 100644 index aa1de227..00000000 --- a/src/profound/types/shared/topic_id_filter.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["TopicIDFilter"] - - -class TopicIDFilter(BaseModel): - field: Literal["topic_id", "topic"] - """- `topic` - Deprecated""" - - operator: Literal["is", "not_is", "in", "not_in"] - - value: Union[str, List[str]] diff --git a/src/profound/types/shared_params/__init__.py b/src/profound/types/shared_params/__init__.py index 334ae146..b1725edc 100644 --- a/src/profound/types/shared_params/__init__.py +++ b/src/profound/types/shared_params/__init__.py @@ -3,9 +3,4 @@ from .pagination import Pagination as Pagination from .path_filter import PathFilter as PathFilter from .prompt_filter import PromptFilter as PromptFilter -from .tag_id_filter import TagIDFilter as TagIDFilter -from .model_id_filter import ModelIDFilter as ModelIDFilter -from .topic_id_filter import TopicIDFilter as TopicIDFilter -from .region_id_filter import RegionIDFilter as RegionIDFilter from .asset_name_filter import AssetNameFilter as AssetNameFilter -from .persona_id_filter import PersonaIDFilter as PersonaIDFilter diff --git a/src/profound/types/shared_params/model_id_filter.py b/src/profound/types/shared_params/model_id_filter.py deleted file mode 100644 index 1f587db5..00000000 --- a/src/profound/types/shared_params/model_id_filter.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["ModelIDFilter"] - - -class ModelIDFilter(TypedDict, total=False): - field: Required[Literal["model_id", "model"]] - """- `model` - Deprecated""" - - operator: Required[Literal["is", "not_is", "in", "not_in"]] - - value: Required[Union[str, SequenceNotStr[str]]] diff --git a/src/profound/types/shared_params/persona_id_filter.py b/src/profound/types/shared_params/persona_id_filter.py deleted file mode 100644 index df7ecff7..00000000 --- a/src/profound/types/shared_params/persona_id_filter.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["PersonaIDFilter"] - - -class PersonaIDFilter(TypedDict, total=False): - field: Required[Literal["persona_id"]] - - operator: Required[Literal["is", "not_is", "in", "not_in"]] - - value: Required[Union[str, SequenceNotStr[str]]] diff --git a/src/profound/types/shared_params/region_id_filter.py b/src/profound/types/shared_params/region_id_filter.py deleted file mode 100644 index ac7b47f0..00000000 --- a/src/profound/types/shared_params/region_id_filter.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["RegionIDFilter"] - - -class RegionIDFilter(TypedDict, total=False): - field: Required[Literal["region_id", "region"]] - """- `region` - Deprecated""" - - operator: Required[Literal["is", "not_is", "in", "not_in"]] - - value: Required[Union[str, SequenceNotStr[str]]] diff --git a/src/profound/types/shared_params/tag_id_filter.py b/src/profound/types/shared_params/tag_id_filter.py deleted file mode 100644 index a2f07369..00000000 --- a/src/profound/types/shared_params/tag_id_filter.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["TagIDFilter"] - - -class TagIDFilter(TypedDict, total=False): - field: Required[Literal["tag_id", "tag"]] - """- `tag` - Deprecated""" - - operator: Required[Literal["is", "not_is", "in", "not_in"]] - - value: Required[Union[str, SequenceNotStr[str]]] diff --git a/src/profound/types/shared_params/topic_id_filter.py b/src/profound/types/shared_params/topic_id_filter.py deleted file mode 100644 index b4f3f8e9..00000000 --- a/src/profound/types/shared_params/topic_id_filter.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["TopicIDFilter"] - - -class TopicIDFilter(TypedDict, total=False): - field: Required[Literal["topic_id", "topic"]] - """- `topic` - Deprecated""" - - operator: Required[Literal["is", "not_is", "in", "not_in"]] - - value: Required[Union[str, SequenceNotStr[str]]] diff --git a/tests/api_resources/logs/test_raw.py b/tests/api_resources/logs/test_raw.py index dd0d4081..ef77124c 100644 --- a/tests/api_resources/logs/test_raw.py +++ b/tests/api_resources/logs/test_raw.py @@ -46,6 +46,7 @@ def test_method_bots_with_all_params(self, client: Profound) -> None: } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -111,6 +112,7 @@ def test_method_logs_with_all_params(self, client: Profound) -> None: } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -182,6 +184,7 @@ async def test_method_bots_with_all_params(self, async_client: AsyncProfound) -> } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -247,6 +250,7 @@ async def test_method_logs_with_all_params(self, async_client: AsyncProfound) -> } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, diff --git a/tests/api_resources/test_reports.py b/tests/api_resources/test_reports.py index 6272c1f1..7a73c891 100644 --- a/tests/api_resources/test_reports.py +++ b/tests/api_resources/test_reports.py @@ -117,6 +117,7 @@ def test_method_get_bots_report_with_all_params(self, client: Profound) -> None: } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -182,6 +183,7 @@ def test_method_get_referrals_report_with_all_params(self, client: Profound) -> } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -457,6 +459,7 @@ async def test_method_get_bots_report_with_all_params(self, async_client: AsyncP } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, @@ -522,6 +525,7 @@ async def test_method_get_referrals_report_with_all_params(self, async_client: A } ], order_by={"date": "asc"}, + organization_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", pagination={ "limit": 1, "offset": 0, diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 00000000..12cdf7bc --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from profound._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs)