From a10a1ed1298179229f69dc3c12b899f6242ed1b4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:59:17 +0000 Subject: [PATCH 1/9] fix: use correct field name format for multipart file arrays --- src/tabstack/_qs.py | 8 ++----- src/tabstack/_types.py | 3 +++ src/tabstack/_utils/_utils.py | 42 ++++++++++++++++++++++++++++------- tests/test_extract_files.py | 28 ++++++++++++++++++----- tests/test_files.py | 2 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/tabstack/_qs.py b/src/tabstack/_qs.py index de8c99b..4127c19 100644 --- a/src/tabstack/_qs.py +++ b/src/tabstack/_qs.py @@ -2,17 +2,13 @@ from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args +from typing_extensions import get_args -from ._types import NotGiven, not_given +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 diff --git a/src/tabstack/_types.py b/src/tabstack/_types.py index 818a79d..7e89ea2 100644 --- a/src/tabstack/_types.py +++ b/src/tabstack/_types.py @@ -47,6 +47,9 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances diff --git a/src/tabstack/_utils/_utils.py b/src/tabstack/_utils/_utils.py index 771859f..199cd23 100644 --- a/src/tabstack/_utils/_utils.py +++ b/src/tabstack/_utils/_utils.py @@ -17,11 +17,11 @@ ) from pathlib import Path from datetime import date, datetime -from typing_extensions import TypeGuard +from typing_extensions import TypeGuard, get_args import sniffio -from .._types import Omit, NotGiven, FileTypes, HeadersLike +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -40,25 +40,45 @@ def extract_files( query: Mapping[str, object], *, paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, + array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] @@ -75,9 +95,11 @@ def _extract_items( if is_list(obj): files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) @@ -106,6 +128,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -117,9 +140,12 @@ def _extract_items( item, path, index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, ) - for item in obj + for array_index, item in enumerate(obj) ] ) diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index a11b04c..e40b192 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from tabstack._types import FileTypes +from tabstack._types import FileTypes, ArrayFormat from tabstack._utils import extract_files @@ -37,10 +37,7 @@ def test_multiple_files() -> None: 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 extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @@ -71,3 +68,24 @@ def test_ignores_incorrect_paths( expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected + + +@pytest.mark.parametrize( + "array_format,expected_top_level,expected_nested", + [ + ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), + ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), + ], +) +def test_array_format_controls_file_field_names( + array_format: ArrayFormat, + expected_top_level: list[tuple[str, FileTypes]], + expected_nested: list[tuple[str, FileTypes]], +) -> None: + top_level = {"files": [b"a", b"b"]} + assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level + + nested = {"items": [{"file": b"a"}, {"file": b"b"}]} + assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested diff --git a/tests/test_files.py b/tests/test_files.py index cd25fef..cf7fb6e 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -131,7 +131,7 @@ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) - assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1}, From 4699481aee8341952e2cae287bc0ff0fc629a292 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:26:50 +0000 Subject: [PATCH 2/9] feat: support setting headers via env --- src/tabstack/_client.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/tabstack/_client.py b/src/tabstack/_client.py index 9085397..1c3577e 100644 --- a/src/tabstack/_client.py +++ b/src/tabstack/_client.py @@ -19,7 +19,11 @@ RequestOptions, not_given, ) -from ._utils import is_given, get_async_library +from ._utils import ( + is_given, + is_mapping_t, + get_async_library, +) from ._compat import cached_property from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream @@ -92,6 +96,15 @@ def __init__( if base_url is None: base_url = f"https://api.tabstack.ai/v1" + custom_headers_env = os.environ.get("TABSTACK_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -278,6 +291,15 @@ def __init__( if base_url is None: base_url = f"https://api.tabstack.ai/v1" + custom_headers_env = os.environ.get("TABSTACK_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, From 20d3ff3b4c5e8d7d287ef9807ecbc33c522bed37 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:21:27 +0000 Subject: [PATCH 3/9] feat(automate): emit task:trace_context SSE event with trace ID --- .stats.yml | 4 ++-- src/tabstack/types/automate_event.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 93a1821..e14e672 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla%2Ftabstack-5e6e6508dca15391d72cbea74ec33838c511cbc17e046699142f97d1573b069a.yml -openapi_spec_hash: b73b5922a495fd375736896912815d18 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla%2Ftabstack-1ccfa0fbdfebb542246f24972683742e8ffa175bee4fea8040e814695cde951c.yml +openapi_spec_hash: 8a4612fa101f82893c6edca118a86a2d config_hash: 57c64e5e8fe99c1bd7af536d82af4ad9 diff --git a/src/tabstack/types/automate_event.py b/src/tabstack/types/automate_event.py index 388dba2..d03e447 100644 --- a/src/tabstack/types/automate_event.py +++ b/src/tabstack/types/automate_event.py @@ -147,6 +147,8 @@ "V1AutomateEventTaskSetupData", "V1AutomateEventTaskStarted", "V1AutomateEventTaskStartedData", + "V1AutomateEventTaskTraceContext", + "V1AutomateEventTaskTraceContextData", "V1AutomateEventTaskValidated", "V1AutomateEventTaskValidatedData", "V1AutomateEventTaskValidationError", @@ -2550,6 +2552,30 @@ class V1AutomateEventTaskStarted(BaseModel): event: Literal["task:started"] +class V1AutomateEventTaskTraceContextData(BaseModel): + """Payload for the task:trace_context event. + + Carries the OpenTelemetry trace ID for this /v1/automate request so consumers can deep-link to distributed-tracing UIs (e.g. Cloud Trace, Cloud Logging) for the run. + """ + + trace_id: str = FieldInfo(alias="traceId") + """W3C trace ID — 32-character lowercase hexadecimal string.""" + + +class V1AutomateEventTaskTraceContext(BaseModel): + """Envelope for the "task:trace_context" event from /v1/automate.""" + + data: V1AutomateEventTaskTraceContextData + """Payload for the task:trace_context event. + + Carries the OpenTelemetry trace ID for this /v1/automate request so consumers + can deep-link to distributed-tracing UIs (e.g. Cloud Trace, Cloud Logging) for + the run. + """ + + event: Literal["task:trace_context"] + + class V1AutomateEventTaskValidatedData(BaseModel): """Event data for task validation""" @@ -2630,6 +2656,7 @@ class V1AutomateEventTaskValidationError(BaseModel): V1AutomateEventTaskMetricsIncremental, V1AutomateEventTaskSetup, V1AutomateEventTaskStarted, + V1AutomateEventTaskTraceContext, V1AutomateEventTaskValidated, V1AutomateEventTaskValidationError, ], From 36a009f3b298853e2384a82966046267b71ec966 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:13:24 +0000 Subject: [PATCH 4/9] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index e14e672..0e39953 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla%2Ftabstack-1ccfa0fbdfebb542246f24972683742e8ffa175bee4fea8040e814695cde951c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-1ccfa0fbdfebb542246f24972683742e8ffa175bee4fea8040e814695cde951c.yml openapi_spec_hash: 8a4612fa101f82893c6edca118a86a2d config_hash: 57c64e5e8fe99c1bd7af536d82af4ad9 From 2fd7fb43e52f544f2cd1d89447b16843a31620e3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:19:58 +0000 Subject: [PATCH 5/9] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 0e39953..8d539a5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-1ccfa0fbdfebb542246f24972683742e8ffa175bee4fea8040e814695cde951c.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-7d424abc15447c30f5050aaf11eaf7d27b310b36f637537c51dedc6b7f884474.yml openapi_spec_hash: 8a4612fa101f82893c6edca118a86a2d config_hash: 57c64e5e8fe99c1bd7af536d82af4ad9 From c909f52bf36e746213c2cb8b316e6eb1909a2e80 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 20:56:38 +0000 Subject: [PATCH 6/9] chore(internal): reformat pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3050875..226974b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -154,7 +154,7 @@ show_error_codes = true # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ['src/tabstack/_files.py', '_dev/.*.py', 'tests/.*'] +exclude = ["src/tabstack/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true From 15bcc6b29cf0e64fa5bf544f099471dac26d9cea Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 16:16:49 +0000 Subject: [PATCH 7/9] feat(api): add endpoint specfic timeouts The default timeout of 60 seconds regularly cuts off automate and research requests. These values have been updated to allow the slowest requests we have seen complete for the API --- .stats.yml | 4 ++-- src/tabstack/resources/agent.py | 11 ++++++++++- src/tabstack/resources/extract.py | 11 ++++++++++- src/tabstack/resources/generate.py | 7 ++++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8d539a5..a838a7f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-7d424abc15447c30f5050aaf11eaf7d27b310b36f637537c51dedc6b7f884474.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-a587e9165648e2a318298ea7df679602eacb2d2126032fb1ef83de1aa34c3ff6.yml openapi_spec_hash: 8a4612fa101f82893c6edca118a86a2d -config_hash: 57c64e5e8fe99c1bd7af536d82af4ad9 +config_hash: ef374c8c9d6f0a94ef9163bd41bf9052 diff --git a/src/tabstack/resources/agent.py b/src/tabstack/resources/agent.py index 01a80cb..11303e4 100644 --- a/src/tabstack/resources/agent.py +++ b/src/tabstack/resources/agent.py @@ -9,7 +9,7 @@ from ..types import agent_automate_params, agent_research_params, agent_automate_input_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import path_template, maybe_transform, async_maybe_transform +from .._utils import is_given, path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -18,6 +18,7 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) +from .._constants import DEFAULT_TIMEOUT from .._streaming import Stream, AsyncStream from .._base_client import make_request_options from ..types.automate_event import AutomateEvent @@ -112,6 +113,8 @@ def automate( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return self._post( "/automate", @@ -250,6 +253,8 @@ def research( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return self._post( "/research", @@ -360,6 +365,8 @@ async def automate( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return await self._post( "/automate", @@ -498,6 +505,8 @@ async def research( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return await self._post( "/research", diff --git a/src/tabstack/resources/extract.py b/src/tabstack/resources/extract.py index 06f0ff3..a4a2bb2 100644 --- a/src/tabstack/resources/extract.py +++ b/src/tabstack/resources/extract.py @@ -8,7 +8,7 @@ from ..types import extract_json_params, extract_markdown_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import is_given, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,6 +17,7 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) +from .._constants import DEFAULT_TIMEOUT from .._base_client import make_request_options from ..types.extract_json_response import ExtractJsonResponse from ..types.extract_markdown_response import ExtractMarkdownResponse @@ -83,6 +84,8 @@ def json( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return self._post( "/extract/json", body=maybe_transform( @@ -142,6 +145,8 @@ def markdown( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 180 return self._post( "/extract/markdown", body=maybe_transform( @@ -220,6 +225,8 @@ async def json( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return await self._post( "/extract/json", body=await async_maybe_transform( @@ -279,6 +286,8 @@ async def markdown( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 180 return await self._post( "/extract/markdown", body=await async_maybe_transform( diff --git a/src/tabstack/resources/generate.py b/src/tabstack/resources/generate.py index b9a2996..d222e7e 100644 --- a/src/tabstack/resources/generate.py +++ b/src/tabstack/resources/generate.py @@ -8,7 +8,7 @@ from ..types import generate_json_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import is_given, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -17,6 +17,7 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) +from .._constants import DEFAULT_TIMEOUT from .._base_client import make_request_options from ..types.generate_json_response import GenerateJsonResponse @@ -86,6 +87,8 @@ def json( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return self._post( "/generate/json", body=maybe_transform( @@ -169,6 +172,8 @@ async def json( timeout: Override the client-level default timeout for this request, in seconds """ + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 300 return await self._post( "/generate/json", body=await async_maybe_transform( From e89cb7a58d7e139cb46d54f15fbb26515a54f205 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:28:49 +0000 Subject: [PATCH 8/9] feat(api): manual updates --- .stats.yml | 4 +- api.md | 14 ++- src/tabstack/resources/agent.py | 5 +- src/tabstack/resources/extract.py | 9 +- src/tabstack/resources/generate.py | 5 +- src/tabstack/types/__init__.py | 3 + src/tabstack/types/agent_automate_params.py | 15 +-- src/tabstack/types/automate_event.py | 97 +------------------ src/tabstack/types/extract_json_params.py | 16 +-- src/tabstack/types/extract_markdown_params.py | 16 +-- src/tabstack/types/generate_json_params.py | 16 +-- src/tabstack/types/research_event.py | 43 +------- src/tabstack/types/shared/__init__.py | 3 + .../types/shared/geotarget_geo_target.py | 15 +++ src/tabstack/types/shared_params/__init__.py | 3 + .../shared_params/geotarget_geo_target.py | 15 +++ src/tabstack/types/v1_global_buffer.py | 37 +++++++ .../types/v1_research_question_assessment.py | 23 +++++ tests/api_resources/test_extract.py | 5 +- 19 files changed, 151 insertions(+), 193 deletions(-) create mode 100644 src/tabstack/types/shared/__init__.py create mode 100644 src/tabstack/types/shared/geotarget_geo_target.py create mode 100644 src/tabstack/types/shared_params/__init__.py create mode 100644 src/tabstack/types/shared_params/geotarget_geo_target.py create mode 100644 src/tabstack/types/v1_global_buffer.py create mode 100644 src/tabstack/types/v1_research_question_assessment.py diff --git a/.stats.yml b/.stats.yml index a838a7f..c91eb54 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 6 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-a587e9165648e2a318298ea7df679602eacb2d2126032fb1ef83de1aa34c3ff6.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mozilla/tabstack-c97fe4d7e39fd2f35a75dc7fdeb6d3f19aab2f9b56d4983c3376a5433dc9e268.yml openapi_spec_hash: 8a4612fa101f82893c6edca118a86a2d -config_hash: ef374c8c9d6f0a94ef9163bd41bf9052 +config_hash: 5827ec0cdbbac0e7c5db2f5456f67dca diff --git a/api.md b/api.md index 51b78b1..c9a0a65 100644 --- a/api.md +++ b/api.md @@ -1,9 +1,21 @@ +# Shared Types + +```python +from tabstack.types import GeotargetGeoTarget +``` + # Agent Types: ```python -from tabstack.types import AutomateEvent, ResearchEvent, AgentAutomateInputResponse +from tabstack.types import ( + AutomateEvent, + ResearchEvent, + V1GlobalBuffer, + V1ResearchQuestionAssessment, + AgentAutomateInputResponse, +) ``` Methods: diff --git a/src/tabstack/resources/agent.py b/src/tabstack/resources/agent.py index 11303e4..198a843 100644 --- a/src/tabstack/resources/agent.py +++ b/src/tabstack/resources/agent.py @@ -24,6 +24,7 @@ from ..types.automate_event import AutomateEvent from ..types.research_event import ResearchEvent from ..types.agent_automate_input_response import AgentAutomateInputResponse +from ..types.shared_params.geotarget_geo_target import GeotargetGeoTarget __all__ = ["AgentResource", "AsyncAgentResource"] @@ -53,7 +54,7 @@ def automate( *, task: str, data: object | Omit = omit, - geo_target: agent_automate_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, guardrails: str | Omit = omit, interactive: bool | Omit = omit, max_iterations: int | Omit = omit, @@ -305,7 +306,7 @@ async def automate( *, task: str, data: object | Omit = omit, - geo_target: agent_automate_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, guardrails: str | Omit = omit, interactive: bool | Omit = omit, max_iterations: int | Omit = omit, diff --git a/src/tabstack/resources/extract.py b/src/tabstack/resources/extract.py index a4a2bb2..19914ee 100644 --- a/src/tabstack/resources/extract.py +++ b/src/tabstack/resources/extract.py @@ -21,6 +21,7 @@ from .._base_client import make_request_options from ..types.extract_json_response import ExtractJsonResponse from ..types.extract_markdown_response import ExtractMarkdownResponse +from ..types.shared_params.geotarget_geo_target import GeotargetGeoTarget __all__ = ["ExtractResource", "AsyncExtractResource"] @@ -51,7 +52,7 @@ def json( json_schema: object, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: extract_json_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -109,7 +110,7 @@ def markdown( *, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: extract_markdown_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, metadata: bool | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -192,7 +193,7 @@ async def json( json_schema: object, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: extract_json_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -250,7 +251,7 @@ async def markdown( *, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: extract_markdown_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, metadata: bool | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/tabstack/resources/generate.py b/src/tabstack/resources/generate.py index d222e7e..d990445 100644 --- a/src/tabstack/resources/generate.py +++ b/src/tabstack/resources/generate.py @@ -20,6 +20,7 @@ from .._constants import DEFAULT_TIMEOUT from .._base_client import make_request_options from ..types.generate_json_response import GenerateJsonResponse +from ..types.shared_params.geotarget_geo_target import GeotargetGeoTarget __all__ = ["GenerateResource", "AsyncGenerateResource"] @@ -51,7 +52,7 @@ def json( json_schema: object, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: generate_json_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -136,7 +137,7 @@ async def json( json_schema: object, url: str, effort: Literal["min", "standard", "max"] | Omit = omit, - geo_target: generate_json_params.GeoTarget | Omit = omit, + geo_target: GeotargetGeoTarget | Omit = omit, nocache: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/tabstack/types/__init__.py b/src/tabstack/types/__init__.py index feec32c..83aab21 100644 --- a/src/tabstack/types/__init__.py +++ b/src/tabstack/types/__init__.py @@ -2,8 +2,10 @@ from __future__ import annotations +from .shared import GeotargetGeoTarget as GeotargetGeoTarget from .automate_event import AutomateEvent as AutomateEvent from .research_event import ResearchEvent as ResearchEvent +from .v1_global_buffer import V1GlobalBuffer as V1GlobalBuffer from .extract_json_params import ExtractJsonParams as ExtractJsonParams from .generate_json_params import GenerateJsonParams as GenerateJsonParams from .agent_automate_params import AgentAutomateParams as AgentAutomateParams @@ -14,3 +16,4 @@ from .extract_markdown_response import ExtractMarkdownResponse as ExtractMarkdownResponse from .agent_automate_input_params import AgentAutomateInputParams as AgentAutomateInputParams from .agent_automate_input_response import AgentAutomateInputResponse as AgentAutomateInputResponse +from .v1_research_question_assessment import V1ResearchQuestionAssessment as V1ResearchQuestionAssessment diff --git a/src/tabstack/types/agent_automate_params.py b/src/tabstack/types/agent_automate_params.py index 3d63b9c..583ac5a 100644 --- a/src/tabstack/types/agent_automate_params.py +++ b/src/tabstack/types/agent_automate_params.py @@ -5,8 +5,9 @@ from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo +from .shared_params.geotarget_geo_target import GeotargetGeoTarget -__all__ = ["AgentAutomateParams", "GeoTarget"] +__all__ = ["AgentAutomateParams"] class AgentAutomateParams(TypedDict, total=False): @@ -16,7 +17,7 @@ class AgentAutomateParams(TypedDict, total=False): data: object """JSON data to provide context for form filling or complex tasks""" - geo_target: GeoTarget + geo_target: GeotargetGeoTarget """Optional geotargeting parameters for proxy requests""" guardrails: str @@ -33,13 +34,3 @@ class AgentAutomateParams(TypedDict, total=False): url: str """Starting URL for the task""" - - -class GeoTarget(TypedDict, total=False): - """Optional geotargeting parameters for proxy requests""" - - country: str - """ - Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", - "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - """ diff --git a/src/tabstack/types/automate_event.py b/src/tabstack/types/automate_event.py index d03e447..e9c3ac5 100644 --- a/src/tabstack/types/automate_event.py +++ b/src/tabstack/types/automate_event.py @@ -8,6 +8,7 @@ from .._utils import PropertyInfo from .._models import BaseModel +from .v1_global_buffer import V1GlobalBuffer __all__ = [ "AutomateEvent", @@ -38,15 +39,11 @@ "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageUnionMember1", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageUnionMember1Buffer", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageByteLength", - "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBuffer", - "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBufferBuffer", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1File", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileData", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataUnionMember1", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataUnionMember1Buffer", "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataByteLength", - "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBuffer", - "V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBufferBuffer", "V1AutomateEventAIGenerationDataMessageAssistant", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1Text", @@ -55,8 +52,6 @@ "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataUnionMember1", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataUnionMember1Buffer", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataByteLength", - "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBuffer", - "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBufferBuffer", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1Reasoning", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1ToolCall", "V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1ToolResult", @@ -397,39 +392,11 @@ class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageByt byte_length: float = FieldInfo(alias="byteLength") -class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBufferBuffer(BaseModel): - byte_length: float = FieldInfo(alias="byteLength") - - -class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBuffer(BaseModel): - buffer: V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBufferBuffer - - byte_length: float = FieldInfo(alias="byteLength") - - byte_offset: float = FieldInfo(alias="byteOffset") - - bytes_per_element: float = FieldInfo(alias="BYTES_PER_ELEMENT") - - length: float - - if TYPE_CHECKING: - # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a - # value to this field, so for compatibility we avoid doing it at runtime. - __pydantic_extra__: Dict[str, float] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] - - # Stub to indicate that arbitrary properties are accepted. - # To access properties that are not valid identifiers you can use `getattr`, e.g. - # `getattr(obj, '$type')` - def __getattr__(self, attr: str) -> float: ... - else: - __pydantic_extra__: Dict[str, float] - - V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImage: TypeAlias = Union[ str, V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageUnionMember1, V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageByteLength, - V1AutomateEventAIGenerationDataMessageUserContentUnionMember1ImageImageV1GlobalBuffer, + V1GlobalBuffer, ] @@ -502,39 +469,11 @@ class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataByteL byte_length: float = FieldInfo(alias="byteLength") -class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBufferBuffer(BaseModel): - byte_length: float = FieldInfo(alias="byteLength") - - -class V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBuffer(BaseModel): - buffer: V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBufferBuffer - - byte_length: float = FieldInfo(alias="byteLength") - - byte_offset: float = FieldInfo(alias="byteOffset") - - bytes_per_element: float = FieldInfo(alias="BYTES_PER_ELEMENT") - - length: float - - if TYPE_CHECKING: - # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a - # value to this field, so for compatibility we avoid doing it at runtime. - __pydantic_extra__: Dict[str, float] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] - - # Stub to indicate that arbitrary properties are accepted. - # To access properties that are not valid identifiers you can use `getattr`, e.g. - # `getattr(obj, '$type')` - def __getattr__(self, attr: str) -> float: ... - else: - __pydantic_extra__: Dict[str, float] - - V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileData: TypeAlias = Union[ str, V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataUnionMember1, V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataByteLength, - V1AutomateEventAIGenerationDataMessageUserContentUnionMember1FileDataV1GlobalBuffer, + V1GlobalBuffer, ] @@ -680,39 +619,11 @@ class V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileData byte_length: float = FieldInfo(alias="byteLength") -class V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBufferBuffer(BaseModel): - byte_length: float = FieldInfo(alias="byteLength") - - -class V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBuffer(BaseModel): - buffer: V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBufferBuffer - - byte_length: float = FieldInfo(alias="byteLength") - - byte_offset: float = FieldInfo(alias="byteOffset") - - bytes_per_element: float = FieldInfo(alias="BYTES_PER_ELEMENT") - - length: float - - if TYPE_CHECKING: - # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a - # value to this field, so for compatibility we avoid doing it at runtime. - __pydantic_extra__: Dict[str, float] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] - - # Stub to indicate that arbitrary properties are accepted. - # To access properties that are not valid identifiers you can use `getattr`, e.g. - # `getattr(obj, '$type')` - def __getattr__(self, attr: str) -> float: ... - else: - __pydantic_extra__: Dict[str, float] - - V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileData: TypeAlias = Union[ str, V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataUnionMember1, V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataByteLength, - V1AutomateEventAIGenerationDataMessageAssistantContentUnionMember1FileDataV1GlobalBuffer, + V1GlobalBuffer, ] diff --git a/src/tabstack/types/extract_json_params.py b/src/tabstack/types/extract_json_params.py index d6d27a7..58ea5ff 100644 --- a/src/tabstack/types/extract_json_params.py +++ b/src/tabstack/types/extract_json_params.py @@ -4,7 +4,9 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ExtractJsonParams", "GeoTarget"] +from .shared_params.geotarget_geo_target import GeotargetGeoTarget + +__all__ = ["ExtractJsonParams"] class ExtractJsonParams(TypedDict, total=False): @@ -22,18 +24,8 @@ class ExtractJsonParams(TypedDict, total=False): JS-heavy sites (15-60s). """ - geo_target: GeoTarget + geo_target: GeotargetGeoTarget """Optional geotargeting parameters for proxy requests""" nocache: bool """Bypass cache and force fresh data retrieval""" - - -class GeoTarget(TypedDict, total=False): - """Optional geotargeting parameters for proxy requests""" - - country: str - """ - Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", - "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - """ diff --git a/src/tabstack/types/extract_markdown_params.py b/src/tabstack/types/extract_markdown_params.py index bd57499..c204227 100644 --- a/src/tabstack/types/extract_markdown_params.py +++ b/src/tabstack/types/extract_markdown_params.py @@ -4,7 +4,9 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ExtractMarkdownParams", "GeoTarget"] +from .shared_params.geotarget_geo_target import GeotargetGeoTarget + +__all__ = ["ExtractMarkdownParams"] class ExtractMarkdownParams(TypedDict, total=False): @@ -19,7 +21,7 @@ class ExtractMarkdownParams(TypedDict, total=False): JS-heavy sites (15-60s). """ - geo_target: GeoTarget + geo_target: GeotargetGeoTarget """Optional geotargeting parameters for proxy requests""" metadata: bool @@ -30,13 +32,3 @@ class ExtractMarkdownParams(TypedDict, total=False): nocache: bool """Bypass cache and force fresh data retrieval""" - - -class GeoTarget(TypedDict, total=False): - """Optional geotargeting parameters for proxy requests""" - - country: str - """ - Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", - "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - """ diff --git a/src/tabstack/types/generate_json_params.py b/src/tabstack/types/generate_json_params.py index 486e3ed..b1cc9b7 100644 --- a/src/tabstack/types/generate_json_params.py +++ b/src/tabstack/types/generate_json_params.py @@ -4,7 +4,9 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["GenerateJsonParams", "GeoTarget"] +from .shared_params.geotarget_geo_target import GeotargetGeoTarget + +__all__ = ["GenerateJsonParams"] class GenerateJsonParams(TypedDict, total=False): @@ -25,18 +27,8 @@ class GenerateJsonParams(TypedDict, total=False): JS-heavy sites (15-60s). """ - geo_target: GeoTarget + geo_target: GeotargetGeoTarget """Optional geotargeting parameters for proxy requests""" nocache: bool """Bypass cache and force fresh data retrieval""" - - -class GeoTarget(TypedDict, total=False): - """Optional geotargeting parameters for proxy requests""" - - country: str - """ - Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", - "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - """ diff --git a/src/tabstack/types/research_event.py b/src/tabstack/types/research_event.py index a41f2e9..67ad1dd 100644 --- a/src/tabstack/types/research_event.py +++ b/src/tabstack/types/research_event.py @@ -7,6 +7,7 @@ from .._utils import PropertyInfo from .._models import BaseModel +from .v1_research_question_assessment import V1ResearchQuestionAssessment __all__ = [ "ResearchEvent", @@ -20,7 +21,6 @@ "V1ResearchEventCompleteDataMetadata", "V1ResearchEventCompleteDataMetadataCitedPage", "V1ResearchEventCompleteDataMetadataGapEvaluation", - "V1ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessment", "V1ResearchEventCompleteDataMetadataJudgment", "V1ResearchEventCompleteDataMetadataMetrics", "V1ResearchEventCompleteDataMetadataMetricsPhases", @@ -33,7 +33,6 @@ "V1ResearchEventErrorDataError", "V1ResearchEventEvaluatingEnd", "V1ResearchEventEvaluatingEndData", - "V1ResearchEventEvaluatingEndDataQuestionAssessment", "V1ResearchEventEvaluatingStart", "V1ResearchEventEvaluatingStartData", "V1ResearchEventFollowingEnd", @@ -170,22 +169,6 @@ class V1ResearchEventCompleteDataMetadataCitedPage(BaseModel): """URL source tracking - where a URL came from""" -class V1ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessment(BaseModel): - """Assessment of a single research question""" - - findings: str - """What we learned (if answered/partial) or what's missing (if unanswered)""" - - question: str - """The research question being assessed""" - - status: Literal["answered", "partial", "unanswered"] - """ - Status: answered (clear info), partial (some info, gaps remain), unanswered (no - relevant info) - """ - - class V1ResearchEventCompleteDataMetadataGapEvaluation(BaseModel): """Gap evaluation results from research strategist""" @@ -195,9 +178,7 @@ class V1ResearchEventCompleteDataMetadataGapEvaluation(BaseModel): needed? """ - question_assessments: List[V1ResearchEventCompleteDataMetadataGapEvaluationQuestionAssessment] = FieldInfo( - alias="questionAssessments" - ) + question_assessments: List[V1ResearchQuestionAssessment] = FieldInfo(alias="questionAssessments") """Assessment of each research question's status and findings""" research_coverage: Literal["Light", "Moderate", "Solid", "Comprehensive"] = FieldInfo(alias="researchCoverage") @@ -456,22 +437,6 @@ class V1ResearchEventError(BaseModel): event: Literal["error"] -class V1ResearchEventEvaluatingEndDataQuestionAssessment(BaseModel): - """Assessment of a single research question""" - - findings: str - """What we learned (if answered/partial) or what's missing (if unanswered)""" - - question: str - """The research question being assessed""" - - status: Literal["answered", "partial", "unanswered"] - """ - Status: answered (clear info), partial (some info, gaps remain), unanswered (no - relevant info) - """ - - class V1ResearchEventEvaluatingEndData(BaseModel): coverage: Literal["Light", "Moderate", "Solid", "Comprehensive"] @@ -483,9 +448,7 @@ class V1ResearchEventEvaluatingEndData(BaseModel): next_queries: List[str] = FieldInfo(alias="nextQueries") - question_assessments: List[V1ResearchEventEvaluatingEndDataQuestionAssessment] = FieldInfo( - alias="questionAssessments" - ) + question_assessments: List[V1ResearchQuestionAssessment] = FieldInfo(alias="questionAssessments") should_continue: bool = FieldInfo(alias="shouldContinue") diff --git a/src/tabstack/types/shared/__init__.py b/src/tabstack/types/shared/__init__.py new file mode 100644 index 0000000..797ab9e --- /dev/null +++ b/src/tabstack/types/shared/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .geotarget_geo_target import GeotargetGeoTarget as GeotargetGeoTarget diff --git a/src/tabstack/types/shared/geotarget_geo_target.py b/src/tabstack/types/shared/geotarget_geo_target.py new file mode 100644 index 0000000..88c2cd2 --- /dev/null +++ b/src/tabstack/types/shared/geotarget_geo_target.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["GeotargetGeoTarget"] + + +class GeotargetGeoTarget(BaseModel): + country: Optional[str] = None + """ + Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", + "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + """ diff --git a/src/tabstack/types/shared_params/__init__.py b/src/tabstack/types/shared_params/__init__.py new file mode 100644 index 0000000..797ab9e --- /dev/null +++ b/src/tabstack/types/shared_params/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .geotarget_geo_target import GeotargetGeoTarget as GeotargetGeoTarget diff --git a/src/tabstack/types/shared_params/geotarget_geo_target.py b/src/tabstack/types/shared_params/geotarget_geo_target.py new file mode 100644 index 0000000..204298c --- /dev/null +++ b/src/tabstack/types/shared_params/geotarget_geo_target.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["GeotargetGeoTarget"] + + +class GeotargetGeoTarget(TypedDict, total=False): + country: str + """ + Country code using ISO 3166-1 alpha-2 standard (2 letters, e.g., "US", "GB", + "JP"). See: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + """ diff --git a/src/tabstack/types/v1_global_buffer.py b/src/tabstack/types/v1_global_buffer.py new file mode 100644 index 0000000..3f48d48 --- /dev/null +++ b/src/tabstack/types/v1_global_buffer.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["V1GlobalBuffer", "Buffer"] + + +class Buffer(BaseModel): + byte_length: float = FieldInfo(alias="byteLength") + + +class V1GlobalBuffer(BaseModel): + buffer: Buffer + + byte_length: float = FieldInfo(alias="byteLength") + + byte_offset: float = FieldInfo(alias="byteOffset") + + bytes_per_element: float = FieldInfo(alias="BYTES_PER_ELEMENT") + + length: float + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, float] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> float: ... + else: + __pydantic_extra__: Dict[str, float] diff --git a/src/tabstack/types/v1_research_question_assessment.py b/src/tabstack/types/v1_research_question_assessment.py new file mode 100644 index 0000000..22b7041 --- /dev/null +++ b/src/tabstack/types/v1_research_question_assessment.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["V1ResearchQuestionAssessment"] + + +class V1ResearchQuestionAssessment(BaseModel): + """Assessment of a single research question""" + + findings: str + """What we learned (if answered/partial) or what's missing (if unanswered)""" + + question: str + """The research question being assessed""" + + status: Literal["answered", "partial", "unanswered"] + """ + Status: answered (clear info), partial (some info, gaps remain), unanswered (no + relevant info) + """ diff --git a/tests/api_resources/test_extract.py b/tests/api_resources/test_extract.py index 262bac9..e6f463b 100644 --- a/tests/api_resources/test_extract.py +++ b/tests/api_resources/test_extract.py @@ -9,7 +9,10 @@ from tabstack import Tabstack, AsyncTabstack from tests.utils import assert_matches_type -from tabstack.types import ExtractJsonResponse, ExtractMarkdownResponse +from tabstack.types import ( + ExtractJsonResponse, + ExtractMarkdownResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") From 6b3047d96e6e001ea786235065bd29c25319203f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 15:35:45 +0000 Subject: [PATCH 9/9] release: 2.6.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- src/tabstack/_version.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 511dd51..2f8909f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.6.0" + ".": "2.6.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bfb6c6..9a90f1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 2.6.1 (2026-05-05) + +Full Changelog: [v2.6.0...v2.6.1](https://github.com/Mozilla-Ocho/tabstack-python/compare/v2.6.0...v2.6.1) + +### Features + +* **api:** add endpoint specfic timeouts ([15bcc6b](https://github.com/Mozilla-Ocho/tabstack-python/commit/15bcc6b29cf0e64fa5bf544f099471dac26d9cea)) +* **api:** manual updates ([e89cb7a](https://github.com/Mozilla-Ocho/tabstack-python/commit/e89cb7a58d7e139cb46d54f15fbb26515a54f205)) +* **automate:** emit task:trace_context SSE event with trace ID ([20d3ff3](https://github.com/Mozilla-Ocho/tabstack-python/commit/20d3ff3b4c5e8d7d287ef9807ecbc33c522bed37)) +* support setting headers via env ([4699481](https://github.com/Mozilla-Ocho/tabstack-python/commit/4699481aee8341952e2cae287bc0ff0fc629a292)) + + +### Bug Fixes + +* use correct field name format for multipart file arrays ([a10a1ed](https://github.com/Mozilla-Ocho/tabstack-python/commit/a10a1ed1298179229f69dc3c12b899f6242ed1b4)) + + +### Chores + +* **internal:** reformat pyproject.toml ([c909f52](https://github.com/Mozilla-Ocho/tabstack-python/commit/c909f52bf36e746213c2cb8b316e6eb1909a2e80)) + ## 2.6.0 (2026-04-24) Full Changelog: [v2.5.0...v2.6.0](https://github.com/Mozilla-Ocho/tabstack-python/compare/v2.5.0...v2.6.0) diff --git a/pyproject.toml b/pyproject.toml index 226974b..92a2355 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "tabstack" -version = "2.6.0" +version = "2.6.1" description = "The official Python library for the tabstack API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/tabstack/_version.py b/src/tabstack/_version.py index 5187085..f9930e7 100644 --- a/src/tabstack/_version.py +++ b/src/tabstack/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "tabstack" -__version__ = "2.6.0" # x-release-please-version +__version__ = "2.6.1" # x-release-please-version