From 7c314a29f73103eb592686df101c86c6cfd26b3f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 03:16:18 +0000 Subject: [PATCH 01/27] fix: ensure file data are only sent as 1 parameter --- src/context/dev/_utils/_utils.py | 5 +++-- tests/test_extract_files.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/context/dev/_utils/_utils.py b/src/context/dev/_utils/_utils.py index eec7f4a..63b8cd6 100644 --- a/src/context/dev/_utils/_utils.py +++ b/src/context/dev/_utils/_utils.py @@ -86,8 +86,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # 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] diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index e7585ee..34b6253 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ From f40bfc4d684ebadba87a95864527073246f12a33 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:45:09 +0000 Subject: [PATCH 02/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 998ed68..e39c396 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/context-dev%2Fcontext.dev-5ee7a321f8e01d2678d56277f011f71294c2b8ac4d0cff0bda412362c0d9cf73.yml -openapi_spec_hash: bf5d57e3bddb6975770cf85c267b5035 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-1d064d1bca27e031f7d9b542b011714fb6bbe06cd96b7884ed1df1b2dd8274eb.yml +openapi_spec_hash: c92f8ae2943fcaf6bddcb605354bce8e config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From 3d6e0ec317ac58e5d9707beafb0db7f7c61bf4ec Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:52:31 +0000 Subject: [PATCH 03/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index e39c396..d6aa285 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/context-dev%2Fcontext.dev-1d064d1bca27e031f7d9b542b011714fb6bbe06cd96b7884ed1df1b2dd8274eb.yml -openapi_spec_hash: c92f8ae2943fcaf6bddcb605354bce8e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-a53e98e50a4e63511e7a494eb64d3b0617cfdb539353c0b369ba26fe38e2c069.yml +openapi_spec_hash: 2154f043db9fb371566745e96e9cc11b config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From cb002b7c430f0641bc4f9245056cf8b90c7f7ba7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:57:36 +0000 Subject: [PATCH 04/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d6aa285..6b1d08e 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/context-dev%2Fcontext.dev-a53e98e50a4e63511e7a494eb64d3b0617cfdb539353c0b369ba26fe38e2c069.yml -openapi_spec_hash: 2154f043db9fb371566745e96e9cc11b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-614b5e75731f2517a8000093185b13a53a57712f9b76fd206788ccd6f0bcef45.yml +openapi_spec_hash: 782af9c10f9fad64198c366929c66add config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From ba4723066fd967a8b79072a39447d295d7897193 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:17:07 +0000 Subject: [PATCH 05/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6b1d08e..95c414d 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/context-dev%2Fcontext.dev-614b5e75731f2517a8000093185b13a53a57712f9b76fd206788ccd6f0bcef45.yml -openapi_spec_hash: 782af9c10f9fad64198c366929c66add +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-8c7755d44545e3f10ee595024c331242e5428ba79df4710c8e02e8802219d3b6.yml +openapi_spec_hash: 699ef094c46d731c3234408056b06aef config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From b9374f57a2a0ed207c39e80b6fbf710bbe170e11 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:41:13 +0000 Subject: [PATCH 06/27] feat(api): api update --- .stats.yml | 4 +-- .../style_extract_styleguide_response.py | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 95c414d..c6a749e 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/context-dev%2Fcontext.dev-8c7755d44545e3f10ee595024c331242e5428ba79df4710c8e02e8802219d3b6.yml -openapi_spec_hash: 699ef094c46d731c3234408056b06aef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-86caee6743259bcab1e72868d17acdfc8a515d51b6d9b1e9a1dca49cdd3bd8da.yml +openapi_spec_hash: c0855025a8590139f57db43dd3e1bad1 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/types/style_extract_styleguide_response.py b/src/context/dev/types/style_extract_styleguide_response.py index 3dfe70d..7c327d8 100644 --- a/src/context/dev/types/style_extract_styleguide_response.py +++ b/src/context/dev/types/style_extract_styleguide_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Dict, List, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo @@ -18,6 +18,7 @@ "StyleguideComponentsButtonSecondary", "StyleguideComponentsCard", "StyleguideElementSpacing", + "StyleguideFontLinks", "StyleguideShadows", "StyleguideTypography", "StyleguideTypographyHeadings", @@ -244,6 +245,30 @@ class StyleguideElementSpacing(BaseModel): xs: str +class StyleguideFontLinks(BaseModel): + files: Dict[str, str] + """Upright font files keyed by weight string (e.g. + + "400" for regular, "500", "700"). Values are absolute URLs. + """ + + type: Literal["google", "custom"] + + category: Optional[str] = None + """Google Fonts category when type is google (e.g. + + sans-serif, serif, monospace, display, handwriting). Omitted for custom fonts + when unknown. + """ + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + """ + Present when type is custom: human-readable name derived from the fontLinks key + (strip build/hash suffixes, split camelCase / PascalCase, normalize separators). + Google entries omit this. + """ + + class StyleguideShadows(BaseModel): """Shadow styles used on the website""" @@ -371,6 +396,13 @@ class Styleguide(BaseModel): element_spacing: StyleguideElementSpacing = FieldInfo(alias="elementSpacing") """Spacing system used on the website""" + font_links: Dict[str, StyleguideFontLinks] = FieldInfo(alias="fontLinks") + """ + Font assets keyed by family name as it appears in fontFamily/fontFallbacks + (non-generic names only). Clients match typography.fontFamily / fontWeight or + button styles to pick a file URL from files. + """ + mode: Literal["light", "dark"] """The primary color mode of the website design""" From 3fc9944ff014539c18e0216b93c936c1df2b0219 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:20:23 +0000 Subject: [PATCH 07/27] feat(api): api update --- .stats.yml | 4 +- src/context/dev/resources/web.py | 38 ++++++++++++++++++- .../dev/types/web_web_scrape_html_params.py | 11 +++++- .../dev/types/web_web_scrape_md_params.py | 7 ++++ tests/api_resources/test_web.py | 20 ++++++++++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index c6a749e..1cbf4b7 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/context-dev%2Fcontext.dev-86caee6743259bcab1e72868d17acdfc8a515d51b6d9b1e9a1dca49cdd3bd8da.yml -openapi_spec_hash: c0855025a8590139f57db43dd3e1bad1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-955fa0d672331fb3f8b40547895bfdd1e490add254ca13db769289d3bef2ed44.yml +openapi_spec_hash: 32eca3166fb8f369c9241faf4cee3077 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 33c3d6f..7ffffea 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -209,6 +209,7 @@ def web_scrape_html( self, *, url: str, + max_age_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, @@ -222,6 +223,10 @@ def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -237,7 +242,13 @@ def web_scrape_html( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"url": url}, web_web_scrape_html_params.WebWebScrapeHTMLParams), + query=maybe_transform( + { + "url": url, + "max_age_ms": max_age_ms, + }, + web_web_scrape_html_params.WebWebScrapeHTMLParams, + ), ), cast_to=WebWebScrapeHTMLResponse, ) @@ -288,6 +299,7 @@ def web_scrape_md( url: str, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + max_age_ms: int | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -309,6 +321,10 @@ def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -334,6 +350,7 @@ def web_scrape_md( "url": url, "include_images": include_images, "include_links": include_links, + "max_age_ms": max_age_ms, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, @@ -568,6 +585,7 @@ async def web_scrape_html( self, *, url: str, + max_age_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, @@ -581,6 +599,10 @@ async def web_scrape_html( Args: url: Full URL to scrape (must include http:// or https:// protocol) + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -596,7 +618,13 @@ async def web_scrape_html( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"url": url}, web_web_scrape_html_params.WebWebScrapeHTMLParams), + query=await async_maybe_transform( + { + "url": url, + "max_age_ms": max_age_ms, + }, + web_web_scrape_html_params.WebWebScrapeHTMLParams, + ), ), cast_to=WebWebScrapeHTMLResponse, ) @@ -647,6 +675,7 @@ async def web_scrape_md( url: str, include_images: bool | Omit = omit, include_links: bool | Omit = omit, + max_age_ms: int | Omit = omit, shorten_base64_images: bool | Omit = omit, use_main_content_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -668,6 +697,10 @@ async def web_scrape_md( include_links: Preserve hyperlinks in Markdown output + max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + shorten_base64_images: Shorten base64-encoded image data in the Markdown output use_main_content_only: Extract only the main content of the page, excluding headers, footers, sidebars, @@ -693,6 +726,7 @@ async def web_scrape_md( "url": url, "include_images": include_images, "include_links": include_links, + "max_age_ms": max_age_ms, "shorten_base64_images": shorten_base64_images, "use_main_content_only": use_main_content_only, }, diff --git a/src/context/dev/types/web_web_scrape_html_params.py b/src/context/dev/types/web_web_scrape_html_params.py index 1847d07..460e3b6 100644 --- a/src/context/dev/types/web_web_scrape_html_params.py +++ b/src/context/dev/types/web_web_scrape_html_params.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["WebWebScrapeHTMLParams"] @@ -10,3 +12,10 @@ class WebWebScrapeHTMLParams(TypedDict, total=False): url: Required[str] """Full URL to scrape (must include http:// or https:// protocol)""" + + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + """ diff --git a/src/context/dev/types/web_web_scrape_md_params.py b/src/context/dev/types/web_web_scrape_md_params.py index d556da1..d7f66f1 100644 --- a/src/context/dev/types/web_web_scrape_md_params.py +++ b/src/context/dev/types/web_web_scrape_md_params.py @@ -22,6 +22,13 @@ class WebWebScrapeMdParams(TypedDict, total=False): include_links: Annotated[bool, PropertyInfo(alias="includeLinks")] """Preserve hyperlinks in Markdown output""" + max_age_ms: Annotated[int, PropertyInfo(alias="maxAgeMs")] + """ + Return a cached result if a prior scrape for the same parameters exists and is + younger than this many milliseconds. Defaults to 1 day (86400000 ms) when + omitted. Set to 0 to always scrape fresh. + """ + shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] """Shorten base64-encoded image data in the Markdown output""" diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py index 2d8a511..c974319 100644 --- a/tests/api_resources/test_web.py +++ b/tests/api_resources/test_web.py @@ -122,6 +122,15 @@ def test_method_web_scrape_html(self, client: ContextDev) -> None: ) assert_matches_type(WebWebScrapeHTMLResponse, web, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_web_scrape_html_with_all_params(self, client: ContextDev) -> None: + web = client.web.web_scrape_html( + url="https://example.com", + max_age_ms=0, + ) + assert_matches_type(WebWebScrapeHTMLResponse, web, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_web_scrape_html(self, client: ContextDev) -> None: @@ -197,6 +206,7 @@ def test_method_web_scrape_md_with_all_params(self, client: ContextDev) -> None: url="https://example.com", include_images=True, include_links=True, + max_age_ms=0, shorten_base64_images=True, use_main_content_only=True, ) @@ -375,6 +385,15 @@ async def test_method_web_scrape_html(self, async_client: AsyncContextDev) -> No ) assert_matches_type(WebWebScrapeHTMLResponse, web, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.web_scrape_html( + url="https://example.com", + max_age_ms=0, + ) + assert_matches_type(WebWebScrapeHTMLResponse, web, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_web_scrape_html(self, async_client: AsyncContextDev) -> None: @@ -450,6 +469,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncCon url="https://example.com", include_images=True, include_links=True, + max_age_ms=0, shorten_base64_images=True, use_main_content_only=True, ) From 656e585bcdf228d8651cdab5ee37f1119a51eafa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:36:48 +0000 Subject: [PATCH 08/27] feat(api): api update --- .stats.yml | 4 ++-- src/context/dev/resources/web.py | 4 ++-- src/context/dev/types/web_web_scrape_html_params.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1cbf4b7..7be40e1 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/context-dev%2Fcontext.dev-955fa0d672331fb3f8b40547895bfdd1e490add254ca13db769289d3bef2ed44.yml -openapi_spec_hash: 32eca3166fb8f369c9241faf4cee3077 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-c023cdf4ae403e8c32724869eff157cb24d4436b7c048809571c41f08dd20e03.yml +openapi_spec_hash: b92878e27bdb184e1506f4baf3280118 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 7ffffea..7efd77c 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -225,7 +225,7 @@ def web_scrape_html( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. extra_headers: Send extra headers @@ -601,7 +601,7 @@ async def web_scrape_html( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. extra_headers: Send extra headers diff --git a/src/context/dev/types/web_web_scrape_html_params.py b/src/context/dev/types/web_web_scrape_html_params.py index 460e3b6..d184801 100644 --- a/src/context/dev/types/web_web_scrape_html_params.py +++ b/src/context/dev/types/web_web_scrape_html_params.py @@ -17,5 +17,5 @@ class WebWebScrapeHTMLParams(TypedDict, total=False): """ Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ From 648e71bf7afb870dceafe6485bf47932c74ebe1b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:45:11 +0000 Subject: [PATCH 09/27] feat(api): api update --- .stats.yml | 4 ++-- src/context/dev/resources/web.py | 14 ++++++-------- src/context/dev/types/web_web_scrape_md_params.py | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7be40e1..2ddea68 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/context-dev%2Fcontext.dev-c023cdf4ae403e8c32724869eff157cb24d4436b7c048809571c41f08dd20e03.yml -openapi_spec_hash: b92878e27bdb184e1506f4baf3280118 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-8ef1d8ed8ceacb8e8e73a5b6069c63adc9c0fd74d9b9aa9dee8b218cf94eac41.yml +openapi_spec_hash: 5271c711707ccf1deb2a47e0fb1119b7 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 7efd77c..b419c68 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -310,11 +310,10 @@ def web_scrape_md( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeMdResponse: """ - Scrapes the given URL, converts the HTML content to Markdown, and returns the - result. + Scrapes the given URL into LLM usable Markdown. Args: - url: Full URL to scrape and convert to markdown (must include http:// or https:// + url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) include_images: Include image references in Markdown output @@ -323,7 +322,7 @@ def web_scrape_md( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. shorten_base64_images: Shorten base64-encoded image data in the Markdown output @@ -686,11 +685,10 @@ async def web_scrape_md( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeMdResponse: """ - Scrapes the given URL, converts the HTML content to Markdown, and returns the - result. + Scrapes the given URL into LLM usable Markdown. Args: - url: Full URL to scrape and convert to markdown (must include http:// or https:// + url: Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) include_images: Include image references in Markdown output @@ -699,7 +697,7 @@ async def web_scrape_md( max_age_ms: Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. shorten_base64_images: Shorten base64-encoded image data in the Markdown output diff --git a/src/context/dev/types/web_web_scrape_md_params.py b/src/context/dev/types/web_web_scrape_md_params.py index d7f66f1..8cc9333 100644 --- a/src/context/dev/types/web_web_scrape_md_params.py +++ b/src/context/dev/types/web_web_scrape_md_params.py @@ -12,7 +12,7 @@ class WebWebScrapeMdParams(TypedDict, total=False): url: Required[str] """ - Full URL to scrape and convert to markdown (must include http:// or https:// + Full URL to scrape into LLM usable Markdown (must include http:// or https:// protocol) """ @@ -26,7 +26,7 @@ class WebWebScrapeMdParams(TypedDict, total=False): """ Return a cached result if a prior scrape for the same parameters exists and is younger than this many milliseconds. Defaults to 1 day (86400000 ms) when - omitted. Set to 0 to always scrape fresh. + omitted. Max is 30 days (2592000000 ms). Set to 0 to always scrape fresh. """ shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")] From f5cd0d05f4bff00de8bd637de37b27d21b37b22d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:56:38 +0000 Subject: [PATCH 10/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2ddea68..88a619f 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/context-dev%2Fcontext.dev-8ef1d8ed8ceacb8e8e73a5b6069c63adc9c0fd74d9b9aa9dee8b218cf94eac41.yml -openapi_spec_hash: 5271c711707ccf1deb2a47e0fb1119b7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-6358650aa8bbd5ba9a423f2c146607c43c09ce92e455e7905a94ce64c032551b.yml +openapi_spec_hash: a6734ff6462155d6d2f0be39185dcfdc config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From 241bacf196c1e316aef39429e3919fda1c5a78eb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:06:05 +0000 Subject: [PATCH 11/27] feat(api): api update --- .stats.yml | 4 ++-- src/context/dev/resources/web.py | 14 ++++---------- .../dev/types/web_web_scrape_sitemap_params.py | 5 +---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.stats.yml b/.stats.yml index 88a619f..2f7dc9a 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/context-dev%2Fcontext.dev-6358650aa8bbd5ba9a423f2c146607c43c09ce92e455e7905a94ce64c032551b.yml -openapi_spec_hash: a6734ff6462155d6d2f0be39185dcfdc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-710f2543cb3ae7b29cf20a527e5fcca5f3aa518c7435ecb2b84fb9f8fe61506c.yml +openapi_spec_hash: 219f0884a214dba75a2e971dc5962804 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index b419c68..9858258 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -372,13 +372,10 @@ def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeSitemapResponse: """ - Crawls the sitemap of the given domain and returns all discovered page URLs. - Supports sitemap index files (recursive), parallel fetching with concurrency - control, deduplication, and filters out non-page resources (images, PDFs, etc.). + Crawl an entire website's sitemap and return all discovered page URLs Args: - domain: Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be - automatically normalized and validated. + domain: Domain to build a sitemap for max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. @@ -747,13 +744,10 @@ async def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeSitemapResponse: """ - Crawls the sitemap of the given domain and returns all discovered page URLs. - Supports sitemap index files (recursive), parallel fetching with concurrency - control, deduplication, and filters out non-page resources (images, PDFs, etc.). + Crawl an entire website's sitemap and return all discovered page URLs Args: - domain: Domain name to crawl sitemaps for (e.g., 'example.com'). The domain will be - automatically normalized and validated. + domain: Domain to build a sitemap for max_links: Maximum number of links to return from the sitemap crawl. Defaults to 10,000. Minimum is 1, maximum is 100,000. diff --git a/src/context/dev/types/web_web_scrape_sitemap_params.py b/src/context/dev/types/web_web_scrape_sitemap_params.py index f78f498..4a1c766 100644 --- a/src/context/dev/types/web_web_scrape_sitemap_params.py +++ b/src/context/dev/types/web_web_scrape_sitemap_params.py @@ -11,10 +11,7 @@ class WebWebScrapeSitemapParams(TypedDict, total=False): domain: Required[str] - """Domain name to crawl sitemaps for (e.g., 'example.com'). - - The domain will be automatically normalized and validated. - """ + """Domain to build a sitemap for""" max_links: Annotated[int, PropertyInfo(alias="maxLinks")] """Maximum number of links to return from the sitemap crawl. From bd68035907a2c0ab41cff19d74beed560764fa1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:18:41 +0000 Subject: [PATCH 12/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2f7dc9a..de8e2b5 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/context-dev%2Fcontext.dev-710f2543cb3ae7b29cf20a527e5fcca5f3aa518c7435ecb2b84fb9f8fe61506c.yml -openapi_spec_hash: 219f0884a214dba75a2e971dc5962804 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-e8d8a7fcb448cc026cb7afe7f0d0d927cdb8bc86ae841ed14377107ce2c0e039.yml +openapi_spec_hash: 40ff2ba6f9f52e728b790acd2c85af8c config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce From bbc2458c38933208b22e234d6bf1f1ffee7f883e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:23:41 +0000 Subject: [PATCH 13/27] feat(api): api update --- .stats.yml | 4 ++-- src/context/dev/resources/web.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index de8e2b5..354168e 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/context-dev%2Fcontext.dev-e8d8a7fcb448cc026cb7afe7f0d0d927cdb8bc86ae841ed14377107ce2c0e039.yml -openapi_spec_hash: 40ff2ba6f9f52e728b790acd2c85af8c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-93024db99e0ac34b761b052b495751dddc318c87b9492cf88d8c1b03c7163277.yml +openapi_spec_hash: bae5518ec851fa744b0025676df65ec6 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 9858258..398dc75 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -150,8 +150,7 @@ def web_crawl_md( ) -> WebWebCrawlMdResponse: """ Performs a crawl starting from a given URL, extracts page content as Markdown, - and returns results for all crawled pages. Only follows links within the same - domain as the starting URL. Costs 1 credit per successful page crawled. + and returns results for all crawled pages. Args: url: The starting URL for the crawl (must include http:// or https:// protocol) @@ -522,8 +521,7 @@ async def web_crawl_md( ) -> WebWebCrawlMdResponse: """ Performs a crawl starting from a given URL, extracts page content as Markdown, - and returns results for all crawled pages. Only follows links within the same - domain as the starting URL. Costs 1 credit per successful page crawled. + and returns results for all crawled pages. Args: url: The starting URL for the crawl (must include http:// or https:// protocol) From 707a6c3e49096e2248ec9e4ae7e683138647c2d6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 21:54:54 +0000 Subject: [PATCH 14/27] feat(api): api update --- .stats.yml | 6 +- api.md | 3 +- src/context/dev/resources/style.py | 115 +----------------- src/context/dev/types/__init__.py | 2 - .../dev/types/style_extract_fonts_params.py | 24 ---- .../dev/types/style_extract_fonts_response.py | 44 ------- tests/api_resources/test_style.py | 91 +------------- 7 files changed, 6 insertions(+), 279 deletions(-) delete mode 100644 src/context/dev/types/style_extract_fonts_params.py delete mode 100644 src/context/dev/types/style_extract_fonts_response.py diff --git a/.stats.yml b/.stats.yml index 354168e..4f702fa 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/context-dev%2Fcontext.dev-93024db99e0ac34b761b052b495751dddc318c87b9492cf88d8c1b03c7163277.yml -openapi_spec_hash: bae5518ec851fa744b0025676df65ec6 +configured_endpoints: 20 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-fabb017ff3666dbd9571166423c32c9567f9c09448fd7189d147c06666bf0f39.yml +openapi_spec_hash: 2175516001dec1c8c45a1c8416c6fe53 config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce diff --git a/api.md b/api.md index 4c7de31..da5218b 100644 --- a/api.md +++ b/api.md @@ -41,12 +41,11 @@ Methods: Types: ```python -from context.dev.types import StyleExtractFontsResponse, StyleExtractStyleguideResponse +from context.dev.types import StyleExtractStyleguideResponse ``` Methods: -- client.style.extract_fonts(\*\*params) -> StyleExtractFontsResponse - client.style.extract_styleguide(\*\*params) -> StyleExtractStyleguideResponse # Brand diff --git a/src/context/dev/resources/style.py b/src/context/dev/resources/style.py index 36ea477..4bf4845 100644 --- a/src/context/dev/resources/style.py +++ b/src/context/dev/resources/style.py @@ -4,7 +4,7 @@ import httpx -from ..types import style_extract_fonts_params, style_extract_styleguide_params +from ..types import style_extract_styleguide_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -16,7 +16,6 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options -from ..types.style_extract_fonts_response import StyleExtractFontsResponse from ..types.style_extract_styleguide_response import StyleExtractStyleguideResponse __all__ = ["StyleResource", "AsyncStyleResource"] @@ -42,56 +41,6 @@ def with_streaming_response(self) -> StyleResourceWithStreamingResponse: """ return StyleResourceWithStreamingResponse(self) - def extract_fonts( - self, - *, - domain: str, - 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, - ) -> StyleExtractFontsResponse: - """ - Extract font information from a brand's website including font families, usage - statistics, fallbacks, and element/word counts. - - Args: - domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - 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( - "/brand/fonts", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "domain": domain, - "timeout_ms": timeout_ms, - }, - style_extract_fonts_params.StyleExtractFontsParams, - ), - ), - cast_to=StyleExtractFontsResponse, - ) - def extract_styleguide( self, *, @@ -170,56 +119,6 @@ def with_streaming_response(self) -> AsyncStyleResourceWithStreamingResponse: """ return AsyncStyleResourceWithStreamingResponse(self) - async def extract_fonts( - self, - *, - domain: str, - 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, - ) -> StyleExtractFontsResponse: - """ - Extract font information from a brand's website including font families, usage - statistics, fallbacks, and element/word counts. - - Args: - domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - 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( - "/brand/fonts", - 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, - "timeout_ms": timeout_ms, - }, - style_extract_fonts_params.StyleExtractFontsParams, - ), - ), - cast_to=StyleExtractFontsResponse, - ) - async def extract_styleguide( self, *, @@ -282,9 +181,6 @@ class StyleResourceWithRawResponse: def __init__(self, style: StyleResource) -> None: self._style = style - self.extract_fonts = to_raw_response_wrapper( - style.extract_fonts, - ) self.extract_styleguide = to_raw_response_wrapper( style.extract_styleguide, ) @@ -294,9 +190,6 @@ class AsyncStyleResourceWithRawResponse: def __init__(self, style: AsyncStyleResource) -> None: self._style = style - self.extract_fonts = async_to_raw_response_wrapper( - style.extract_fonts, - ) self.extract_styleguide = async_to_raw_response_wrapper( style.extract_styleguide, ) @@ -306,9 +199,6 @@ class StyleResourceWithStreamingResponse: def __init__(self, style: StyleResource) -> None: self._style = style - self.extract_fonts = to_streamed_response_wrapper( - style.extract_fonts, - ) self.extract_styleguide = to_streamed_response_wrapper( style.extract_styleguide, ) @@ -318,9 +208,6 @@ class AsyncStyleResourceWithStreamingResponse: def __init__(self, style: AsyncStyleResource) -> None: self._style = style - self.extract_fonts = async_to_streamed_response_wrapper( - style.extract_fonts, - ) self.extract_styleguide = async_to_streamed_response_wrapper( style.extract_styleguide, ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 4053426..773db09 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -15,12 +15,10 @@ from .utility_prefetch_response import UtilityPrefetchResponse as UtilityPrefetchResponse from .web_web_crawl_md_response import WebWebCrawlMdResponse as WebWebCrawlMdResponse from .ai_extract_products_params import AIExtractProductsParams as AIExtractProductsParams -from .style_extract_fonts_params import StyleExtractFontsParams as StyleExtractFontsParams from .web_web_scrape_html_params import WebWebScrapeHTMLParams as WebWebScrapeHTMLParams from .web_web_scrape_md_response import WebWebScrapeMdResponse as WebWebScrapeMdResponse from .ai_extract_product_response import AIExtractProductResponse as AIExtractProductResponse from .ai_extract_products_response import AIExtractProductsResponse as AIExtractProductsResponse -from .style_extract_fonts_response import StyleExtractFontsResponse as StyleExtractFontsResponse from .web_web_scrape_html_response import WebWebScrapeHTMLResponse as WebWebScrapeHTMLResponse from .web_web_scrape_images_params import WebWebScrapeImagesParams as WebWebScrapeImagesParams from .brand_retrieve_by_isin_params import BrandRetrieveByIsinParams as BrandRetrieveByIsinParams diff --git a/src/context/dev/types/style_extract_fonts_params.py b/src/context/dev/types/style_extract_fonts_params.py deleted file mode 100644 index 178f6f3..0000000 --- a/src/context/dev/types/style_extract_fonts_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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__ = ["StyleExtractFontsParams"] - - -class StyleExtractFontsParams(TypedDict, total=False): - domain: Required[str] - """Domain name to extract fonts from (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - 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/style_extract_fonts_response.py b/src/context/dev/types/style_extract_fonts_response.py deleted file mode 100644 index ab00e10..0000000 --- a/src/context/dev/types/style_extract_fonts_response.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from .._models import BaseModel - -__all__ = ["StyleExtractFontsResponse", "Font"] - - -class Font(BaseModel): - fallbacks: List[str] - """Array of fallback font families""" - - font: str - """Font family name""" - - num_elements: float - """Number of elements using this font""" - - num_words: float - """Number of words using this font""" - - percent_elements: float - """Percentage of elements using this font""" - - percent_words: float - """Percentage of words using this font""" - - uses: List[str] - """Array of CSS selectors or element types where this font is used""" - - -class StyleExtractFontsResponse(BaseModel): - code: int - """HTTP status code, e.g., 200""" - - domain: str - """The normalized domain that was processed""" - - fonts: List[Font] - """Array of font usage information""" - - status: str - """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_style.py b/tests/api_resources/test_style.py index 8c9f16a..3206971 100644 --- a/tests/api_resources/test_style.py +++ b/tests/api_resources/test_style.py @@ -9,10 +9,7 @@ from context.dev import ContextDev, AsyncContextDev from tests.utils import assert_matches_type -from context.dev.types import ( - StyleExtractFontsResponse, - StyleExtractStyleguideResponse, -) +from context.dev.types import StyleExtractStyleguideResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,49 +17,6 @@ class TestStyle: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_extract_fonts(self, client: ContextDev) -> None: - style = client.style.extract_fonts( - domain="domain", - ) - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_extract_fonts_with_all_params(self, client: ContextDev) -> None: - style = client.style.extract_fonts( - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_extract_fonts(self, client: ContextDev) -> None: - response = client.style.with_raw_response.extract_fonts( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - style = response.parse() - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_extract_fonts(self, client: ContextDev) -> None: - with client.style.with_streaming_response.extract_fonts( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - style = response.parse() - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_extract_styleguide(self, client: ContextDev) -> None: @@ -107,49 +61,6 @@ class TestAsyncStyle: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_extract_fonts(self, async_client: AsyncContextDev) -> None: - style = await async_client.style.extract_fonts( - domain="domain", - ) - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_extract_fonts_with_all_params(self, async_client: AsyncContextDev) -> None: - style = await async_client.style.extract_fonts( - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_extract_fonts(self, async_client: AsyncContextDev) -> None: - response = await async_client.style.with_raw_response.extract_fonts( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - style = await response.parse() - assert_matches_type(StyleExtractFontsResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_extract_fonts(self, async_client: AsyncContextDev) -> None: - async with async_client.style.with_streaming_response.extract_fonts( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - style = await response.parse() - assert_matches_type(StyleExtractFontsResponse, style, 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_styleguide(self, async_client: AsyncContextDev) -> None: From ec8ff2d2a0d84fcb0c05791fa913441c60ed9c24 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:00:38 +0000 Subject: [PATCH 15/27] feat(api): manual updates --- .stats.yml | 4 +- api.md | 2 + src/context/dev/resources/web.py | 126 ++++++++++++++++++ src/context/dev/types/__init__.py | 2 + .../dev/types/web_extract_fonts_params.py | 30 +++++ .../dev/types/web_extract_fonts_response.py | 44 ++++++ tests/api_resources/test_web.py | 77 +++++++++++ 7 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 src/context/dev/types/web_extract_fonts_params.py create mode 100644 src/context/dev/types/web_extract_fonts_response.py diff --git a/.stats.yml b/.stats.yml index 4f702fa..c5d0c12 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 +configured_endpoints: 21 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-fabb017ff3666dbd9571166423c32c9567f9c09448fd7189d147c06666bf0f39.yml openapi_spec_hash: 2175516001dec1c8c45a1c8416c6fe53 -config_hash: 682b89b02a20f5d1c13e2c91ecbcf5ce +config_hash: 859455bbffc8a009cd9b5eed9b3ba2d7 diff --git a/api.md b/api.md index da5218b..0f123c4 100644 --- a/api.md +++ b/api.md @@ -4,6 +4,7 @@ Types: ```python from context.dev.types import ( + WebExtractFontsResponse, WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeHTMLResponse, @@ -15,6 +16,7 @@ from context.dev.types import ( Methods: +- client.web.extract_fonts(\*\*params) -> WebExtractFontsResponse - client.web.screenshot(\*\*params) -> WebScreenshotResponse - client.web.web_crawl_md(\*\*params) -> WebWebCrawlMdResponse - client.web.web_scrape_html(\*\*params) -> WebWebScrapeHTMLResponse diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 398dc75..362b501 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -9,6 +9,7 @@ from ..types import ( web_screenshot_params, web_web_crawl_md_params, + web_extract_fonts_params, web_web_scrape_md_params, web_web_scrape_html_params, web_web_scrape_images_params, @@ -27,6 +28,7 @@ from .._base_client import make_request_options from ..types.web_screenshot_response import WebScreenshotResponse from ..types.web_web_crawl_md_response import WebWebCrawlMdResponse +from ..types.web_extract_fonts_response import WebExtractFontsResponse from ..types.web_web_scrape_md_response import WebWebScrapeMdResponse from ..types.web_web_scrape_html_response import WebWebScrapeHTMLResponse from ..types.web_web_scrape_images_response import WebWebScrapeImagesResponse @@ -55,6 +57,62 @@ def with_streaming_response(self) -> WebResourceWithStreamingResponse: """ return WebResourceWithStreamingResponse(self) + def extract_fonts( + self, + *, + direct_url: str | Omit = omit, + domain: str | 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, + ) -> WebExtractFontsResponse: + """ + Scrape font information from a website including font families, usage + statistics, fallbacks, and element/word counts. Either 'domain' or 'directUrl' + must be provided as a query parameter, but not both. + + Args: + direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., + 'https://example.com/design-system'). + + domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. + + 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/fonts", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "timeout_ms": timeout_ms, + }, + web_extract_fonts_params.WebExtractFontsParams, + ), + ), + cast_to=WebExtractFontsResponse, + ) + def screenshot( self, *, @@ -426,6 +484,62 @@ def with_streaming_response(self) -> AsyncWebResourceWithStreamingResponse: """ return AsyncWebResourceWithStreamingResponse(self) + async def extract_fonts( + self, + *, + direct_url: str | Omit = omit, + domain: str | 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, + ) -> WebExtractFontsResponse: + """ + Scrape font information from a website including font families, usage + statistics, fallbacks, and element/word counts. Either 'domain' or 'directUrl' + must be provided as a query parameter, but not both. + + Args: + direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., + 'https://example.com/design-system'). + + domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. + + 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/fonts", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "timeout_ms": timeout_ms, + }, + web_extract_fonts_params.WebExtractFontsParams, + ), + ), + cast_to=WebExtractFontsResponse, + ) + async def screenshot( self, *, @@ -781,6 +895,9 @@ class WebResourceWithRawResponse: def __init__(self, web: WebResource) -> None: self._web = web + self.extract_fonts = to_raw_response_wrapper( + web.extract_fonts, + ) self.screenshot = to_raw_response_wrapper( web.screenshot, ) @@ -805,6 +922,9 @@ class AsyncWebResourceWithRawResponse: def __init__(self, web: AsyncWebResource) -> None: self._web = web + self.extract_fonts = async_to_raw_response_wrapper( + web.extract_fonts, + ) self.screenshot = async_to_raw_response_wrapper( web.screenshot, ) @@ -829,6 +949,9 @@ class WebResourceWithStreamingResponse: def __init__(self, web: WebResource) -> None: self._web = web + self.extract_fonts = to_streamed_response_wrapper( + web.extract_fonts, + ) self.screenshot = to_streamed_response_wrapper( web.screenshot, ) @@ -853,6 +976,9 @@ class AsyncWebResourceWithStreamingResponse: def __init__(self, web: AsyncWebResource) -> None: self._web = web + self.extract_fonts = async_to_streamed_response_wrapper( + web.extract_fonts, + ) self.screenshot = async_to_streamed_response_wrapper( web.screenshot, ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 773db09..fb1616f 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -10,11 +10,13 @@ from .utility_prefetch_params import UtilityPrefetchParams as UtilityPrefetchParams from .web_screenshot_response import WebScreenshotResponse as WebScreenshotResponse from .web_web_crawl_md_params import WebWebCrawlMdParams as WebWebCrawlMdParams +from .web_extract_fonts_params import WebExtractFontsParams as WebExtractFontsParams from .web_web_scrape_md_params import WebWebScrapeMdParams as WebWebScrapeMdParams from .ai_extract_product_params import AIExtractProductParams as AIExtractProductParams from .utility_prefetch_response import UtilityPrefetchResponse as UtilityPrefetchResponse from .web_web_crawl_md_response import WebWebCrawlMdResponse as WebWebCrawlMdResponse from .ai_extract_products_params import AIExtractProductsParams as AIExtractProductsParams +from .web_extract_fonts_response import WebExtractFontsResponse as WebExtractFontsResponse from .web_web_scrape_html_params import WebWebScrapeHTMLParams as WebWebScrapeHTMLParams from .web_web_scrape_md_response import WebWebScrapeMdResponse as WebWebScrapeMdResponse from .ai_extract_product_response import AIExtractProductResponse as AIExtractProductResponse diff --git a/src/context/dev/types/web_extract_fonts_params.py b/src/context/dev/types/web_extract_fonts_params.py new file mode 100644 index 0000000..db307de --- /dev/null +++ b/src/context/dev/types/web_extract_fonts_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 Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebExtractFontsParams"] + + +class WebExtractFontsParams(TypedDict, total=False): + direct_url: Annotated[str, PropertyInfo(alias="directUrl")] + """ + A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., + 'https://example.com/design-system'). + """ + + domain: str + """Domain name to extract fonts from (e.g., 'example.com', 'google.com'). + + The domain will be automatically normalized and validated. + """ + + 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_fonts_response.py b/src/context/dev/types/web_extract_fonts_response.py new file mode 100644 index 0000000..55fec61 --- /dev/null +++ b/src/context/dev/types/web_extract_fonts_response.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel + +__all__ = ["WebExtractFontsResponse", "Font"] + + +class Font(BaseModel): + fallbacks: List[str] + """Array of fallback font families""" + + font: str + """Font family name""" + + num_elements: float + """Number of elements using this font""" + + num_words: float + """Number of words using this font""" + + percent_elements: float + """Percentage of elements using this font""" + + percent_words: float + """Percentage of words using this font""" + + uses: List[str] + """Array of CSS selectors or element types where this font is used""" + + +class WebExtractFontsResponse(BaseModel): + code: int + """HTTP status code, e.g., 200""" + + domain: str + """The normalized domain that was processed""" + + fonts: List[Font] + """Array of font usage information""" + + status: str + """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py index c974319..f78e5b7 100644 --- a/tests/api_resources/test_web.py +++ b/tests/api_resources/test_web.py @@ -13,6 +13,7 @@ WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeMdResponse, + WebExtractFontsResponse, WebWebScrapeHTMLResponse, WebWebScrapeImagesResponse, WebWebScrapeSitemapResponse, @@ -24,6 +25,44 @@ class TestWeb: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_extract_fonts(self, client: ContextDev) -> None: + web = client.web.extract_fonts() + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_extract_fonts_with_all_params(self, client: ContextDev) -> None: + web = client.web.extract_fonts( + direct_url="https://example.com", + domain="domain", + timeout_ms=1000, + ) + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_extract_fonts(self, client: ContextDev) -> None: + response = client.web.with_raw_response.extract_fonts() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = response.parse() + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_extract_fonts(self, client: ContextDev) -> None: + with client.web.with_streaming_response.extract_fonts() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = response.parse() + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_screenshot(self, client: ContextDev) -> None: @@ -287,6 +326,44 @@ class TestAsyncWeb: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_fonts(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_fonts() + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_fonts_with_all_params(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_fonts( + direct_url="https://example.com", + domain="domain", + timeout_ms=1000, + ) + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_extract_fonts(self, async_client: AsyncContextDev) -> None: + response = await async_client.web.with_raw_response.extract_fonts() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = await response.parse() + assert_matches_type(WebExtractFontsResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_extract_fonts(self, async_client: AsyncContextDev) -> None: + async with async_client.web.with_streaming_response.extract_fonts() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = await response.parse() + assert_matches_type(WebExtractFontsResponse, 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_screenshot(self, async_client: AsyncContextDev) -> None: From 639075e0a0aa6203375592b8b67438ae0719ca26 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 22:18:20 +0000 Subject: [PATCH 16/27] feat(api): api update --- .stats.yml | 6 +- api.md | 2 - src/context/dev/resources/web.py | 170 +----------------- src/context/dev/types/__init__.py | 2 - .../dev/types/web_screenshot_params.py | 48 ----- .../dev/types/web_screenshot_response.py | 27 --- tests/api_resources/test_web.py | 81 --------- 7 files changed, 5 insertions(+), 331 deletions(-) delete mode 100644 src/context/dev/types/web_screenshot_params.py delete mode 100644 src/context/dev/types/web_screenshot_response.py diff --git a/.stats.yml b/.stats.yml index c5d0c12..2ed55ae 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/context-dev%2Fcontext.dev-fabb017ff3666dbd9571166423c32c9567f9c09448fd7189d147c06666bf0f39.yml -openapi_spec_hash: 2175516001dec1c8c45a1c8416c6fe53 +configured_endpoints: 20 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-142c1d289d90b82edca3269964fc4fd6595f0e97c36b42a88b6f774fa5e65c10.yml +openapi_spec_hash: 1dc3e05add427f444e7714173c2fe8a9 config_hash: 859455bbffc8a009cd9b5eed9b3ba2d7 diff --git a/api.md b/api.md index 0f123c4..614b840 100644 --- a/api.md +++ b/api.md @@ -5,7 +5,6 @@ Types: ```python from context.dev.types import ( WebExtractFontsResponse, - WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeHTMLResponse, WebWebScrapeImagesResponse, @@ -17,7 +16,6 @@ from context.dev.types import ( Methods: - client.web.extract_fonts(\*\*params) -> WebExtractFontsResponse -- client.web.screenshot(\*\*params) -> WebScreenshotResponse - client.web.web_crawl_md(\*\*params) -> WebWebCrawlMdResponse - client.web.web_scrape_html(\*\*params) -> WebWebScrapeHTMLResponse - client.web.web_scrape_images(\*\*params) -> WebWebScrapeImagesResponse diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 362b501..6d1dcac 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -2,12 +2,9 @@ from __future__ import annotations -from typing_extensions import Literal - import httpx from ..types import ( - web_screenshot_params, web_web_crawl_md_params, web_extract_fonts_params, web_web_scrape_md_params, @@ -26,7 +23,6 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options -from ..types.web_screenshot_response import WebScreenshotResponse from ..types.web_web_crawl_md_response import WebWebCrawlMdResponse from ..types.web_extract_fonts_response import WebExtractFontsResponse from ..types.web_web_scrape_md_response import WebWebScrapeMdResponse @@ -72,8 +68,7 @@ def extract_fonts( ) -> WebExtractFontsResponse: """ Scrape font information from a website including font families, usage - statistics, fallbacks, and element/word counts. Either 'domain' or 'directUrl' - must be provided as a query parameter, but not both. + statistics, fallbacks, and element/word counts. Args: direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., @@ -113,80 +108,6 @@ def extract_fonts( cast_to=WebExtractFontsResponse, ) - def screenshot( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - full_screenshot: Literal["true", "false"] | Omit = omit, - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, - prioritize: Literal["speed", "quality"] | 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, - ) -> WebScreenshotResponse: - """Capture a screenshot of a website. - - Supports both viewport (standard browser - view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' - or 'directUrl' must be provided as a query parameter, but not both. Returns a - URL to the uploaded screenshot image hosted on our CDN. - - Args: - direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - - domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page - screenshot capturing all content. If 'false' or not provided, takes a viewport - screenshot (standard browser view). - - page: Optional parameter to specify which page type to screenshot. If provided, the - system will scrape the domain's links and use heuristics to find the most - appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. Only applicable when using - 'domain', not 'directUrl'. - - prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for - faster capture with basic quality. If 'quality', optimizes for higher quality - with longer wait times. Defaults to 'quality' if not provided. - - 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( - "/brand/screenshot", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "full_screenshot": full_screenshot, - "page": page, - "prioritize": prioritize, - }, - web_screenshot_params.WebScreenshotParams, - ), - ), - cast_to=WebScreenshotResponse, - ) - def web_crawl_md( self, *, @@ -499,8 +420,7 @@ async def extract_fonts( ) -> WebExtractFontsResponse: """ Scrape font information from a website including font families, usage - statistics, fallbacks, and element/word counts. Either 'domain' or 'directUrl' - must be provided as a query parameter, but not both. + statistics, fallbacks, and element/word counts. Args: direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., @@ -540,80 +460,6 @@ async def extract_fonts( cast_to=WebExtractFontsResponse, ) - async def screenshot( - self, - *, - direct_url: str | Omit = omit, - domain: str | Omit = omit, - full_screenshot: Literal["true", "false"] | Omit = omit, - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, - prioritize: Literal["speed", "quality"] | 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, - ) -> WebScreenshotResponse: - """Capture a screenshot of a website. - - Supports both viewport (standard browser - view) and full-page screenshots. Can also screenshot specific page types (login, - pricing, etc.) by using heuristics to find the appropriate URL. Either 'domain' - or 'directUrl' must be provided as a query parameter, but not both. Returns a - URL to the uploaded screenshot image hosted on our CDN. - - Args: - direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - - domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page - screenshot capturing all content. If 'false' or not provided, takes a viewport - screenshot (standard browser view). - - page: Optional parameter to specify which page type to screenshot. If provided, the - system will scrape the domain's links and use heuristics to find the most - appropriate URL for the specified page type (30 supported languages). If not - provided, screenshots the main domain landing page. Only applicable when using - 'domain', not 'directUrl'. - - prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for - faster capture with basic quality. If 'quality', optimizes for higher quality - with longer wait times. Defaults to 'quality' if not provided. - - 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( - "/brand/screenshot", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "full_screenshot": full_screenshot, - "page": page, - "prioritize": prioritize, - }, - web_screenshot_params.WebScreenshotParams, - ), - ), - cast_to=WebScreenshotResponse, - ) - async def web_crawl_md( self, *, @@ -898,9 +744,6 @@ def __init__(self, web: WebResource) -> None: self.extract_fonts = to_raw_response_wrapper( web.extract_fonts, ) - self.screenshot = to_raw_response_wrapper( - web.screenshot, - ) self.web_crawl_md = to_raw_response_wrapper( web.web_crawl_md, ) @@ -925,9 +768,6 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract_fonts = async_to_raw_response_wrapper( web.extract_fonts, ) - self.screenshot = async_to_raw_response_wrapper( - web.screenshot, - ) self.web_crawl_md = async_to_raw_response_wrapper( web.web_crawl_md, ) @@ -952,9 +792,6 @@ def __init__(self, web: WebResource) -> None: self.extract_fonts = to_streamed_response_wrapper( web.extract_fonts, ) - self.screenshot = to_streamed_response_wrapper( - web.screenshot, - ) self.web_crawl_md = to_streamed_response_wrapper( web.web_crawl_md, ) @@ -979,9 +816,6 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract_fonts = async_to_streamed_response_wrapper( web.extract_fonts, ) - self.screenshot = async_to_streamed_response_wrapper( - web.screenshot, - ) self.web_crawl_md = async_to_streamed_response_wrapper( web.web_crawl_md, ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index fb1616f..3cc5ca9 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -5,10 +5,8 @@ from .ai_ai_query_params import AIAIQueryParams as AIAIQueryParams from .ai_ai_query_response import AIAIQueryResponse as AIAIQueryResponse from .brand_retrieve_params import BrandRetrieveParams as BrandRetrieveParams -from .web_screenshot_params import WebScreenshotParams as WebScreenshotParams from .brand_retrieve_response import BrandRetrieveResponse as BrandRetrieveResponse from .utility_prefetch_params import UtilityPrefetchParams as UtilityPrefetchParams -from .web_screenshot_response import WebScreenshotResponse as WebScreenshotResponse from .web_web_crawl_md_params import WebWebCrawlMdParams as WebWebCrawlMdParams from .web_extract_fonts_params import WebExtractFontsParams as WebExtractFontsParams from .web_web_scrape_md_params import WebWebScrapeMdParams as WebWebScrapeMdParams diff --git a/src/context/dev/types/web_screenshot_params.py b/src/context/dev/types/web_screenshot_params.py deleted file mode 100644 index d278e90..0000000 --- a/src/context/dev/types/web_screenshot_params.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["WebScreenshotParams"] - - -class WebScreenshotParams(TypedDict, total=False): - direct_url: Annotated[str, PropertyInfo(alias="directUrl")] - """ - A specific URL to screenshot directly, bypassing domain resolution (e.g., - 'https://example.com/pricing'). When provided, the screenshot is taken of this - exact URL. - """ - - domain: str - """Domain name to take screenshot of (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - full_screenshot: Annotated[Literal["true", "false"], PropertyInfo(alias="fullScreenshot")] - """Optional parameter to determine screenshot type. - - If 'true', takes a full page screenshot capturing all content. If 'false' or not - provided, takes a viewport screenshot (standard browser view). - """ - - page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] - """Optional parameter to specify which page type to screenshot. - - If provided, the system will scrape the domain's links and use heuristics to - find the most appropriate URL for the specified page type (30 supported - languages). If not provided, screenshots the main domain landing page. Only - applicable when using 'domain', not 'directUrl'. - """ - - prioritize: Literal["speed", "quality"] - """Optional parameter to prioritize screenshot capture. - - If 'speed', optimizes for faster capture with basic quality. If 'quality', - optimizes for higher quality with longer wait times. Defaults to 'quality' if - not provided. - """ diff --git a/src/context/dev/types/web_screenshot_response.py b/src/context/dev/types/web_screenshot_response.py deleted file mode 100644 index e81a5b2..0000000 --- a/src/context/dev/types/web_screenshot_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["WebScreenshotResponse"] - - -class WebScreenshotResponse(BaseModel): - code: Optional[int] = None - """HTTP status code""" - - domain: Optional[str] = None - """The normalized domain that was processed""" - - screenshot: Optional[str] = None - """Public URL of the uploaded screenshot image""" - - screenshot_type: Optional[Literal["viewport", "fullPage"]] = FieldInfo(alias="screenshotType", default=None) - """Type of screenshot that was captured""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py index f78e5b7..8ffd8dc 100644 --- a/tests/api_resources/test_web.py +++ b/tests/api_resources/test_web.py @@ -10,7 +10,6 @@ from context.dev import ContextDev, AsyncContextDev from tests.utils import assert_matches_type from context.dev.types import ( - WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeMdResponse, WebExtractFontsResponse, @@ -63,46 +62,6 @@ def test_streaming_response_extract_fonts(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_screenshot(self, client: ContextDev) -> None: - web = client.web.screenshot() - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_screenshot_with_all_params(self, client: ContextDev) -> None: - web = client.web.screenshot( - direct_url="https://example.com", - domain="domain", - full_screenshot="true", - page="login", - prioritize="speed", - ) - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_screenshot(self, client: ContextDev) -> None: - response = client.web.with_raw_response.screenshot() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - web = response.parse() - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_screenshot(self, client: ContextDev) -> None: - with client.web.with_streaming_response.screenshot() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - web = response.parse() - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_web_crawl_md(self, client: ContextDev) -> None: @@ -364,46 +323,6 @@ async def test_streaming_response_extract_fonts(self, async_client: AsyncContext assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_screenshot(self, async_client: AsyncContextDev) -> None: - web = await async_client.web.screenshot() - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_screenshot_with_all_params(self, async_client: AsyncContextDev) -> None: - web = await async_client.web.screenshot( - direct_url="https://example.com", - domain="domain", - full_screenshot="true", - page="login", - prioritize="speed", - ) - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_screenshot(self, async_client: AsyncContextDev) -> None: - response = await async_client.web.with_raw_response.screenshot() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - web = await response.parse() - assert_matches_type(WebScreenshotResponse, web, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_screenshot(self, async_client: AsyncContextDev) -> None: - async with async_client.web.with_streaming_response.screenshot() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - web = await response.parse() - assert_matches_type(WebScreenshotResponse, 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_web_crawl_md(self, async_client: AsyncContextDev) -> None: From 9ddc161215cd12384152295997b95d5ac44bec08 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:11:28 +0000 Subject: [PATCH 17/27] feat(api): api update --- .stats.yml | 6 +- api.md | 12 - src/context/dev/_client.py | 39 +- src/context/dev/resources/__init__.py | 14 - src/context/dev/resources/style.py | 213 --------- src/context/dev/resources/web.py | 12 +- src/context/dev/types/__init__.py | 2 - .../types/style_extract_styleguide_params.py | 30 -- .../style_extract_styleguide_response.py | 427 ------------------ .../dev/types/web_extract_fonts_params.py | 6 +- tests/api_resources/test_style.py | 100 ---- 11 files changed, 16 insertions(+), 845 deletions(-) delete mode 100644 src/context/dev/resources/style.py delete mode 100644 src/context/dev/types/style_extract_styleguide_params.py delete mode 100644 src/context/dev/types/style_extract_styleguide_response.py delete mode 100644 tests/api_resources/test_style.py diff --git a/.stats.yml b/.stats.yml index 2ed55ae..912df3b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-142c1d289d90b82edca3269964fc4fd6595f0e97c36b42a88b6f774fa5e65c10.yml -openapi_spec_hash: 1dc3e05add427f444e7714173c2fe8a9 +configured_endpoints: 19 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-3c753cf7c4d04fe467710a51b1a2f6f024b0b6a670704be7340b92e064bcf1a9.yml +openapi_spec_hash: f9b02e6756001e2be87d541d5cc609e5 config_hash: 859455bbffc8a009cd9b5eed9b3ba2d7 diff --git a/api.md b/api.md index 614b840..4638a0c 100644 --- a/api.md +++ b/api.md @@ -36,18 +36,6 @@ Methods: - client.ai.extract_product(\*\*params) -> AIExtractProductResponse - client.ai.extract_products(\*\*params) -> AIExtractProductsResponse -# Style - -Types: - -```python -from context.dev.types import StyleExtractStyleguideResponse -``` - -Methods: - -- client.style.extract_styleguide(\*\*params) -> StyleExtractStyleguideResponse - # Brand Types: diff --git a/src/context/dev/_client.py b/src/context/dev/_client.py index 06cee7f..ace042b 100644 --- a/src/context/dev/_client.py +++ b/src/context/dev/_client.py @@ -31,11 +31,10 @@ ) if TYPE_CHECKING: - from .resources import ai, web, brand, style, utility, industry + from .resources import ai, web, brand, utility, industry from .resources.ai import AIResource, AsyncAIResource from .resources.web import WebResource, AsyncWebResource from .resources.brand import BrandResource, AsyncBrandResource - from .resources.style import StyleResource, AsyncStyleResource from .resources.utility import UtilityResource, AsyncUtilityResource from .resources.industry import IndustryResource, AsyncIndustryResource @@ -118,12 +117,6 @@ def ai(self) -> AIResource: return AIResource(self) - @cached_property - def style(self) -> StyleResource: - from .resources.style import StyleResource - - return StyleResource(self) - @cached_property def brand(self) -> BrandResource: from .resources.brand import BrandResource @@ -322,12 +315,6 @@ def ai(self) -> AsyncAIResource: return AsyncAIResource(self) - @cached_property - def style(self) -> AsyncStyleResource: - from .resources.style import AsyncStyleResource - - return AsyncStyleResource(self) - @cached_property def brand(self) -> AsyncBrandResource: from .resources.brand import AsyncBrandResource @@ -477,12 +464,6 @@ def ai(self) -> ai.AIResourceWithRawResponse: return AIResourceWithRawResponse(self._client.ai) - @cached_property - def style(self) -> style.StyleResourceWithRawResponse: - from .resources.style import StyleResourceWithRawResponse - - return StyleResourceWithRawResponse(self._client.style) - @cached_property def brand(self) -> brand.BrandResourceWithRawResponse: from .resources.brand import BrandResourceWithRawResponse @@ -520,12 +501,6 @@ def ai(self) -> ai.AsyncAIResourceWithRawResponse: return AsyncAIResourceWithRawResponse(self._client.ai) - @cached_property - def style(self) -> style.AsyncStyleResourceWithRawResponse: - from .resources.style import AsyncStyleResourceWithRawResponse - - return AsyncStyleResourceWithRawResponse(self._client.style) - @cached_property def brand(self) -> brand.AsyncBrandResourceWithRawResponse: from .resources.brand import AsyncBrandResourceWithRawResponse @@ -563,12 +538,6 @@ def ai(self) -> ai.AIResourceWithStreamingResponse: return AIResourceWithStreamingResponse(self._client.ai) - @cached_property - def style(self) -> style.StyleResourceWithStreamingResponse: - from .resources.style import StyleResourceWithStreamingResponse - - return StyleResourceWithStreamingResponse(self._client.style) - @cached_property def brand(self) -> brand.BrandResourceWithStreamingResponse: from .resources.brand import BrandResourceWithStreamingResponse @@ -606,12 +575,6 @@ def ai(self) -> ai.AsyncAIResourceWithStreamingResponse: return AsyncAIResourceWithStreamingResponse(self._client.ai) - @cached_property - def style(self) -> style.AsyncStyleResourceWithStreamingResponse: - from .resources.style import AsyncStyleResourceWithStreamingResponse - - return AsyncStyleResourceWithStreamingResponse(self._client.style) - @cached_property def brand(self) -> brand.AsyncBrandResourceWithStreamingResponse: from .resources.brand import AsyncBrandResourceWithStreamingResponse diff --git a/src/context/dev/resources/__init__.py b/src/context/dev/resources/__init__.py index 2bcb6db..edc5356 100644 --- a/src/context/dev/resources/__init__.py +++ b/src/context/dev/resources/__init__.py @@ -24,14 +24,6 @@ BrandResourceWithStreamingResponse, AsyncBrandResourceWithStreamingResponse, ) -from .style import ( - StyleResource, - AsyncStyleResource, - StyleResourceWithRawResponse, - AsyncStyleResourceWithRawResponse, - StyleResourceWithStreamingResponse, - AsyncStyleResourceWithStreamingResponse, -) from .utility import ( UtilityResource, AsyncUtilityResource, @@ -62,12 +54,6 @@ "AsyncAIResourceWithRawResponse", "AIResourceWithStreamingResponse", "AsyncAIResourceWithStreamingResponse", - "StyleResource", - "AsyncStyleResource", - "StyleResourceWithRawResponse", - "AsyncStyleResourceWithRawResponse", - "StyleResourceWithStreamingResponse", - "AsyncStyleResourceWithStreamingResponse", "BrandResource", "AsyncBrandResource", "BrandResourceWithRawResponse", diff --git a/src/context/dev/resources/style.py b/src/context/dev/resources/style.py deleted file mode 100644 index 4bf4845..0000000 --- a/src/context/dev/resources/style.py +++ /dev/null @@ -1,213 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..types import style_extract_styleguide_params -from .._types import Body, Omit, Query, Headers, NotGiven, 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.style_extract_styleguide_response import StyleExtractStyleguideResponse - -__all__ = ["StyleResource", "AsyncStyleResource"] - - -class StyleResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> StyleResourceWithRawResponse: - """ - 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers - """ - return StyleResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> StyleResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response - """ - return StyleResourceWithStreamingResponse(self) - - def extract_styleguide( - self, - *, - direct_url: str | Omit = omit, - domain: str | 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, - ) -> StyleExtractStyleguideResponse: - """ - Automatically extract comprehensive design system information from a brand's - website including colors, typography, spacing, shadows, and UI components. - Either 'domain' or 'directUrl' must be provided as a query parameter, but not - both. - - Args: - direct_url: A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - - domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - 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( - "/brand/styleguide", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "timeout_ms": timeout_ms, - }, - style_extract_styleguide_params.StyleExtractStyleguideParams, - ), - ), - cast_to=StyleExtractStyleguideResponse, - ) - - -class AsyncStyleResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncStyleResourceWithRawResponse: - """ - 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncStyleResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncStyleResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response - """ - return AsyncStyleResourceWithStreamingResponse(self) - - async def extract_styleguide( - self, - *, - direct_url: str | Omit = omit, - domain: str | 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, - ) -> StyleExtractStyleguideResponse: - """ - Automatically extract comprehensive design system information from a brand's - website including colors, typography, spacing, shadows, and UI components. - Either 'domain' or 'directUrl' must be provided as a query parameter, but not - both. - - Args: - direct_url: A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - - domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. - - 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( - "/brand/styleguide", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "direct_url": direct_url, - "domain": domain, - "timeout_ms": timeout_ms, - }, - style_extract_styleguide_params.StyleExtractStyleguideParams, - ), - ), - cast_to=StyleExtractStyleguideResponse, - ) - - -class StyleResourceWithRawResponse: - def __init__(self, style: StyleResource) -> None: - self._style = style - - self.extract_styleguide = to_raw_response_wrapper( - style.extract_styleguide, - ) - - -class AsyncStyleResourceWithRawResponse: - def __init__(self, style: AsyncStyleResource) -> None: - self._style = style - - self.extract_styleguide = async_to_raw_response_wrapper( - style.extract_styleguide, - ) - - -class StyleResourceWithStreamingResponse: - def __init__(self, style: StyleResource) -> None: - self._style = style - - self.extract_styleguide = to_streamed_response_wrapper( - style.extract_styleguide, - ) - - -class AsyncStyleResourceWithStreamingResponse: - def __init__(self, style: AsyncStyleResource) -> None: - self._style = style - - self.extract_styleguide = async_to_streamed_response_wrapper( - style.extract_styleguide, - ) diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 6d1dcac..951c368 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -72,10 +72,12 @@ def extract_fonts( Args: direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., - 'https://example.com/design-system'). + 'https://example.com/design-system'). When provided, fonts are extracted from + this exact URL. You must provide either 'domain' or 'directUrl', but not both. domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. 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 @@ -424,10 +426,12 @@ async def extract_fonts( Args: direct_url: A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., - 'https://example.com/design-system'). + 'https://example.com/design-system'). When provided, fonts are extracted from + this exact URL. You must provide either 'domain' or 'directUrl', but not both. domain: Domain name to extract fonts from (e.g., 'example.com', 'google.com'). The - domain will be automatically normalized and validated. + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. 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 diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 3cc5ca9..94e5e1f 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -30,14 +30,12 @@ from .brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse from .brand_retrieve_by_name_response import BrandRetrieveByNameResponse as BrandRetrieveByNameResponse from .brand_retrieve_by_ticker_params import BrandRetrieveByTickerParams as BrandRetrieveByTickerParams -from .style_extract_styleguide_params import StyleExtractStyleguideParams as StyleExtractStyleguideParams from .web_web_scrape_sitemap_response import WebWebScrapeSitemapResponse as WebWebScrapeSitemapResponse from .brand_retrieve_by_email_response import BrandRetrieveByEmailResponse as BrandRetrieveByEmailResponse 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 .brand_retrieve_by_ticker_response import BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse -from .style_extract_styleguide_response import StyleExtractStyleguideResponse as StyleExtractStyleguideResponse from .brand_retrieve_simplified_response import BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse from .utility_prefetch_by_email_response import UtilityPrefetchByEmailResponse as UtilityPrefetchByEmailResponse from .brand_identify_from_transaction_params import ( diff --git a/src/context/dev/types/style_extract_styleguide_params.py b/src/context/dev/types/style_extract_styleguide_params.py deleted file mode 100644 index c07da4f..0000000 --- a/src/context/dev/types/style_extract_styleguide_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["StyleExtractStyleguideParams"] - - -class StyleExtractStyleguideParams(TypedDict, total=False): - direct_url: Annotated[str, PropertyInfo(alias="directUrl")] - """ - A specific URL to fetch the styleguide from directly, bypassing domain - resolution (e.g., 'https://example.com/design-system'). - """ - - domain: str - """Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). - - The domain will be automatically normalized and validated. - """ - - 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/style_extract_styleguide_response.py b/src/context/dev/types/style_extract_styleguide_response.py deleted file mode 100644 index 7c327d8..0000000 --- a/src/context/dev/types/style_extract_styleguide_response.py +++ /dev/null @@ -1,427 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = [ - "StyleExtractStyleguideResponse", - "Styleguide", - "StyleguideColors", - "StyleguideComponents", - "StyleguideComponentsButton", - "StyleguideComponentsButtonLink", - "StyleguideComponentsButtonPrimary", - "StyleguideComponentsButtonSecondary", - "StyleguideComponentsCard", - "StyleguideElementSpacing", - "StyleguideFontLinks", - "StyleguideShadows", - "StyleguideTypography", - "StyleguideTypographyHeadings", - "StyleguideTypographyHeadingsH1", - "StyleguideTypographyHeadingsH2", - "StyleguideTypographyHeadingsH3", - "StyleguideTypographyHeadingsH4", - "StyleguideTypographyP", -] - - -class StyleguideColors(BaseModel): - """Primary colors used on the website""" - - accent: str - """Accent color (hex format)""" - - background: str - """Background color (hex format)""" - - text: str - """Text color (hex format)""" - - -class StyleguideComponentsButtonLink(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButtonPrimary(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButtonSecondary(BaseModel): - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - """Computed box-shadow (comma-separated layers when present)""" - - color: str - - css: str - """Ready-to-use CSS declaration block for this component style""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - min_height: str = FieldInfo(alias="minHeight") - """Sampled minimum height of the button box (typically px)""" - - min_width: str = FieldInfo(alias="minWidth") - """Sampled minimum width of the button box (typically px)""" - - padding: str - - text_decoration: str = FieldInfo(alias="textDecoration") - - font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) - """Full ordered font list from computed font-family""" - - font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) - """Primary button typeface (first in fontFallbacks)""" - - text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) - """Hex color of the underline when it differs from the text color""" - - -class StyleguideComponentsButton(BaseModel): - """Button component styles""" - - link: Optional[StyleguideComponentsButtonLink] = None - - primary: Optional[StyleguideComponentsButtonPrimary] = None - - secondary: Optional[StyleguideComponentsButtonSecondary] = None - - -class StyleguideComponentsCard(BaseModel): - """Card component style""" - - background_color: str = FieldInfo(alias="backgroundColor") - - border_color: str = FieldInfo(alias="borderColor") - """ - Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has - alpha) - """ - - border_radius: str = FieldInfo(alias="borderRadius") - - border_style: str = FieldInfo(alias="borderStyle") - - border_width: str = FieldInfo(alias="borderWidth") - - box_shadow: str = FieldInfo(alias="boxShadow") - - css: str - """Ready-to-use CSS declaration block for this component style""" - - padding: str - - text_color: str = FieldInfo(alias="textColor") - - -class StyleguideComponents(BaseModel): - """UI component styles""" - - button: StyleguideComponentsButton - """Button component styles""" - - card: Optional[StyleguideComponentsCard] = None - """Card component style""" - - -class StyleguideElementSpacing(BaseModel): - """Spacing system used on the website""" - - lg: str - - md: str - - sm: str - - xl: str - - xs: str - - -class StyleguideFontLinks(BaseModel): - files: Dict[str, str] - """Upright font files keyed by weight string (e.g. - - "400" for regular, "500", "700"). Values are absolute URLs. - """ - - type: Literal["google", "custom"] - - category: Optional[str] = None - """Google Fonts category when type is google (e.g. - - sans-serif, serif, monospace, display, handwriting). Omitted for custom fonts - when unknown. - """ - - display_name: Optional[str] = FieldInfo(alias="displayName", default=None) - """ - Present when type is custom: human-readable name derived from the fontLinks key - (strip build/hash suffixes, split camelCase / PascalCase, normalize separators). - Google entries omit this. - """ - - -class StyleguideShadows(BaseModel): - """Shadow styles used on the website""" - - inner: str - - lg: str - - md: str - - sm: str - - xl: str - - -class StyleguideTypographyHeadingsH1(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH2(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH3(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadingsH4(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypographyHeadings(BaseModel): - """Heading styles""" - - h1: Optional[StyleguideTypographyHeadingsH1] = None - - h2: Optional[StyleguideTypographyHeadingsH2] = None - - h3: Optional[StyleguideTypographyHeadingsH3] = None - - h4: Optional[StyleguideTypographyHeadingsH4] = None - - -class StyleguideTypographyP(BaseModel): - font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") - """Full ordered font list from resolved computed font-family""" - - font_family: str = FieldInfo(alias="fontFamily") - """Primary face (first family in the computed stack)""" - - font_size: str = FieldInfo(alias="fontSize") - - font_weight: float = FieldInfo(alias="fontWeight") - - letter_spacing: str = FieldInfo(alias="letterSpacing") - - line_height: str = FieldInfo(alias="lineHeight") - - -class StyleguideTypography(BaseModel): - """Typography styles used on the website""" - - headings: StyleguideTypographyHeadings - """Heading styles""" - - p: Optional[StyleguideTypographyP] = None - - -class Styleguide(BaseModel): - """Comprehensive styleguide data extracted from the website""" - - colors: StyleguideColors - """Primary colors used on the website""" - - components: StyleguideComponents - """UI component styles""" - - element_spacing: StyleguideElementSpacing = FieldInfo(alias="elementSpacing") - """Spacing system used on the website""" - - font_links: Dict[str, StyleguideFontLinks] = FieldInfo(alias="fontLinks") - """ - Font assets keyed by family name as it appears in fontFamily/fontFallbacks - (non-generic names only). Clients match typography.fontFamily / fontWeight or - button styles to pick a file URL from files. - """ - - mode: Literal["light", "dark"] - """The primary color mode of the website design""" - - shadows: StyleguideShadows - """Shadow styles used on the website""" - - typography: StyleguideTypography - """Typography styles used on the website""" - - -class StyleExtractStyleguideResponse(BaseModel): - code: Optional[int] = None - """HTTP status code""" - - domain: Optional[str] = None - """The normalized domain that was processed""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" - - styleguide: Optional[Styleguide] = None - """Comprehensive styleguide data extracted from the website""" diff --git a/src/context/dev/types/web_extract_fonts_params.py b/src/context/dev/types/web_extract_fonts_params.py index db307de..9bdc46e 100644 --- a/src/context/dev/types/web_extract_fonts_params.py +++ b/src/context/dev/types/web_extract_fonts_params.py @@ -13,13 +13,15 @@ class WebExtractFontsParams(TypedDict, total=False): direct_url: Annotated[str, PropertyInfo(alias="directUrl")] """ A specific URL to fetch fonts from directly, bypassing domain resolution (e.g., - 'https://example.com/design-system'). + 'https://example.com/design-system'). When provided, fonts are extracted from + this exact URL. You must provide either 'domain' or 'directUrl', but not both. """ domain: str """Domain name to extract fonts from (e.g., 'example.com', 'google.com'). - The domain will be automatically normalized and validated. + The domain will be automatically normalized and validated. You must provide + either 'domain' or 'directUrl', but not both. """ timeout_ms: Annotated[int, PropertyInfo(alias="timeoutMS")] diff --git a/tests/api_resources/test_style.py b/tests/api_resources/test_style.py deleted file mode 100644 index 3206971..0000000 --- a/tests/api_resources/test_style.py +++ /dev/null @@ -1,100 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from context.dev import ContextDev, AsyncContextDev -from tests.utils import assert_matches_type -from context.dev.types import StyleExtractStyleguideResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestStyle: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_extract_styleguide(self, client: ContextDev) -> None: - style = client.style.extract_styleguide() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_extract_styleguide_with_all_params(self, client: ContextDev) -> None: - style = client.style.extract_styleguide( - direct_url="https://example.com", - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_extract_styleguide(self, client: ContextDev) -> None: - response = client.style.with_raw_response.extract_styleguide() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - style = response.parse() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_extract_styleguide(self, client: ContextDev) -> None: - with client.style.with_streaming_response.extract_styleguide() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - style = response.parse() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncStyle: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_extract_styleguide(self, async_client: AsyncContextDev) -> None: - style = await async_client.style.extract_styleguide() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_extract_styleguide_with_all_params(self, async_client: AsyncContextDev) -> None: - style = await async_client.style.extract_styleguide( - direct_url="https://example.com", - domain="domain", - timeout_ms=1000, - ) - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_extract_styleguide(self, async_client: AsyncContextDev) -> None: - response = await async_client.style.with_raw_response.extract_styleguide() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - style = await response.parse() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_extract_styleguide(self, async_client: AsyncContextDev) -> None: - async with async_client.style.with_streaming_response.extract_styleguide() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - style = await response.parse() - assert_matches_type(StyleExtractStyleguideResponse, style, path=["response"]) - - assert cast(Any, response.is_closed) is True From ccc073454bed05a34a06bb3c5e4208e863a6d688 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:13:04 +0000 Subject: [PATCH 18/27] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 912df3b..d8c4ba8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-3c753cf7c4d04fe467710a51b1a2f6f024b0b6a670704be7340b92e064bcf1a9.yml openapi_spec_hash: f9b02e6756001e2be87d541d5cc609e5 -config_hash: 859455bbffc8a009cd9b5eed9b3ba2d7 +config_hash: d039d212bed1d71e6d015ca71ea4d0f9 From 826959bf7019451a7d93d4bb3ec248f7bd7fc4b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:21:59 +0000 Subject: [PATCH 19/27] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index d8c4ba8..8b923f5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-3c753cf7c4d04fe467710a51b1a2f6f024b0b6a670704be7340b92e064bcf1a9.yml openapi_spec_hash: f9b02e6756001e2be87d541d5cc609e5 -config_hash: d039d212bed1d71e6d015ca71ea4d0f9 +config_hash: ca7ca57332b7ae61763f08dd7a022601 From d6acbc6552d84e58a6ff7a9e99952a2a7a624647 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:41:03 +0000 Subject: [PATCH 20/27] feat(api): manual updates --- .stats.yml | 4 +- api.md | 4 + src/context/dev/resources/web.py | 286 ++++++++++++ src/context/dev/types/__init__.py | 4 + .../types/web_extract_styleguide_params.py | 33 ++ .../types/web_extract_styleguide_response.py | 427 ++++++++++++++++++ .../dev/types/web_screenshot_params.py | 49 ++ .../dev/types/web_screenshot_response.py | 27 ++ tests/api_resources/test_web.py | 158 +++++++ 9 files changed, 990 insertions(+), 2 deletions(-) create mode 100644 src/context/dev/types/web_extract_styleguide_params.py create mode 100644 src/context/dev/types/web_extract_styleguide_response.py create mode 100644 src/context/dev/types/web_screenshot_params.py create mode 100644 src/context/dev/types/web_screenshot_response.py diff --git a/.stats.yml b/.stats.yml index 8b923f5..d51d6f7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 19 +configured_endpoints: 21 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-3c753cf7c4d04fe467710a51b1a2f6f024b0b6a670704be7340b92e064bcf1a9.yml openapi_spec_hash: f9b02e6756001e2be87d541d5cc609e5 -config_hash: ca7ca57332b7ae61763f08dd7a022601 +config_hash: 85e90b8fda2e1f326ca60f8ab3e53704 diff --git a/api.md b/api.md index 4638a0c..946ecb1 100644 --- a/api.md +++ b/api.md @@ -5,6 +5,8 @@ Types: ```python from context.dev.types import ( WebExtractFontsResponse, + WebExtractStyleguideResponse, + WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeHTMLResponse, WebWebScrapeImagesResponse, @@ -16,6 +18,8 @@ from context.dev.types import ( Methods: - client.web.extract_fonts(\*\*params) -> WebExtractFontsResponse +- client.web.extract_styleguide(\*\*params) -> WebExtractStyleguideResponse +- client.web.screenshot(\*\*params) -> WebScreenshotResponse - client.web.web_crawl_md(\*\*params) -> WebWebCrawlMdResponse - client.web.web_scrape_html(\*\*params) -> WebWebScrapeHTMLResponse - client.web.web_scrape_images(\*\*params) -> WebWebScrapeImagesResponse diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 951c368..49da2d4 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -2,14 +2,18 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..types import ( + web_screenshot_params, web_web_crawl_md_params, web_extract_fonts_params, web_web_scrape_md_params, web_web_scrape_html_params, web_web_scrape_images_params, + web_extract_styleguide_params, web_web_scrape_sitemap_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given @@ -23,11 +27,13 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options +from ..types.web_screenshot_response import WebScreenshotResponse from ..types.web_web_crawl_md_response import WebWebCrawlMdResponse from ..types.web_extract_fonts_response import WebExtractFontsResponse from ..types.web_web_scrape_md_response import WebWebScrapeMdResponse from ..types.web_web_scrape_html_response import WebWebScrapeHTMLResponse 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 __all__ = ["WebResource", "AsyncWebResource"] @@ -110,6 +116,134 @@ def extract_fonts( cast_to=WebExtractFontsResponse, ) + def extract_styleguide( + self, + *, + direct_url: str | Omit = omit, + domain: str | 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, + ) -> WebExtractStyleguideResponse: + """ + Extract a comprehensive design system from a website including colors, + typography, spacing, shadows, and UI components. + + Args: + direct_url: A specific URL to fetch the styleguide from directly, bypassing domain + resolution (e.g., 'https://example.com/design-system'). When provided, the + styleguide is extracted from this exact URL. You must provide either 'domain' or + 'directUrl', but not both. + + domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. + + 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/styleguide", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "timeout_ms": timeout_ms, + }, + web_extract_styleguide_params.WebExtractStyleguideParams, + ), + ), + cast_to=WebExtractStyleguideResponse, + ) + + def screenshot( + self, + *, + direct_url: str | Omit = omit, + domain: str | Omit = omit, + full_screenshot: Literal["true", "false"] | Omit = omit, + page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, + prioritize: Literal["speed", "quality"] | 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, + ) -> WebScreenshotResponse: + """ + Capture a screenshot of a website. + + Args: + direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. You must provide either 'domain' or 'directUrl', but not both. + + domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. + + full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page + screenshot capturing all content. If 'false' or not provided, takes a viewport + screenshot (standard browser view). + + page: Optional parameter to specify which page type to screenshot. If provided, the + system will scrape the domain's links and use heuristics to find the most + appropriate URL for the specified page type (30 supported languages). If not + provided, screenshots the main domain landing page. Only applicable when using + 'domain', not 'directUrl'. + + prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for + faster capture with basic quality. If 'quality', optimizes for higher quality + with longer wait times. Defaults to 'quality' if not provided. + + 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/screenshot", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "full_screenshot": full_screenshot, + "page": page, + "prioritize": prioritize, + }, + web_screenshot_params.WebScreenshotParams, + ), + ), + cast_to=WebScreenshotResponse, + ) + def web_crawl_md( self, *, @@ -464,6 +598,134 @@ async def extract_fonts( cast_to=WebExtractFontsResponse, ) + async def extract_styleguide( + self, + *, + direct_url: str | Omit = omit, + domain: str | 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, + ) -> WebExtractStyleguideResponse: + """ + Extract a comprehensive design system from a website including colors, + typography, spacing, shadows, and UI components. + + Args: + direct_url: A specific URL to fetch the styleguide from directly, bypassing domain + resolution (e.g., 'https://example.com/design-system'). When provided, the + styleguide is extracted from this exact URL. You must provide either 'domain' or + 'directUrl', but not both. + + domain: Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. + + 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/styleguide", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "timeout_ms": timeout_ms, + }, + web_extract_styleguide_params.WebExtractStyleguideParams, + ), + ), + cast_to=WebExtractStyleguideResponse, + ) + + async def screenshot( + self, + *, + direct_url: str | Omit = omit, + domain: str | Omit = omit, + full_screenshot: Literal["true", "false"] | Omit = omit, + page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit, + prioritize: Literal["speed", "quality"] | 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, + ) -> WebScreenshotResponse: + """ + Capture a screenshot of a website. + + Args: + direct_url: A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. You must provide either 'domain' or 'directUrl', but not both. + + domain: Domain name to take screenshot of (e.g., 'example.com', 'google.com'). The + domain will be automatically normalized and validated. You must provide either + 'domain' or 'directUrl', but not both. + + full_screenshot: Optional parameter to determine screenshot type. If 'true', takes a full page + screenshot capturing all content. If 'false' or not provided, takes a viewport + screenshot (standard browser view). + + page: Optional parameter to specify which page type to screenshot. If provided, the + system will scrape the domain's links and use heuristics to find the most + appropriate URL for the specified page type (30 supported languages). If not + provided, screenshots the main domain landing page. Only applicable when using + 'domain', not 'directUrl'. + + prioritize: Optional parameter to prioritize screenshot capture. If 'speed', optimizes for + faster capture with basic quality. If 'quality', optimizes for higher quality + with longer wait times. Defaults to 'quality' if not provided. + + 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/screenshot", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "direct_url": direct_url, + "domain": domain, + "full_screenshot": full_screenshot, + "page": page, + "prioritize": prioritize, + }, + web_screenshot_params.WebScreenshotParams, + ), + ), + cast_to=WebScreenshotResponse, + ) + async def web_crawl_md( self, *, @@ -748,6 +1010,12 @@ def __init__(self, web: WebResource) -> None: self.extract_fonts = to_raw_response_wrapper( web.extract_fonts, ) + self.extract_styleguide = to_raw_response_wrapper( + web.extract_styleguide, + ) + self.screenshot = to_raw_response_wrapper( + web.screenshot, + ) self.web_crawl_md = to_raw_response_wrapper( web.web_crawl_md, ) @@ -772,6 +1040,12 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract_fonts = async_to_raw_response_wrapper( web.extract_fonts, ) + self.extract_styleguide = async_to_raw_response_wrapper( + web.extract_styleguide, + ) + self.screenshot = async_to_raw_response_wrapper( + web.screenshot, + ) self.web_crawl_md = async_to_raw_response_wrapper( web.web_crawl_md, ) @@ -796,6 +1070,12 @@ def __init__(self, web: WebResource) -> None: self.extract_fonts = to_streamed_response_wrapper( web.extract_fonts, ) + self.extract_styleguide = to_streamed_response_wrapper( + web.extract_styleguide, + ) + self.screenshot = to_streamed_response_wrapper( + web.screenshot, + ) self.web_crawl_md = to_streamed_response_wrapper( web.web_crawl_md, ) @@ -820,6 +1100,12 @@ def __init__(self, web: AsyncWebResource) -> None: self.extract_fonts = async_to_streamed_response_wrapper( web.extract_fonts, ) + self.extract_styleguide = async_to_streamed_response_wrapper( + web.extract_styleguide, + ) + self.screenshot = async_to_streamed_response_wrapper( + web.screenshot, + ) self.web_crawl_md = async_to_streamed_response_wrapper( web.web_crawl_md, ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 94e5e1f..232daf5 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -5,8 +5,10 @@ from .ai_ai_query_params import AIAIQueryParams as AIAIQueryParams from .ai_ai_query_response import AIAIQueryResponse as AIAIQueryResponse from .brand_retrieve_params import BrandRetrieveParams as BrandRetrieveParams +from .web_screenshot_params import WebScreenshotParams as WebScreenshotParams from .brand_retrieve_response import BrandRetrieveResponse as BrandRetrieveResponse from .utility_prefetch_params import UtilityPrefetchParams as UtilityPrefetchParams +from .web_screenshot_response import WebScreenshotResponse as WebScreenshotResponse from .web_web_crawl_md_params import WebWebCrawlMdParams as WebWebCrawlMdParams from .web_extract_fonts_params import WebExtractFontsParams as WebExtractFontsParams from .web_web_scrape_md_params import WebWebScrapeMdParams as WebWebScrapeMdParams @@ -23,6 +25,7 @@ from .web_web_scrape_images_params import WebWebScrapeImagesParams as WebWebScrapeImagesParams from .brand_retrieve_by_isin_params import BrandRetrieveByIsinParams as BrandRetrieveByIsinParams from .brand_retrieve_by_name_params import BrandRetrieveByNameParams as BrandRetrieveByNameParams +from .web_extract_styleguide_params import WebExtractStyleguideParams as WebExtractStyleguideParams from .web_web_scrape_sitemap_params import WebWebScrapeSitemapParams as WebWebScrapeSitemapParams from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams from .industry_retrieve_naics_params import IndustryRetrieveNaicsParams as IndustryRetrieveNaicsParams @@ -30,6 +33,7 @@ from .brand_retrieve_by_isin_response import BrandRetrieveByIsinResponse as BrandRetrieveByIsinResponse from .brand_retrieve_by_name_response import BrandRetrieveByNameResponse as BrandRetrieveByNameResponse from .brand_retrieve_by_ticker_params import BrandRetrieveByTickerParams as BrandRetrieveByTickerParams +from .web_extract_styleguide_response import WebExtractStyleguideResponse as WebExtractStyleguideResponse from .web_web_scrape_sitemap_response import WebWebScrapeSitemapResponse as WebWebScrapeSitemapResponse from .brand_retrieve_by_email_response import BrandRetrieveByEmailResponse as BrandRetrieveByEmailResponse from .brand_retrieve_simplified_params import BrandRetrieveSimplifiedParams as BrandRetrieveSimplifiedParams diff --git a/src/context/dev/types/web_extract_styleguide_params.py b/src/context/dev/types/web_extract_styleguide_params.py new file mode 100644 index 0000000..42b6d88 --- /dev/null +++ b/src/context/dev/types/web_extract_styleguide_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebExtractStyleguideParams"] + + +class WebExtractStyleguideParams(TypedDict, total=False): + direct_url: Annotated[str, PropertyInfo(alias="directUrl")] + """ + A specific URL to fetch the styleguide from directly, bypassing domain + resolution (e.g., 'https://example.com/design-system'). When provided, the + styleguide is extracted from this exact URL. You must provide either 'domain' or + 'directUrl', but not both. + """ + + domain: str + """Domain name to extract styleguide from (e.g., 'example.com', 'google.com'). + + The domain will be automatically normalized and validated. You must provide + either 'domain' or 'directUrl', but not both. + """ + + 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_styleguide_response.py b/src/context/dev/types/web_extract_styleguide_response.py new file mode 100644 index 0000000..77e2b55 --- /dev/null +++ b/src/context/dev/types/web_extract_styleguide_response.py @@ -0,0 +1,427 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = [ + "WebExtractStyleguideResponse", + "Styleguide", + "StyleguideColors", + "StyleguideComponents", + "StyleguideComponentsButton", + "StyleguideComponentsButtonLink", + "StyleguideComponentsButtonPrimary", + "StyleguideComponentsButtonSecondary", + "StyleguideComponentsCard", + "StyleguideElementSpacing", + "StyleguideFontLinks", + "StyleguideShadows", + "StyleguideTypography", + "StyleguideTypographyHeadings", + "StyleguideTypographyHeadingsH1", + "StyleguideTypographyHeadingsH2", + "StyleguideTypographyHeadingsH3", + "StyleguideTypographyHeadingsH4", + "StyleguideTypographyP", +] + + +class StyleguideColors(BaseModel): + """Primary colors used on the website""" + + accent: str + """Accent color (hex format)""" + + background: str + """Background color (hex format)""" + + text: str + """Text color (hex format)""" + + +class StyleguideComponentsButtonLink(BaseModel): + background_color: str = FieldInfo(alias="backgroundColor") + + border_color: str = FieldInfo(alias="borderColor") + """ + Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has + alpha) + """ + + border_radius: str = FieldInfo(alias="borderRadius") + + border_style: str = FieldInfo(alias="borderStyle") + + border_width: str = FieldInfo(alias="borderWidth") + + box_shadow: str = FieldInfo(alias="boxShadow") + """Computed box-shadow (comma-separated layers when present)""" + + color: str + + css: str + """Ready-to-use CSS declaration block for this component style""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + min_height: str = FieldInfo(alias="minHeight") + """Sampled minimum height of the button box (typically px)""" + + min_width: str = FieldInfo(alias="minWidth") + """Sampled minimum width of the button box (typically px)""" + + padding: str + + text_decoration: str = FieldInfo(alias="textDecoration") + + font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) + """Full ordered font list from computed font-family""" + + font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) + """Primary button typeface (first in fontFallbacks)""" + + text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) + """Hex color of the underline when it differs from the text color""" + + +class StyleguideComponentsButtonPrimary(BaseModel): + background_color: str = FieldInfo(alias="backgroundColor") + + border_color: str = FieldInfo(alias="borderColor") + """ + Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has + alpha) + """ + + border_radius: str = FieldInfo(alias="borderRadius") + + border_style: str = FieldInfo(alias="borderStyle") + + border_width: str = FieldInfo(alias="borderWidth") + + box_shadow: str = FieldInfo(alias="boxShadow") + """Computed box-shadow (comma-separated layers when present)""" + + color: str + + css: str + """Ready-to-use CSS declaration block for this component style""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + min_height: str = FieldInfo(alias="minHeight") + """Sampled minimum height of the button box (typically px)""" + + min_width: str = FieldInfo(alias="minWidth") + """Sampled minimum width of the button box (typically px)""" + + padding: str + + text_decoration: str = FieldInfo(alias="textDecoration") + + font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) + """Full ordered font list from computed font-family""" + + font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) + """Primary button typeface (first in fontFallbacks)""" + + text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) + """Hex color of the underline when it differs from the text color""" + + +class StyleguideComponentsButtonSecondary(BaseModel): + background_color: str = FieldInfo(alias="backgroundColor") + + border_color: str = FieldInfo(alias="borderColor") + """ + Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has + alpha) + """ + + border_radius: str = FieldInfo(alias="borderRadius") + + border_style: str = FieldInfo(alias="borderStyle") + + border_width: str = FieldInfo(alias="borderWidth") + + box_shadow: str = FieldInfo(alias="boxShadow") + """Computed box-shadow (comma-separated layers when present)""" + + color: str + + css: str + """Ready-to-use CSS declaration block for this component style""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + min_height: str = FieldInfo(alias="minHeight") + """Sampled minimum height of the button box (typically px)""" + + min_width: str = FieldInfo(alias="minWidth") + """Sampled minimum width of the button box (typically px)""" + + padding: str + + text_decoration: str = FieldInfo(alias="textDecoration") + + font_fallbacks: Optional[List[str]] = FieldInfo(alias="fontFallbacks", default=None) + """Full ordered font list from computed font-family""" + + font_family: Optional[str] = FieldInfo(alias="fontFamily", default=None) + """Primary button typeface (first in fontFallbacks)""" + + text_decoration_color: Optional[str] = FieldInfo(alias="textDecorationColor", default=None) + """Hex color of the underline when it differs from the text color""" + + +class StyleguideComponentsButton(BaseModel): + """Button component styles""" + + link: Optional[StyleguideComponentsButtonLink] = None + + primary: Optional[StyleguideComponentsButtonPrimary] = None + + secondary: Optional[StyleguideComponentsButtonSecondary] = None + + +class StyleguideComponentsCard(BaseModel): + """Card component style""" + + background_color: str = FieldInfo(alias="backgroundColor") + + border_color: str = FieldInfo(alias="borderColor") + """ + Border color as CSS hex (#RRGGBB or #RRGGBBAA when computed border-color has + alpha) + """ + + border_radius: str = FieldInfo(alias="borderRadius") + + border_style: str = FieldInfo(alias="borderStyle") + + border_width: str = FieldInfo(alias="borderWidth") + + box_shadow: str = FieldInfo(alias="boxShadow") + + css: str + """Ready-to-use CSS declaration block for this component style""" + + padding: str + + text_color: str = FieldInfo(alias="textColor") + + +class StyleguideComponents(BaseModel): + """UI component styles""" + + button: StyleguideComponentsButton + """Button component styles""" + + card: Optional[StyleguideComponentsCard] = None + """Card component style""" + + +class StyleguideElementSpacing(BaseModel): + """Spacing system used on the website""" + + lg: str + + md: str + + sm: str + + xl: str + + xs: str + + +class StyleguideFontLinks(BaseModel): + files: Dict[str, str] + """Upright font files keyed by weight string (e.g. + + "400" for regular, "500", "700"). Values are absolute URLs. + """ + + type: Literal["google", "custom"] + + category: Optional[str] = None + """Google Fonts category when type is google (e.g. + + sans-serif, serif, monospace, display, handwriting). Omitted for custom fonts + when unknown. + """ + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + """ + Present when type is custom: human-readable name derived from the fontLinks key + (strip build/hash suffixes, split camelCase / PascalCase, normalize separators). + Google entries omit this. + """ + + +class StyleguideShadows(BaseModel): + """Shadow styles used on the website""" + + inner: str + + lg: str + + md: str + + sm: str + + xl: str + + +class StyleguideTypographyHeadingsH1(BaseModel): + font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") + """Full ordered font list from resolved computed font-family""" + + font_family: str = FieldInfo(alias="fontFamily") + """Primary face (first family in the computed stack)""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + letter_spacing: str = FieldInfo(alias="letterSpacing") + + line_height: str = FieldInfo(alias="lineHeight") + + +class StyleguideTypographyHeadingsH2(BaseModel): + font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") + """Full ordered font list from resolved computed font-family""" + + font_family: str = FieldInfo(alias="fontFamily") + """Primary face (first family in the computed stack)""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + letter_spacing: str = FieldInfo(alias="letterSpacing") + + line_height: str = FieldInfo(alias="lineHeight") + + +class StyleguideTypographyHeadingsH3(BaseModel): + font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") + """Full ordered font list from resolved computed font-family""" + + font_family: str = FieldInfo(alias="fontFamily") + """Primary face (first family in the computed stack)""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + letter_spacing: str = FieldInfo(alias="letterSpacing") + + line_height: str = FieldInfo(alias="lineHeight") + + +class StyleguideTypographyHeadingsH4(BaseModel): + font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") + """Full ordered font list from resolved computed font-family""" + + font_family: str = FieldInfo(alias="fontFamily") + """Primary face (first family in the computed stack)""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + letter_spacing: str = FieldInfo(alias="letterSpacing") + + line_height: str = FieldInfo(alias="lineHeight") + + +class StyleguideTypographyHeadings(BaseModel): + """Heading styles""" + + h1: Optional[StyleguideTypographyHeadingsH1] = None + + h2: Optional[StyleguideTypographyHeadingsH2] = None + + h3: Optional[StyleguideTypographyHeadingsH3] = None + + h4: Optional[StyleguideTypographyHeadingsH4] = None + + +class StyleguideTypographyP(BaseModel): + font_fallbacks: List[str] = FieldInfo(alias="fontFallbacks") + """Full ordered font list from resolved computed font-family""" + + font_family: str = FieldInfo(alias="fontFamily") + """Primary face (first family in the computed stack)""" + + font_size: str = FieldInfo(alias="fontSize") + + font_weight: float = FieldInfo(alias="fontWeight") + + letter_spacing: str = FieldInfo(alias="letterSpacing") + + line_height: str = FieldInfo(alias="lineHeight") + + +class StyleguideTypography(BaseModel): + """Typography styles used on the website""" + + headings: StyleguideTypographyHeadings + """Heading styles""" + + p: Optional[StyleguideTypographyP] = None + + +class Styleguide(BaseModel): + """Comprehensive styleguide data extracted from the website""" + + colors: StyleguideColors + """Primary colors used on the website""" + + components: StyleguideComponents + """UI component styles""" + + element_spacing: StyleguideElementSpacing = FieldInfo(alias="elementSpacing") + """Spacing system used on the website""" + + font_links: Dict[str, StyleguideFontLinks] = FieldInfo(alias="fontLinks") + """ + Font assets keyed by family name as it appears in fontFamily/fontFallbacks + (non-generic names only). Clients match typography.fontFamily / fontWeight or + button styles to pick a file URL from files. + """ + + mode: Literal["light", "dark"] + """The primary color mode of the website design""" + + shadows: StyleguideShadows + """Shadow styles used on the website""" + + typography: StyleguideTypography + """Typography styles used on the website""" + + +class WebExtractStyleguideResponse(BaseModel): + code: Optional[int] = None + """HTTP status code""" + + domain: Optional[str] = None + """The normalized domain that was processed""" + + status: Optional[str] = None + """Status of the response, e.g., 'ok'""" + + styleguide: Optional[Styleguide] = None + """Comprehensive styleguide data extracted from the website""" diff --git a/src/context/dev/types/web_screenshot_params.py b/src/context/dev/types/web_screenshot_params.py new file mode 100644 index 0000000..c6de68a --- /dev/null +++ b/src/context/dev/types/web_screenshot_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_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebScreenshotParams"] + + +class WebScreenshotParams(TypedDict, total=False): + direct_url: Annotated[str, PropertyInfo(alias="directUrl")] + """ + A specific URL to screenshot directly, bypassing domain resolution (e.g., + 'https://example.com/pricing'). When provided, the screenshot is taken of this + exact URL. You must provide either 'domain' or 'directUrl', but not both. + """ + + domain: str + """Domain name to take screenshot of (e.g., 'example.com', 'google.com'). + + The domain will be automatically normalized and validated. You must provide + either 'domain' or 'directUrl', but not both. + """ + + full_screenshot: Annotated[Literal["true", "false"], PropertyInfo(alias="fullScreenshot")] + """Optional parameter to determine screenshot type. + + If 'true', takes a full page screenshot capturing all content. If 'false' or not + provided, takes a viewport screenshot (standard browser view). + """ + + page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] + """Optional parameter to specify which page type to screenshot. + + If provided, the system will scrape the domain's links and use heuristics to + find the most appropriate URL for the specified page type (30 supported + languages). If not provided, screenshots the main domain landing page. Only + applicable when using 'domain', not 'directUrl'. + """ + + prioritize: Literal["speed", "quality"] + """Optional parameter to prioritize screenshot capture. + + If 'speed', optimizes for faster capture with basic quality. If 'quality', + optimizes for higher quality with longer wait times. Defaults to 'quality' if + not provided. + """ diff --git a/src/context/dev/types/web_screenshot_response.py b/src/context/dev/types/web_screenshot_response.py new file mode 100644 index 0000000..e81a5b2 --- /dev/null +++ b/src/context/dev/types/web_screenshot_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebScreenshotResponse"] + + +class WebScreenshotResponse(BaseModel): + code: Optional[int] = None + """HTTP status code""" + + domain: Optional[str] = None + """The normalized domain that was processed""" + + screenshot: Optional[str] = None + """Public URL of the uploaded screenshot image""" + + screenshot_type: Optional[Literal["viewport", "fullPage"]] = FieldInfo(alias="screenshotType", default=None) + """Type of screenshot that was captured""" + + status: Optional[str] = None + """Status of the response, e.g., 'ok'""" diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py index 8ffd8dc..6917533 100644 --- a/tests/api_resources/test_web.py +++ b/tests/api_resources/test_web.py @@ -10,12 +10,14 @@ from context.dev import ContextDev, AsyncContextDev from tests.utils import assert_matches_type from context.dev.types import ( + WebScreenshotResponse, WebWebCrawlMdResponse, WebWebScrapeMdResponse, WebExtractFontsResponse, WebWebScrapeHTMLResponse, WebWebScrapeImagesResponse, WebWebScrapeSitemapResponse, + WebExtractStyleguideResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -62,6 +64,84 @@ def test_streaming_response_extract_fonts(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_styleguide(self, client: ContextDev) -> None: + web = client.web.extract_styleguide() + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_extract_styleguide_with_all_params(self, client: ContextDev) -> None: + web = client.web.extract_styleguide( + direct_url="https://example.com", + domain="domain", + timeout_ms=1000, + ) + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_extract_styleguide(self, client: ContextDev) -> None: + response = client.web.with_raw_response.extract_styleguide() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = response.parse() + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_extract_styleguide(self, client: ContextDev) -> None: + with client.web.with_streaming_response.extract_styleguide() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = response.parse() + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_screenshot(self, client: ContextDev) -> None: + web = client.web.screenshot() + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_screenshot_with_all_params(self, client: ContextDev) -> None: + web = client.web.screenshot( + direct_url="https://example.com", + domain="domain", + full_screenshot="true", + page="login", + prioritize="speed", + ) + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_screenshot(self, client: ContextDev) -> None: + response = client.web.with_raw_response.screenshot() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = response.parse() + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_screenshot(self, client: ContextDev) -> None: + with client.web.with_streaming_response.screenshot() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = response.parse() + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_web_crawl_md(self, client: ContextDev) -> None: @@ -323,6 +403,84 @@ async def test_streaming_response_extract_fonts(self, async_client: AsyncContext assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_styleguide(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_styleguide() + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_extract_styleguide_with_all_params(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.extract_styleguide( + direct_url="https://example.com", + domain="domain", + timeout_ms=1000, + ) + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_extract_styleguide(self, async_client: AsyncContextDev) -> None: + response = await async_client.web.with_raw_response.extract_styleguide() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = await response.parse() + assert_matches_type(WebExtractStyleguideResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_extract_styleguide(self, async_client: AsyncContextDev) -> None: + async with async_client.web.with_streaming_response.extract_styleguide() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = await response.parse() + assert_matches_type(WebExtractStyleguideResponse, 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_screenshot(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.screenshot() + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_screenshot_with_all_params(self, async_client: AsyncContextDev) -> None: + web = await async_client.web.screenshot( + direct_url="https://example.com", + domain="domain", + full_screenshot="true", + page="login", + prioritize="speed", + ) + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_screenshot(self, async_client: AsyncContextDev) -> None: + response = await async_client.web.with_raw_response.screenshot() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + web = await response.parse() + assert_matches_type(WebScreenshotResponse, web, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_screenshot(self, async_client: AsyncContextDev) -> None: + async with async_client.web.with_streaming_response.screenshot() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + web = await response.parse() + assert_matches_type(WebScreenshotResponse, 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_web_crawl_md(self, async_client: AsyncContextDev) -> None: From f98376cf22a331138f716f8abeb4458a8e1e38c2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:46:25 +0000 Subject: [PATCH 21/27] feat(api): api update --- .stats.yml | 4 +- src/context/dev/resources/brand.py | 58 +++++++------------ src/context/dev/resources/web.py | 4 +- .../brand_identify_from_transaction_params.py | 1 - .../types/brand_retrieve_by_name_params.py | 6 +- 5 files changed, 29 insertions(+), 44 deletions(-) diff --git a/.stats.yml b/.stats.yml index d51d6f7..7833f48 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/context-dev%2Fcontext.dev-3c753cf7c4d04fe467710a51b1a2f6f024b0b6a670704be7340b92e064bcf1a9.yml -openapi_spec_hash: f9b02e6756001e2be87d541d5cc609e5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-3a604b0c9775298072e70890d369d21d38422553a8fe23d9d081b0dc06035ee2.yml +openapi_spec_hash: f94db2839851fb192f013b3ca258c39f config_hash: 85e90b8fda2e1f326ca60f8ab3e53704 diff --git a/src/context/dev/resources/brand.py b/src/context/dev/resources/brand.py index 24c4252..3373a27 100644 --- a/src/context/dev/resources/brand.py +++ b/src/context/dev/resources/brand.py @@ -637,7 +637,6 @@ def identify_from_transaction( high_confidence_only: When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -823,9 +822,8 @@ def retrieve_by_email( ) -> BrandRetrieveByEmailResponse: """ Retrieve brand information using an email address while detecting disposable and - free email addresses. This endpoint extracts the domain from the email address - and returns brand data for that domain. Disposable and free email addresses - (like gmail.com, yahoo.com) will throw a 422 error. + free email addresses. Disposable and free email addresses (like gmail.com, + yahoo.com) will throw a 422 error. Args: email: Email address to retrieve brand data for (e.g., 'contact@example.com'). The @@ -1008,8 +1006,7 @@ def retrieve_by_isin( ) -> BrandRetrieveByIsinResponse: """ Retrieve brand information using an ISIN (International Securities - Identification Number). This endpoint looks up the company associated with the - ISIN and returns its brand data. + Identification Number). Args: isin: ISIN (International Securities Identification Number) to retrieve brand data for @@ -1432,17 +1429,15 @@ def retrieve_by_name( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByNameResponse: - """Retrieve brand information using a company name. - - This endpoint searches for the - company by name and returns its brand data. + """ + Retrieve brand information using a company name. Args: name: Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters. - country_gl: Optional country code (GL parameter) to specify the country. This affects the - geographic location used for search queries. + country_gl: Optional country code hint (GL parameter) to specify the country for the company + name. force_language: Optional parameter to force the language of the retrieved brand data. @@ -1694,10 +1689,8 @@ def retrieve_by_ticker( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByTickerResponse: - """Retrieve brand information using a stock ticker symbol. - - This endpoint looks up - the company associated with the ticker and returns its brand data. + """ + Retrieve brand information using a stock ticker symbol. Args: ticker: Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). @@ -1758,8 +1751,8 @@ def retrieve_simplified( ) -> BrandRetrieveSimplifiedResponse: """ Returns a simplified version of brand data containing only essential - information: domain, title, colors, logos, and backdrops. This endpoint is - optimized for faster responses and reduced data transfer. + information: domain, title, colors, logos, and backdrops. Optimized for faster + responses and reduced data transfer. Args: domain: Domain name to retrieve simplified brand data for @@ -2395,7 +2388,6 @@ async def identify_from_transaction( high_confidence_only: When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. max_speed: Optional parameter to optimize the API call for maximum speed. When set to true, the API will skip time-consuming operations for faster response at the cost of @@ -2581,9 +2573,8 @@ async def retrieve_by_email( ) -> BrandRetrieveByEmailResponse: """ Retrieve brand information using an email address while detecting disposable and - free email addresses. This endpoint extracts the domain from the email address - and returns brand data for that domain. Disposable and free email addresses - (like gmail.com, yahoo.com) will throw a 422 error. + free email addresses. Disposable and free email addresses (like gmail.com, + yahoo.com) will throw a 422 error. Args: email: Email address to retrieve brand data for (e.g., 'contact@example.com'). The @@ -2766,8 +2757,7 @@ async def retrieve_by_isin( ) -> BrandRetrieveByIsinResponse: """ Retrieve brand information using an ISIN (International Securities - Identification Number). This endpoint looks up the company associated with the - ISIN and returns its brand data. + Identification Number). Args: isin: ISIN (International Securities Identification Number) to retrieve brand data for @@ -3190,17 +3180,15 @@ async def retrieve_by_name( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByNameResponse: - """Retrieve brand information using a company name. - - This endpoint searches for the - company by name and returns its brand data. + """ + Retrieve brand information using a company name. Args: name: Company name to retrieve brand data for (e.g., 'Apple Inc', 'Microsoft Corporation'). Must be 3-30 characters. - country_gl: Optional country code (GL parameter) to specify the country. This affects the - geographic location used for search queries. + country_gl: Optional country code hint (GL parameter) to specify the country for the company + name. force_language: Optional parameter to force the language of the retrieved brand data. @@ -3452,10 +3440,8 @@ async def retrieve_by_ticker( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BrandRetrieveByTickerResponse: - """Retrieve brand information using a stock ticker symbol. - - This endpoint looks up - the company associated with the ticker and returns its brand data. + """ + Retrieve brand information using a stock ticker symbol. Args: ticker: Stock ticker symbol to retrieve brand data for (e.g., 'AAPL', 'GOOGL', 'BRK.A'). @@ -3516,8 +3502,8 @@ async def retrieve_simplified( ) -> BrandRetrieveSimplifiedResponse: """ Returns a simplified version of brand data containing only essential - information: domain, title, colors, logos, and backdrops. This endpoint is - optimized for faster responses and reduced data transfer. + information: domain, title, colors, logos, and backdrops. Optimized for faster + responses and reduced data transfer. Args: domain: Domain name to retrieve simplified brand data for diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py index 49da2d4..2b68c4f 100644 --- a/src/context/dev/resources/web.py +++ b/src/context/dev/resources/web.py @@ -486,7 +486,7 @@ def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeSitemapResponse: """ - Crawl an entire website's sitemap and return all discovered page URLs + Crawl an entire website's sitemap and return all discovered page URLs. Args: domain: Domain to build a sitemap for @@ -968,7 +968,7 @@ async def web_scrape_sitemap( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WebWebScrapeSitemapResponse: """ - Crawl an entire website's sitemap and return all discovered page URLs + Crawl an entire website's sitemap and return all discovered page URLs. Args: domain: Domain to build a sitemap for diff --git a/src/context/dev/types/brand_identify_from_transaction_params.py b/src/context/dev/types/brand_identify_from_transaction_params.py index bd3f5a8..e6ecadd 100644 --- a/src/context/dev/types/brand_identify_from_transaction_params.py +++ b/src/context/dev/types/brand_identify_from_transaction_params.py @@ -390,7 +390,6 @@ class BrandIdentifyFromTransactionParams(TypedDict, total=False): """ When set to true, the API will perform an additional verification steps to ensure the identified brand matches the transaction with high confidence. - Defaults to false. """ max_speed: Annotated[bool, PropertyInfo(alias="maxSpeed")] diff --git a/src/context/dev/types/brand_retrieve_by_name_params.py b/src/context/dev/types/brand_retrieve_by_name_params.py index 31a9eef..d774223 100644 --- a/src/context/dev/types/brand_retrieve_by_name_params.py +++ b/src/context/dev/types/brand_retrieve_by_name_params.py @@ -257,9 +257,9 @@ class BrandRetrieveByNameParams(TypedDict, total=False): "zm", "zw", ] - """Optional country code (GL parameter) to specify the country. - - This affects the geographic location used for search queries. + """ + Optional country code hint (GL parameter) to specify the country for the company + name. """ force_language: Literal[ From 512bf2610e59733e0e950531ca9e6018a5446672 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:08:16 +0000 Subject: [PATCH 22/27] feat(api): api update --- .stats.yml | 6 +- api.md | 12 - src/context/dev/_client.py | 39 +--- src/context/dev/resources/__init__.py | 14 -- src/context/dev/resources/industry.py | 217 ------------------ src/context/dev/types/__init__.py | 2 - .../types/industry_retrieve_naics_params.py | 34 --- .../types/industry_retrieve_naics_response.py | 33 --- tests/api_resources/test_industry.py | 114 --------- 9 files changed, 4 insertions(+), 467 deletions(-) delete mode 100644 src/context/dev/resources/industry.py delete mode 100644 src/context/dev/types/industry_retrieve_naics_params.py delete mode 100644 src/context/dev/types/industry_retrieve_naics_response.py delete mode 100644 tests/api_resources/test_industry.py diff --git a/.stats.yml b/.stats.yml index 7833f48..a30858e 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/context-dev%2Fcontext.dev-3a604b0c9775298072e70890d369d21d38422553a8fe23d9d081b0dc06035ee2.yml -openapi_spec_hash: f94db2839851fb192f013b3ca258c39f +configured_endpoints: 20 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-7e43493a842618882d9d7d4f33c51d4b6a1e99c960f8be0996bd544864a5e79f.yml +openapi_spec_hash: 44bc5177ca909b32a1617ce637f5d704 config_hash: 85e90b8fda2e1f326ca60f8ab3e53704 diff --git a/api.md b/api.md index 946ecb1..d136a84 100644 --- a/api.md +++ b/api.md @@ -66,18 +66,6 @@ Methods: - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse - client.brand.retrieve_simplified(\*\*params) -> BrandRetrieveSimplifiedResponse -# Industry - -Types: - -```python -from context.dev.types import IndustryRetrieveNaicsResponse -``` - -Methods: - -- client.industry.retrieve_naics(\*\*params) -> IndustryRetrieveNaicsResponse - # Utility Types: diff --git a/src/context/dev/_client.py b/src/context/dev/_client.py index ace042b..5cf4972 100644 --- a/src/context/dev/_client.py +++ b/src/context/dev/_client.py @@ -31,12 +31,11 @@ ) if TYPE_CHECKING: - from .resources import ai, web, brand, utility, industry + from .resources import ai, web, brand, utility from .resources.ai import AIResource, AsyncAIResource from .resources.web import WebResource, AsyncWebResource from .resources.brand import BrandResource, AsyncBrandResource from .resources.utility import UtilityResource, AsyncUtilityResource - from .resources.industry import IndustryResource, AsyncIndustryResource __all__ = [ "Timeout", @@ -123,12 +122,6 @@ def brand(self) -> BrandResource: return BrandResource(self) - @cached_property - def industry(self) -> IndustryResource: - from .resources.industry import IndustryResource - - return IndustryResource(self) - @cached_property def utility(self) -> UtilityResource: from .resources.utility import UtilityResource @@ -321,12 +314,6 @@ def brand(self) -> AsyncBrandResource: return AsyncBrandResource(self) - @cached_property - def industry(self) -> AsyncIndustryResource: - from .resources.industry import AsyncIndustryResource - - return AsyncIndustryResource(self) - @cached_property def utility(self) -> AsyncUtilityResource: from .resources.utility import AsyncUtilityResource @@ -470,12 +457,6 @@ def brand(self) -> brand.BrandResourceWithRawResponse: return BrandResourceWithRawResponse(self._client.brand) - @cached_property - def industry(self) -> industry.IndustryResourceWithRawResponse: - from .resources.industry import IndustryResourceWithRawResponse - - return IndustryResourceWithRawResponse(self._client.industry) - @cached_property def utility(self) -> utility.UtilityResourceWithRawResponse: from .resources.utility import UtilityResourceWithRawResponse @@ -507,12 +488,6 @@ def brand(self) -> brand.AsyncBrandResourceWithRawResponse: return AsyncBrandResourceWithRawResponse(self._client.brand) - @cached_property - def industry(self) -> industry.AsyncIndustryResourceWithRawResponse: - from .resources.industry import AsyncIndustryResourceWithRawResponse - - return AsyncIndustryResourceWithRawResponse(self._client.industry) - @cached_property def utility(self) -> utility.AsyncUtilityResourceWithRawResponse: from .resources.utility import AsyncUtilityResourceWithRawResponse @@ -544,12 +519,6 @@ def brand(self) -> brand.BrandResourceWithStreamingResponse: return BrandResourceWithStreamingResponse(self._client.brand) - @cached_property - def industry(self) -> industry.IndustryResourceWithStreamingResponse: - from .resources.industry import IndustryResourceWithStreamingResponse - - return IndustryResourceWithStreamingResponse(self._client.industry) - @cached_property def utility(self) -> utility.UtilityResourceWithStreamingResponse: from .resources.utility import UtilityResourceWithStreamingResponse @@ -581,12 +550,6 @@ def brand(self) -> brand.AsyncBrandResourceWithStreamingResponse: return AsyncBrandResourceWithStreamingResponse(self._client.brand) - @cached_property - def industry(self) -> industry.AsyncIndustryResourceWithStreamingResponse: - from .resources.industry import AsyncIndustryResourceWithStreamingResponse - - return AsyncIndustryResourceWithStreamingResponse(self._client.industry) - @cached_property def utility(self) -> utility.AsyncUtilityResourceWithStreamingResponse: from .resources.utility import AsyncUtilityResourceWithStreamingResponse diff --git a/src/context/dev/resources/__init__.py b/src/context/dev/resources/__init__.py index edc5356..0980c63 100644 --- a/src/context/dev/resources/__init__.py +++ b/src/context/dev/resources/__init__.py @@ -32,14 +32,6 @@ UtilityResourceWithStreamingResponse, AsyncUtilityResourceWithStreamingResponse, ) -from .industry import ( - IndustryResource, - AsyncIndustryResource, - IndustryResourceWithRawResponse, - AsyncIndustryResourceWithRawResponse, - IndustryResourceWithStreamingResponse, - AsyncIndustryResourceWithStreamingResponse, -) __all__ = [ "WebResource", @@ -60,12 +52,6 @@ "AsyncBrandResourceWithRawResponse", "BrandResourceWithStreamingResponse", "AsyncBrandResourceWithStreamingResponse", - "IndustryResource", - "AsyncIndustryResource", - "IndustryResourceWithRawResponse", - "AsyncIndustryResourceWithRawResponse", - "IndustryResourceWithStreamingResponse", - "AsyncIndustryResourceWithStreamingResponse", "UtilityResource", "AsyncUtilityResource", "UtilityResourceWithRawResponse", diff --git a/src/context/dev/resources/industry.py b/src/context/dev/resources/industry.py deleted file mode 100644 index ae71953..0000000 --- a/src/context/dev/resources/industry.py +++ /dev/null @@ -1,217 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..types import industry_retrieve_naics_params -from .._types import Body, Omit, Query, Headers, NotGiven, 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.industry_retrieve_naics_response import IndustryRetrieveNaicsResponse - -__all__ = ["IndustryResource", "AsyncIndustryResource"] - - -class IndustryResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> IndustryResourceWithRawResponse: - """ - 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers - """ - return IndustryResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> IndustryResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response - """ - return IndustryResourceWithStreamingResponse(self) - - def retrieve_naics( - self, - *, - input: str, - max_results: int | Omit = omit, - min_results: 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, - ) -> IndustryRetrieveNaicsResponse: - """ - Endpoint to classify any brand into a 2022 NAICS code. - - Args: - input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided - in `input`, it will be used for classification, otherwise, we will search for - the brand using the provided title. - - max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults - to 5. - - min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. - - 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( - "/brand/naics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "input": input, - "max_results": max_results, - "min_results": min_results, - "timeout_ms": timeout_ms, - }, - industry_retrieve_naics_params.IndustryRetrieveNaicsParams, - ), - ), - cast_to=IndustryRetrieveNaicsResponse, - ) - - -class AsyncIndustryResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncIndustryResourceWithRawResponse: - """ - 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncIndustryResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncIndustryResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response - """ - return AsyncIndustryResourceWithStreamingResponse(self) - - async def retrieve_naics( - self, - *, - input: str, - max_results: int | Omit = omit, - min_results: 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, - ) -> IndustryRetrieveNaicsResponse: - """ - Endpoint to classify any brand into a 2022 NAICS code. - - Args: - input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided - in `input`, it will be used for classification, otherwise, we will search for - the brand using the provided title. - - max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults - to 5. - - min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. - - 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( - "/brand/naics", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "input": input, - "max_results": max_results, - "min_results": min_results, - "timeout_ms": timeout_ms, - }, - industry_retrieve_naics_params.IndustryRetrieveNaicsParams, - ), - ), - cast_to=IndustryRetrieveNaicsResponse, - ) - - -class IndustryResourceWithRawResponse: - def __init__(self, industry: IndustryResource) -> None: - self._industry = industry - - self.retrieve_naics = to_raw_response_wrapper( - industry.retrieve_naics, - ) - - -class AsyncIndustryResourceWithRawResponse: - def __init__(self, industry: AsyncIndustryResource) -> None: - self._industry = industry - - self.retrieve_naics = async_to_raw_response_wrapper( - industry.retrieve_naics, - ) - - -class IndustryResourceWithStreamingResponse: - def __init__(self, industry: IndustryResource) -> None: - self._industry = industry - - self.retrieve_naics = to_streamed_response_wrapper( - industry.retrieve_naics, - ) - - -class AsyncIndustryResourceWithStreamingResponse: - def __init__(self, industry: AsyncIndustryResource) -> None: - self._industry = industry - - self.retrieve_naics = async_to_streamed_response_wrapper( - industry.retrieve_naics, - ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 232daf5..3d70d6b 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -28,7 +28,6 @@ from .web_extract_styleguide_params import WebExtractStyleguideParams as WebExtractStyleguideParams from .web_web_scrape_sitemap_params import WebWebScrapeSitemapParams as WebWebScrapeSitemapParams from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams -from .industry_retrieve_naics_params import IndustryRetrieveNaicsParams as IndustryRetrieveNaicsParams 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 @@ -37,7 +36,6 @@ from .web_web_scrape_sitemap_response import WebWebScrapeSitemapResponse as WebWebScrapeSitemapResponse from .brand_retrieve_by_email_response import BrandRetrieveByEmailResponse as BrandRetrieveByEmailResponse 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 .brand_retrieve_by_ticker_response import BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse from .brand_retrieve_simplified_response import BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse diff --git a/src/context/dev/types/industry_retrieve_naics_params.py b/src/context/dev/types/industry_retrieve_naics_params.py deleted file mode 100644 index cbaed87..0000000 --- a/src/context/dev/types/industry_retrieve_naics_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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__ = ["IndustryRetrieveNaicsParams"] - - -class IndustryRetrieveNaicsParams(TypedDict, total=False): - input: Required[str] - """Brand domain or title to retrieve NAICS code for. - - If a valid domain is provided in `input`, it will be used for classification, - otherwise, we will search for the brand using the provided title. - """ - - max_results: Annotated[int, PropertyInfo(alias="maxResults")] - """Maximum number of NAICS codes to return. - - Must be between 1 and 10. Defaults to 5. - """ - - min_results: Annotated[int, PropertyInfo(alias="minResults")] - """Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1.""" - - 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/industry_retrieve_naics_response.py b/src/context/dev/types/industry_retrieve_naics_response.py deleted file mode 100644 index bcfc2ad..0000000 --- a/src/context/dev/types/industry_retrieve_naics_response.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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__ = ["IndustryRetrieveNaicsResponse", "Code"] - - -class Code(BaseModel): - code: str - """NAICS code""" - - confidence: Literal["high", "medium", "low"] - """Confidence level for how well this NAICS code matches the company description""" - - name: str - """NAICS title""" - - -class IndustryRetrieveNaicsResponse(BaseModel): - codes: Optional[List[Code]] = None - """Array of NAICS codes and titles.""" - - domain: Optional[str] = None - """Domain found for the brand""" - - status: Optional[str] = None - """Status of the response, e.g., 'ok'""" - - type: Optional[str] = None - """Industry classification type, for naics api it will be `naics`""" diff --git a/tests/api_resources/test_industry.py b/tests/api_resources/test_industry.py deleted file mode 100644 index 543c358..0000000 --- a/tests/api_resources/test_industry.py +++ /dev/null @@ -1,114 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from context.dev import ContextDev, AsyncContextDev -from tests.utils import assert_matches_type -from context.dev.types import IndustryRetrieveNaicsResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestIndustry: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_retrieve_naics(self, client: ContextDev) -> None: - industry = client.industry.retrieve_naics( - input="input", - ) - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_retrieve_naics_with_all_params(self, client: ContextDev) -> None: - industry = client.industry.retrieve_naics( - input="input", - max_results=1, - min_results=1, - timeout_ms=1000, - ) - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_retrieve_naics(self, client: ContextDev) -> None: - response = client.industry.with_raw_response.retrieve_naics( - input="input", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - industry = response.parse() - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_retrieve_naics(self, client: ContextDev) -> None: - with client.industry.with_streaming_response.retrieve_naics( - input="input", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - industry = response.parse() - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncIndustry: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_retrieve_naics(self, async_client: AsyncContextDev) -> None: - industry = await async_client.industry.retrieve_naics( - input="input", - ) - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_retrieve_naics_with_all_params(self, async_client: AsyncContextDev) -> None: - industry = await async_client.industry.retrieve_naics( - input="input", - max_results=1, - min_results=1, - timeout_ms=1000, - ) - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_retrieve_naics(self, async_client: AsyncContextDev) -> None: - response = await async_client.industry.with_raw_response.retrieve_naics( - input="input", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - industry = await response.parse() - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_retrieve_naics(self, async_client: AsyncContextDev) -> None: - async with async_client.industry.with_streaming_response.retrieve_naics( - input="input", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - industry = await response.parse() - assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) - - assert cast(Any, response.is_closed) is True From 2af78a43c1e9b5654668e82c009b679dc3ae8e73 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:10:03 +0000 Subject: [PATCH 23/27] feat(api): manual updates --- .stats.yml | 4 +- api.md | 12 + src/context/dev/_client.py | 39 +++- src/context/dev/resources/__init__.py | 14 ++ src/context/dev/resources/industry.py | 217 ++++++++++++++++++ src/context/dev/types/__init__.py | 2 + .../types/industry_retrieve_naics_params.py | 34 +++ .../types/industry_retrieve_naics_response.py | 33 +++ tests/api_resources/test_industry.py | 114 +++++++++ 9 files changed, 466 insertions(+), 3 deletions(-) create mode 100644 src/context/dev/resources/industry.py create mode 100644 src/context/dev/types/industry_retrieve_naics_params.py create mode 100644 src/context/dev/types/industry_retrieve_naics_response.py create mode 100644 tests/api_resources/test_industry.py diff --git a/.stats.yml b/.stats.yml index a30858e..cdd1e87 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 20 +configured_endpoints: 21 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-7e43493a842618882d9d7d4f33c51d4b6a1e99c960f8be0996bd544864a5e79f.yml openapi_spec_hash: 44bc5177ca909b32a1617ce637f5d704 -config_hash: 85e90b8fda2e1f326ca60f8ab3e53704 +config_hash: 7d13dca2b2c6f71fc463cb6062efa5ea diff --git a/api.md b/api.md index d136a84..963807e 100644 --- a/api.md +++ b/api.md @@ -66,6 +66,18 @@ Methods: - client.brand.retrieve_by_ticker(\*\*params) -> BrandRetrieveByTickerResponse - client.brand.retrieve_simplified(\*\*params) -> BrandRetrieveSimplifiedResponse +# Industry + +Types: + +```python +from context.dev.types import IndustryRetrieveNaicsResponse +``` + +Methods: + +- client.industry.retrieve_naics(\*\*params) -> IndustryRetrieveNaicsResponse + # Utility Types: diff --git a/src/context/dev/_client.py b/src/context/dev/_client.py index 5cf4972..ace042b 100644 --- a/src/context/dev/_client.py +++ b/src/context/dev/_client.py @@ -31,11 +31,12 @@ ) if TYPE_CHECKING: - from .resources import ai, web, brand, utility + from .resources import ai, web, brand, utility, industry from .resources.ai import AIResource, AsyncAIResource from .resources.web import WebResource, AsyncWebResource from .resources.brand import BrandResource, AsyncBrandResource from .resources.utility import UtilityResource, AsyncUtilityResource + from .resources.industry import IndustryResource, AsyncIndustryResource __all__ = [ "Timeout", @@ -122,6 +123,12 @@ def brand(self) -> BrandResource: return BrandResource(self) + @cached_property + def industry(self) -> IndustryResource: + from .resources.industry import IndustryResource + + return IndustryResource(self) + @cached_property def utility(self) -> UtilityResource: from .resources.utility import UtilityResource @@ -314,6 +321,12 @@ def brand(self) -> AsyncBrandResource: return AsyncBrandResource(self) + @cached_property + def industry(self) -> AsyncIndustryResource: + from .resources.industry import AsyncIndustryResource + + return AsyncIndustryResource(self) + @cached_property def utility(self) -> AsyncUtilityResource: from .resources.utility import AsyncUtilityResource @@ -457,6 +470,12 @@ def brand(self) -> brand.BrandResourceWithRawResponse: return BrandResourceWithRawResponse(self._client.brand) + @cached_property + def industry(self) -> industry.IndustryResourceWithRawResponse: + from .resources.industry import IndustryResourceWithRawResponse + + return IndustryResourceWithRawResponse(self._client.industry) + @cached_property def utility(self) -> utility.UtilityResourceWithRawResponse: from .resources.utility import UtilityResourceWithRawResponse @@ -488,6 +507,12 @@ def brand(self) -> brand.AsyncBrandResourceWithRawResponse: return AsyncBrandResourceWithRawResponse(self._client.brand) + @cached_property + def industry(self) -> industry.AsyncIndustryResourceWithRawResponse: + from .resources.industry import AsyncIndustryResourceWithRawResponse + + return AsyncIndustryResourceWithRawResponse(self._client.industry) + @cached_property def utility(self) -> utility.AsyncUtilityResourceWithRawResponse: from .resources.utility import AsyncUtilityResourceWithRawResponse @@ -519,6 +544,12 @@ def brand(self) -> brand.BrandResourceWithStreamingResponse: return BrandResourceWithStreamingResponse(self._client.brand) + @cached_property + def industry(self) -> industry.IndustryResourceWithStreamingResponse: + from .resources.industry import IndustryResourceWithStreamingResponse + + return IndustryResourceWithStreamingResponse(self._client.industry) + @cached_property def utility(self) -> utility.UtilityResourceWithStreamingResponse: from .resources.utility import UtilityResourceWithStreamingResponse @@ -550,6 +581,12 @@ def brand(self) -> brand.AsyncBrandResourceWithStreamingResponse: return AsyncBrandResourceWithStreamingResponse(self._client.brand) + @cached_property + def industry(self) -> industry.AsyncIndustryResourceWithStreamingResponse: + from .resources.industry import AsyncIndustryResourceWithStreamingResponse + + return AsyncIndustryResourceWithStreamingResponse(self._client.industry) + @cached_property def utility(self) -> utility.AsyncUtilityResourceWithStreamingResponse: from .resources.utility import AsyncUtilityResourceWithStreamingResponse diff --git a/src/context/dev/resources/__init__.py b/src/context/dev/resources/__init__.py index 0980c63..edc5356 100644 --- a/src/context/dev/resources/__init__.py +++ b/src/context/dev/resources/__init__.py @@ -32,6 +32,14 @@ UtilityResourceWithStreamingResponse, AsyncUtilityResourceWithStreamingResponse, ) +from .industry import ( + IndustryResource, + AsyncIndustryResource, + IndustryResourceWithRawResponse, + AsyncIndustryResourceWithRawResponse, + IndustryResourceWithStreamingResponse, + AsyncIndustryResourceWithStreamingResponse, +) __all__ = [ "WebResource", @@ -52,6 +60,12 @@ "AsyncBrandResourceWithRawResponse", "BrandResourceWithStreamingResponse", "AsyncBrandResourceWithStreamingResponse", + "IndustryResource", + "AsyncIndustryResource", + "IndustryResourceWithRawResponse", + "AsyncIndustryResourceWithRawResponse", + "IndustryResourceWithStreamingResponse", + "AsyncIndustryResourceWithStreamingResponse", "UtilityResource", "AsyncUtilityResource", "UtilityResourceWithRawResponse", diff --git a/src/context/dev/resources/industry.py b/src/context/dev/resources/industry.py new file mode 100644 index 0000000..73c96ac --- /dev/null +++ b/src/context/dev/resources/industry.py @@ -0,0 +1,217 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import industry_retrieve_naics_params +from .._types import Body, Omit, Query, Headers, NotGiven, 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.industry_retrieve_naics_response import IndustryRetrieveNaicsResponse + +__all__ = ["IndustryResource", "AsyncIndustryResource"] + + +class IndustryResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> IndustryResourceWithRawResponse: + """ + 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers + """ + return IndustryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> IndustryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response + """ + return IndustryResourceWithStreamingResponse(self) + + def retrieve_naics( + self, + *, + input: str, + max_results: int | Omit = omit, + min_results: 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, + ) -> IndustryRetrieveNaicsResponse: + """ + Classify any brand into 2022 NAICS industry codes from its domain or name. + + Args: + input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided, + it will be used for classification, otherwise, we will search for the brand + using the provided title. + + max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults + to 5. + + min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. + + 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/naics", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "input": input, + "max_results": max_results, + "min_results": min_results, + "timeout_ms": timeout_ms, + }, + industry_retrieve_naics_params.IndustryRetrieveNaicsParams, + ), + ), + cast_to=IndustryRetrieveNaicsResponse, + ) + + +class AsyncIndustryResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncIndustryResourceWithRawResponse: + """ + 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/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncIndustryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncIndustryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response + """ + return AsyncIndustryResourceWithStreamingResponse(self) + + async def retrieve_naics( + self, + *, + input: str, + max_results: int | Omit = omit, + min_results: 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, + ) -> IndustryRetrieveNaicsResponse: + """ + Classify any brand into 2022 NAICS industry codes from its domain or name. + + Args: + input: Brand domain or title to retrieve NAICS code for. If a valid domain is provided, + it will be used for classification, otherwise, we will search for the brand + using the provided title. + + max_results: Maximum number of NAICS codes to return. Must be between 1 and 10. Defaults + to 5. + + min_results: Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1. + + 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/naics", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "input": input, + "max_results": max_results, + "min_results": min_results, + "timeout_ms": timeout_ms, + }, + industry_retrieve_naics_params.IndustryRetrieveNaicsParams, + ), + ), + cast_to=IndustryRetrieveNaicsResponse, + ) + + +class IndustryResourceWithRawResponse: + def __init__(self, industry: IndustryResource) -> None: + self._industry = industry + + self.retrieve_naics = to_raw_response_wrapper( + industry.retrieve_naics, + ) + + +class AsyncIndustryResourceWithRawResponse: + def __init__(self, industry: AsyncIndustryResource) -> None: + self._industry = industry + + self.retrieve_naics = async_to_raw_response_wrapper( + industry.retrieve_naics, + ) + + +class IndustryResourceWithStreamingResponse: + def __init__(self, industry: IndustryResource) -> None: + self._industry = industry + + self.retrieve_naics = to_streamed_response_wrapper( + industry.retrieve_naics, + ) + + +class AsyncIndustryResourceWithStreamingResponse: + def __init__(self, industry: AsyncIndustryResource) -> None: + self._industry = industry + + self.retrieve_naics = async_to_streamed_response_wrapper( + industry.retrieve_naics, + ) diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py index 3d70d6b..232daf5 100644 --- a/src/context/dev/types/__init__.py +++ b/src/context/dev/types/__init__.py @@ -28,6 +28,7 @@ from .web_extract_styleguide_params import WebExtractStyleguideParams as WebExtractStyleguideParams from .web_web_scrape_sitemap_params import WebWebScrapeSitemapParams as WebWebScrapeSitemapParams from .brand_retrieve_by_email_params import BrandRetrieveByEmailParams as BrandRetrieveByEmailParams +from .industry_retrieve_naics_params import IndustryRetrieveNaicsParams as IndustryRetrieveNaicsParams 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 @@ -36,6 +37,7 @@ from .web_web_scrape_sitemap_response import WebWebScrapeSitemapResponse as WebWebScrapeSitemapResponse from .brand_retrieve_by_email_response import BrandRetrieveByEmailResponse as BrandRetrieveByEmailResponse 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 .brand_retrieve_by_ticker_response import BrandRetrieveByTickerResponse as BrandRetrieveByTickerResponse from .brand_retrieve_simplified_response import BrandRetrieveSimplifiedResponse as BrandRetrieveSimplifiedResponse diff --git a/src/context/dev/types/industry_retrieve_naics_params.py b/src/context/dev/types/industry_retrieve_naics_params.py new file mode 100644 index 0000000..03db484 --- /dev/null +++ b/src/context/dev/types/industry_retrieve_naics_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_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["IndustryRetrieveNaicsParams"] + + +class IndustryRetrieveNaicsParams(TypedDict, total=False): + input: Required[str] + """Brand domain or title to retrieve NAICS code for. + + If a valid domain is provided, it will be used for classification, otherwise, we + will search for the brand using the provided title. + """ + + max_results: Annotated[int, PropertyInfo(alias="maxResults")] + """Maximum number of NAICS codes to return. + + Must be between 1 and 10. Defaults to 5. + """ + + min_results: Annotated[int, PropertyInfo(alias="minResults")] + """Minimum number of NAICS codes to return. Must be at least 1. Defaults to 1.""" + + 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/industry_retrieve_naics_response.py b/src/context/dev/types/industry_retrieve_naics_response.py new file mode 100644 index 0000000..bcfc2ad --- /dev/null +++ b/src/context/dev/types/industry_retrieve_naics_response.py @@ -0,0 +1,33 @@ +# 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__ = ["IndustryRetrieveNaicsResponse", "Code"] + + +class Code(BaseModel): + code: str + """NAICS code""" + + confidence: Literal["high", "medium", "low"] + """Confidence level for how well this NAICS code matches the company description""" + + name: str + """NAICS title""" + + +class IndustryRetrieveNaicsResponse(BaseModel): + codes: Optional[List[Code]] = None + """Array of NAICS codes and titles.""" + + domain: Optional[str] = None + """Domain found for the brand""" + + status: Optional[str] = None + """Status of the response, e.g., 'ok'""" + + type: Optional[str] = None + """Industry classification type, for naics api it will be `naics`""" diff --git a/tests/api_resources/test_industry.py b/tests/api_resources/test_industry.py new file mode 100644 index 0000000..543c358 --- /dev/null +++ b/tests/api_resources/test_industry.py @@ -0,0 +1,114 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from context.dev import ContextDev, AsyncContextDev +from tests.utils import assert_matches_type +from context.dev.types import IndustryRetrieveNaicsResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestIndustry: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve_naics(self, client: ContextDev) -> None: + industry = client.industry.retrieve_naics( + input="input", + ) + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve_naics_with_all_params(self, client: ContextDev) -> None: + industry = client.industry.retrieve_naics( + input="input", + max_results=1, + min_results=1, + timeout_ms=1000, + ) + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_retrieve_naics(self, client: ContextDev) -> None: + response = client.industry.with_raw_response.retrieve_naics( + input="input", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + industry = response.parse() + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_retrieve_naics(self, client: ContextDev) -> None: + with client.industry.with_streaming_response.retrieve_naics( + input="input", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + industry = response.parse() + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncIndustry: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve_naics(self, async_client: AsyncContextDev) -> None: + industry = await async_client.industry.retrieve_naics( + input="input", + ) + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve_naics_with_all_params(self, async_client: AsyncContextDev) -> None: + industry = await async_client.industry.retrieve_naics( + input="input", + max_results=1, + min_results=1, + timeout_ms=1000, + ) + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_retrieve_naics(self, async_client: AsyncContextDev) -> None: + response = await async_client.industry.with_raw_response.retrieve_naics( + input="input", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + industry = await response.parse() + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_retrieve_naics(self, async_client: AsyncContextDev) -> None: + async with async_client.industry.with_streaming_response.retrieve_naics( + input="input", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + industry = await response.parse() + assert_matches_type(IndustryRetrieveNaicsResponse, industry, path=["response"]) + + assert cast(Any, response.is_closed) is True From 596928131b8a6aeb230bf5a676a90b0c74e47777 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:19:23 +0000 Subject: [PATCH 24/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index cdd1e87..08ffe17 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/context-dev%2Fcontext.dev-7e43493a842618882d9d7d4f33c51d4b6a1e99c960f8be0996bd544864a5e79f.yml -openapi_spec_hash: 44bc5177ca909b32a1617ce637f5d704 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-8ac1b9ffd4276c038dac4e0bd15ce55bd98bbfab12c552bb40da4a0c08e04f5c.yml +openapi_spec_hash: abf54ec56c7f443c31018b325b351118 config_hash: 7d13dca2b2c6f71fc463cb6062efa5ea From 573a70cee8cb9bed8377f816bfba419ecbe86109 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 03:13:31 +0000 Subject: [PATCH 25/27] perf(client): optimize file structure copying in multipart requests --- src/context/dev/_files.py | 56 ++++++++++++++++- src/context/dev/_utils/__init__.py | 1 - src/context/dev/_utils/_utils.py | 15 ----- tests/test_deepcopy.py | 58 ----------------- tests/test_files.py | 99 +++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 78 deletions(-) delete mode 100644 tests/test_deepcopy.py diff --git a/src/context/dev/_files.py b/src/context/dev/_files.py index cc14c14..0fdce17 100644 --- a/src/context/dev/_files.py +++ b/src/context/dev/_files.py @@ -3,8 +3,8 @@ import io import os import pathlib -from typing import overload -from typing_extensions import TypeGuard +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard import anyio @@ -17,7 +17,9 @@ HttpxFileContent, HttpxRequestFiles, ) -from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +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]: @@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent: 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/src/context/dev/_utils/__init__.py b/src/context/dev/_utils/__init__.py index 10cb66d..1c090e5 100644 --- a/src/context/dev/_utils/__init__.py +++ b/src/context/dev/_utils/__init__.py @@ -24,7 +24,6 @@ coerce_integer as coerce_integer, file_from_path as file_from_path, strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, maybe_coerce_float as maybe_coerce_float, get_required_header as get_required_header, diff --git a/src/context/dev/_utils/_utils.py b/src/context/dev/_utils/_utils.py index 63b8cd6..771859f 100644 --- a/src/context/dev/_utils/_utils.py +++ b/src/context/dev/_utils/_utils.py @@ -177,21 +177,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: return isinstance(obj, Iterable) -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - # copied from https://github.com/Rapptz/RoboDanny def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: size = len(seq) diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py deleted file mode 100644 index ec2cbfc..0000000 --- a/tests/test_deepcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -from context.dev._utils import deepcopy_minimal - - -def assert_different_identities(obj1: object, obj2: object) -> None: - assert obj1 == obj2 - assert id(obj1) != id(obj2) - - -def test_simple_dict() -> None: - obj1 = {"foo": "bar"} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_dict() -> None: - obj1 = {"foo": {"bar": True}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - - -def test_complex_nested_dict() -> None: - obj1 = {"foo": {"bar": [{"hello": "world"}]}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) - assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) - - -def test_simple_list() -> None: - obj1 = ["a", "b", "c"] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_list() -> None: - obj1 = ["a", [1, 2, 3]] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1[1], obj2[1]) - - -class MyObject: ... - - -def test_ignores_other_types() -> None: - # custom classes - my_obj = MyObject() - obj1 = {"foo": my_obj} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert obj1["foo"] is my_obj - - # tuples - obj3 = ("a", "b") - obj4 = deepcopy_minimal(obj3) - assert obj3 is obj4 diff --git a/tests/test_files.py b/tests/test_files.py index ede2488..e9e3a2d 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -4,7 +4,8 @@ import pytest from dirty_equals import IsDict, IsList, IsBytes, IsTuple -from context.dev._files import to_httpx_files, async_to_httpx_files +from context.dev._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files +from context.dev._utils import extract_files readme_path = Path(__file__).parent.parent.joinpath("README.md") @@ -49,3 +50,99 @@ def test_string_not_allowed() -> None: "file": "foo", # type: ignore } ) + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert obj1 is not obj2 + + +class TestDeepcopyWithPaths: + def test_copies_top_level_dict(self) -> None: + original = {"file": b"data", "other": "value"} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + + def test_file_value_is_same_reference(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + assert result["file"] is file_bytes + + def test_list_popped_wholesale(self) -> None: + files = [b"f1", b"f2"] + original = {"files": files, "title": "t"} + result = deepcopy_with_paths(original, [["files", ""]]) + assert_different_identities(result, original) + result_files = result["files"] + assert isinstance(result_files, list) + assert_different_identities(result_files, files) + + def test_nested_array_path_copies_list_and_elements(self) -> None: + elem1 = {"file": b"f1", "extra": 1} + elem2 = {"file": b"f2", "extra": 2} + original = {"items": [elem1, elem2]} + result = deepcopy_with_paths(original, [["items", "", "file"]]) + assert_different_identities(result, original) + result_items = result["items"] + assert isinstance(result_items, list) + assert_different_identities(result_items, original["items"]) + assert_different_identities(result_items[0], elem1) + assert_different_identities(result_items[1], elem2) + + def test_empty_paths_returns_same_object(self) -> None: + original = {"foo": "bar"} + result = deepcopy_with_paths(original, []) + assert result is original + + def test_multiple_paths(self) -> None: + f1 = b"file1" + f2 = b"file2" + original = {"a": f1, "b": f2, "c": "unchanged"} + result = deepcopy_with_paths(original, [["a"], ["b"]]) + assert_different_identities(result, original) + assert result["a"] is f1 + assert result["b"] is f2 + assert result["c"] is original["c"] + + def test_extract_files_does_not_mutate_original_top_level(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes, "other": "value"} + + copied = deepcopy_with_paths(original, [["file"]]) + extracted = extract_files(copied, paths=[["file"]]) + + assert extracted == [("file", file_bytes)] + assert original == {"file": file_bytes, "other": "value"} + assert copied == {"other": "value"} + + def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: + file1 = b"f1" + file2 = b"f2" + original = { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + + copied = deepcopy_with_paths(original, [["items", "", "file"]]) + extracted = extract_files(copied, paths=[["items", "", "file"]]) + + assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert original == { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + assert copied == { + "items": [ + {"extra": 1}, + {"extra": 2}, + ], + "title": "example", + } From c3dd0137eed09a66613fc9104f520b1383ca9aa8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 17:37:02 +0000 Subject: [PATCH 26/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 08ffe17..6d316e1 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/context-dev%2Fcontext.dev-8ac1b9ffd4276c038dac4e0bd15ce55bd98bbfab12c552bb40da4a0c08e04f5c.yml -openapi_spec_hash: abf54ec56c7f443c31018b325b351118 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev%2Fcontext.dev-5afee68d7308a77a9ef51fb53917080746f96327c6d745fc029363a7d1494c3c.yml +openapi_spec_hash: b2e32bb58d92a00f6ede04c4f7bf1a34 config_hash: 7d13dca2b2c6f71fc463cb6062efa5ea From 0b47fd480c417dc899cc1651d60eb343442e1ae2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 17:37:29 +0000 Subject: [PATCH 27/27] release: 0.8.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/context/dev/_version.py | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1b77f50..6538ca9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.0" + ".": "0.8.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c00b74..cc6b274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## 0.8.0 (2026-04-19) + +Full Changelog: [v0.7.0...v0.8.0](https://github.com/context-dot-dev/context-python-sdk/compare/v0.7.0...v0.8.0) + +### Features + +* **api:** api update ([512bf26](https://github.com/context-dot-dev/context-python-sdk/commit/512bf2610e59733e0e950531ca9e6018a5446672)) +* **api:** api update ([f98376c](https://github.com/context-dot-dev/context-python-sdk/commit/f98376cf22a331138f716f8abeb4458a8e1e38c2)) +* **api:** api update ([9ddc161](https://github.com/context-dot-dev/context-python-sdk/commit/9ddc161215cd12384152295997b95d5ac44bec08)) +* **api:** api update ([639075e](https://github.com/context-dot-dev/context-python-sdk/commit/639075e0a0aa6203375592b8b67438ae0719ca26)) +* **api:** api update ([707a6c3](https://github.com/context-dot-dev/context-python-sdk/commit/707a6c3e49096e2248ec9e4ae7e683138647c2d6)) +* **api:** api update ([bbc2458](https://github.com/context-dot-dev/context-python-sdk/commit/bbc2458c38933208b22e234d6bf1f1ffee7f883e)) +* **api:** api update ([241bacf](https://github.com/context-dot-dev/context-python-sdk/commit/241bacf196c1e316aef39429e3919fda1c5a78eb)) +* **api:** api update ([648e71b](https://github.com/context-dot-dev/context-python-sdk/commit/648e71bf7afb870dceafe6485bf47932c74ebe1b)) +* **api:** api update ([656e585](https://github.com/context-dot-dev/context-python-sdk/commit/656e585bcdf228d8651cdab5ee37f1119a51eafa)) +* **api:** api update ([3fc9944](https://github.com/context-dot-dev/context-python-sdk/commit/3fc9944ff014539c18e0216b93c936c1df2b0219)) +* **api:** api update ([b9374f5](https://github.com/context-dot-dev/context-python-sdk/commit/b9374f57a2a0ed207c39e80b6fbf710bbe170e11)) +* **api:** manual updates ([2af78a4](https://github.com/context-dot-dev/context-python-sdk/commit/2af78a43c1e9b5654668e82c009b679dc3ae8e73)) +* **api:** manual updates ([d6acbc6](https://github.com/context-dot-dev/context-python-sdk/commit/d6acbc6552d84e58a6ff7a9e99952a2a7a624647)) +* **api:** manual updates ([ec8ff2d](https://github.com/context-dot-dev/context-python-sdk/commit/ec8ff2d2a0d84fcb0c05791fa913441c60ed9c24)) + + +### Bug Fixes + +* ensure file data are only sent as 1 parameter ([7c314a2](https://github.com/context-dot-dev/context-python-sdk/commit/7c314a29f73103eb592686df101c86c6cfd26b3f)) + + +### Performance Improvements + +* **client:** optimize file structure copying in multipart requests ([573a70c](https://github.com/context-dot-dev/context-python-sdk/commit/573a70cee8cb9bed8377f816bfba419ecbe86109)) + ## 0.7.0 (2026-04-09) Full Changelog: [v0.6.0...v0.7.0](https://github.com/context-dot-dev/context-python-sdk/compare/v0.6.0...v0.7.0) diff --git a/pyproject.toml b/pyproject.toml index c5f1939..8a95885 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "context.dev" -version = "0.7.0" +version = "0.8.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 7b88fdc..e505869 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.7.0" # x-release-please-version +__version__ = "0.8.0" # x-release-please-version