diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8935e93..b8dda9b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.28.0" + ".": "0.29.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index deed013..7e84f7a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-1785aa93e6fb2784640a4c32eb0221f0b31aa06644d090f72f8533524aeef7c6.yml +configured_endpoints: 25 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-30ba67603321f69c2ba0432320c45caa4727ebbe353bea8874c9e7db8b95b09e.yml openapi_spec_hash: 7dc1fadcec32637e795f641a0e97c63e -config_hash: cf2c7f48b329e0a61612b30cd2755ad1 +config_hash: f86a4e06ae5ed725aa79d58d7a8dc27c diff --git a/CHANGELOG.md b/CHANGELOG.md index c927c01..488cc29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.29.0 (2026-06-01) + +Full Changelog: [v0.28.0...v0.29.0](https://github.com/context-dot-dev/context-python-sdk/compare/v0.28.0...v0.29.0) + +### Features + +* **api:** manual updates ([9aecc62](https://github.com/context-dot-dev/context-python-sdk/commit/9aecc620c9ab42cb2453afb104022486dab7339f)) + ## 0.28.0 (2026-06-01) Full Changelog: [v0.27.0...v0.28.0](https://github.com/context-dot-dev/context-python-sdk/compare/v0.27.0...v0.28.0) diff --git a/api.md b/api.md index 4fa444b..640d179 100644 --- a/api.md +++ b/api.md @@ -5,6 +5,7 @@ Types: ```python from context.dev.types import ( WebExtractResponse, + WebExtractCompetitorsResponse, WebExtractFontsResponse, WebExtractStyleguideResponse, WebScreenshotResponse, @@ -20,6 +21,7 @@ from context.dev.types import ( Methods: - client.web.extract(\*\*params) -> WebExtractResponse +- client.web.extract_competitors(\*\*params) -> WebExtractCompetitorsResponse - client.web.extract_fonts(\*\*params) -> WebExtractFontsResponse - client.web.extract_styleguide(\*\*params) -> WebExtractStyleguideResponse - client.web.screenshot(\*\*params) -> WebScreenshotResponse diff --git a/pyproject.toml b/pyproject.toml index 8cfb067..f59cebb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "context.dev" -version = "0.28.0" +version = "0.29.0" description = "The official Python library for the context.dev API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/context/dev/_version.py b/src/context/dev/_version.py index 5d6e23e..5bbc7f0 100644 --- a/src/context/dev/_version.py +++ b/src/context/dev/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "context.dev" -__version__ = "0.28.0" # x-release-please-version +__version__ = "0.29.0" # x-release-please-version diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index dde889b..3fc6361 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -18,6 +18,7 @@ web_web_scrape_images_params, web_extract_styleguide_params, web_web_scrape_sitemap_params, + web_extract_competitors_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import maybe_transform, async_maybe_transform @@ -40,6 +41,7 @@ from ..types.web_web_scrape_images_response import WebWebScrapeImagesResponse from ..types.web_extract_styleguide_response import WebExtractStyleguideResponse from ..types.web_web_scrape_sitemap_response import WebWebScrapeSitemapResponse +from ..types.web_extract_competitors_response import WebExtractCompetitorsResponse __all__ = ["WebResource", "AsyncWebResource"] @@ -155,6 +157,60 @@ def extract( cast_to=WebExtractResponse, ) + def extract_competitors( + self, + *, + domain: str, + num_competitors: int | Omit = omit, + timeout_ms: 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, + ) -> WebExtractCompetitorsResponse: + """ + Analyze a company's landing page and web search evidence to return direct + competitors for the same product or market. + + Args: + domain: Company domain to analyze, such as `stripe.com`. Full http(s) URLs are accepted + and normalized to their domain. + + num_competitors: Exact number of direct competitors to return. Defaults to 5. + + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + + 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( + "/web/competitors", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "domain": domain, + "num_competitors": num_competitors, + "timeout_ms": timeout_ms, + }, + web_extract_competitors_params.WebExtractCompetitorsParams, + ), + ), + cast_to=WebExtractCompetitorsResponse, + ) + def extract_fonts( self, *, @@ -943,6 +999,60 @@ async def extract( cast_to=WebExtractResponse, ) + async def extract_competitors( + self, + *, + domain: str, + num_competitors: int | Omit = omit, + timeout_ms: 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, + ) -> WebExtractCompetitorsResponse: + """ + Analyze a company's landing page and web search evidence to return direct + competitors for the same product or market. + + Args: + domain: Company domain to analyze, such as `stripe.com`. Full http(s) URLs are accepted + and normalized to their domain. + + num_competitors: Exact number of direct competitors to return. Defaults to 5. + + timeout_ms: Optional timeout in milliseconds for the request. If the request takes longer + than this value, it will be aborted with a 408 status code. Maximum allowed + value is 300000ms (5 minutes). + + 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( + "/web/competitors", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "domain": domain, + "num_competitors": num_competitors, + "timeout_ms": timeout_ms, + }, + web_extract_competitors_params.WebExtractCompetitorsParams, + ), + ), + cast_to=WebExtractCompetitorsResponse, + ) + async def extract_fonts( self, *, @@ -1627,6 +1737,9 @@ def __init__(self, web: WebResource) -> None: self.extract = to_raw_response_wrapper( web.extract, ) + self.extract_competitors = to_raw_response_wrapper( + web.extract_competitors, + ) self.extract_fonts = to_raw_response_wrapper( web.extract_fonts, ) @@ -1663,6 +1776,9 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract = async_to_raw_response_wrapper( web.extract, ) + self.extract_competitors = async_to_raw_response_wrapper( + web.extract_competitors, + ) self.extract_fonts = async_to_raw_response_wrapper( web.extract_fonts, ) @@ -1699,6 +1815,9 @@ def __init__(self, web: WebResource) -> None: self.extract = to_streamed_response_wrapper( web.extract, ) + self.extract_competitors = to_streamed_response_wrapper( + web.extract_competitors, + ) self.extract_fonts = to_streamed_response_wrapper( web.extract_fonts, ) @@ -1735,6 +1854,9 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract = async_to_streamed_response_wrapper( web.extract, ) + self.extract_competitors = async_to_streamed_response_wrapper( + web.extract_competitors, + ) self.extract_fonts = async_to_streamed_response_wrapper( web.extract_fonts, ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index c24565a..91d5d38 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -35,6 +35,7 @@ from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams from .industry_retrieve_naics_params import IndustryRetrieveNaicsParams as IndustryRetrieveNaicsParams from .industry_retrieve_sic_response import IndustryRetrieveSicResponse as IndustryRetrieveSicResponse +from .web_extract_competitors_params import WebExtractCompetitorsParams as WebExtractCompetitorsParams from .web_web_scrape_images_response import WebWebScrapeImagesResponse as WebWebScrapeImagesResponse from .brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse from .brand_retrieve_by_name_response import BrandRetrieveByNameResponse as BrandRetrieveByNameResponse @@ -45,6 +46,7 @@ from .brand_retrieve_simplified_params import BrandRetrieveSimplifiedParams as BrandRetrieveSimplifiedParams from .industry_retrieve_naics_response import IndustryRetrieveNaicsResponse as IndustryRetrieveNaicsResponse from .utility_prefetch_by_email_params import UtilityPrefetchByEmailParams as UtilityPrefetchByEmailParams +from .web_extract_competitors_response import WebExtractCompetitorsResponse as WebExtractCompetitorsResponse from .brand_retrieve_by_ticker_response import BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse from .brand_retrieve_simplified_response import BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse from .utility_prefetch_by_email_response import UtilityPrefetchByEmailResponse as UtilityPrefetchByEmailResponse diff --git a/src/context/dev/types/web_extract_competitors_params.py b/src/context/dev/types/web_extract_competitors_params.py new file mode 100644 index 0000000..ecfe90e --- /dev/null +++ b/src/context/dev/types/web_extract_competitors_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_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebExtractCompetitorsParams"] + + +class WebExtractCompetitorsParams(TypedDict, total=False): + domain: Required[str] + """Company domain to analyze, such as `stripe.com`. + + Full http(s) URLs are accepted and normalized to their domain. + """ + + num_competitors: Annotated[int, PropertyInfo(alias="numCompetitors")] + """Exact number of direct competitors to return. Defaults to 5.""" + + timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] + """Optional timeout in milliseconds for the request. + + If the request takes longer than this value, it will be aborted with a 408 + status code. Maximum allowed value is 300000ms (5 minutes). + """ diff --git a/src/context/dev/types/web_extract_competitors_response.py b/src/context/dev/types/web_extract_competitors_response.py new file mode 100644 index 0000000..3f6a68c --- /dev/null +++ b/src/context/dev/types/web_extract_competitors_response.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebExtractCompetitorsResponse", "Competitor", "Target"] + + +class Competitor(BaseModel): + confidence: Literal["high", "medium"] + """Confidence that this company is a direct competitor.""" + + description: str + """Short description of the competitor.""" + + domain: str + """Competitor's normalized official domain.""" + + name: str + """Competitor company or product name.""" + + source_urls: List[str] = FieldInfo(alias="sourceUrls") + """Search result URLs used as evidence for this competitor.""" + + url: str + """Competitor website URL.""" + + +class Target(BaseModel): + """Target company profile inferred from the landing page.""" + + company_name: str = FieldInfo(alias="companyName") + """Company or product name inferred from the landing page.""" + + field: str + """Specific operating field, product category, or market.""" + + field_description: str = FieldInfo(alias="fieldDescription") + """One-sentence description of what the target company sells and who it serves.""" + + website_url: str = FieldInfo(alias="websiteUrl") + """Resolved URL used for the landing page analysis.""" + + +class WebExtractCompetitorsResponse(BaseModel): + competitors: List[Competitor] + """Direct competitors ordered by relevance and confidence.""" + + domain: str + """Normalized input domain.""" + + status: Literal["ok"] + """Status of the response.""" + + target: Target + """Target company profile inferred from the landing page.""" diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py index 1fab4ae..7da3ee5 100644 --- a/tests/api_resources/test_web.py +++ b/tests/api_resources/test_web.py @@ -20,6 +20,7 @@ WebWebScrapeImagesResponse, WebWebScrapeSitemapResponse, WebExtractStyleguideResponse, + WebExtractCompetitorsResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -107,6 +108,50 @@ def test_streaming_response_extract(self, client: ContextDev) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_extract_competitors(self, client: ContextDev) -> None: + web = client.web.extract_competitors( + domain="xxx", + ) + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_extract_competitors_with_all_params(self, client: ContextDev) -> None: + web = client.web.extract_competitors( + domain="xxx", + num_competitors=1, + timeout_ms=1000, + ) + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_extract_competitors(self, client: ContextDev) -> None: + response = client.web.with_raw_response.extract_competitors( + domain="xxx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = response.parse() + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_extract_competitors(self, client: ContextDev) -> None: + with client.web.with_streaming_response.extract_competitors( + domain="xxx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = response.parse() + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_extract_fonts(self, client: ContextDev) -> None: @@ -642,6 +687,50 @@ async def test_streaming_response_extract(self, async_client: AsyncContextDev) - assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_competitors(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_competitors( + domain="xxx", + ) + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_competitors_with_all_params(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_competitors( + domain="xxx", + num_competitors=1, + timeout_ms=1000, + ) + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_extract_competitors(self, async_client: AsyncContextDev) -> None: + response = await async_client.web.with_raw_response.extract_competitors( + domain="xxx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = await response.parse() + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_extract_competitors(self, async_client: AsyncContextDev) -> None: + async with async_client.web.with_streaming_response.extract_competitors( + domain="xxx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = await response.parse() + assert_matches_type(WebExtractCompetitorsResponse, web, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_extract_fonts(self, async_client: AsyncContextDev) -> None: