diff --git a/.stats.yml b/.stats.yml index 1fd29914..05f0faf0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 41 +configured_endpoints: 36 diff --git a/README.md b/README.md index 592361c0..f9e3bbc3 100644 --- a/README.md +++ b/README.md @@ -137,12 +137,12 @@ client = Hyperspell( user_id="My User ID", ) -all_context_documents = [] +all_entities = [] # Automatically fetches more pages as needed. -for context_document in client.context_documents.list(): - # Do something with context_document here - all_context_documents.append(context_document) -print(all_context_documents) +for entity in client.entities.list(): + # Do something with entity here + all_entities.append(entity) +print(all_entities) ``` Or, asynchronously: @@ -157,11 +157,11 @@ client = AsyncHyperspell( async def main() -> None: - all_context_documents = [] + all_entities = [] # Iterate through items across all pages, issuing requests as needed. - async for context_document in client.context_documents.list(): - all_context_documents.append(context_document) - print(all_context_documents) + async for entity in client.entities.list(): + all_entities.append(entity) + print(all_entities) asyncio.run(main()) @@ -170,11 +170,11 @@ asyncio.run(main()) Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: ```python -first_page = await client.context_documents.list() +first_page = await client.entities.list() if first_page.has_next_page(): print(f"will fetch next page using these details: {first_page.next_page_info()}") next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.documents)}") + print(f"number of items we just fetched: {len(next_page.items)}") # Remove `await` for non-async usage. ``` @@ -182,11 +182,11 @@ if first_page.has_next_page(): Or just work directly with the returned data: ```python -first_page = await client.context_documents.list() +first_page = await client.entities.list() print(f"next page cursor: {first_page.next_cursor}") # => "next page cursor: ..." -for context_document in first_page.documents: - print(context_document.document_id) +for entity in first_page.items: + print(entity.id) # Remove `await` for non-async usage. ``` @@ -202,10 +202,11 @@ client = Hyperspell( user_id="My User ID", ) -config = client.context_documents.config.update( - structure={}, +query_result = client.memories.search( + query="What does Hyperspell do?", + options={"filter": {}}, ) -print(config.structure) +print(query_result.options) ``` ## File uploads diff --git a/api.md b/api.md index d8e50312..6a83b1d1 100644 --- a/api.md +++ b/api.md @@ -108,74 +108,39 @@ Methods: - client.integrations.web_crawler.index(\*\*params) -> WebCrawlerIndexResponse -# ContextDocuments +# Entities Types: ```python -from hyperspell.types import ( - ContextDocumentListResponse, - ContextDocumentGenerateResponse, - ContextDocumentGetResponse, -) -``` - -Methods: - -- client.context_documents.list(\*\*params) -> SyncContextDocumentsCursorPage[ContextDocumentListResponse] -- client.context_documents.generate(\*\*params) -> ContextDocumentGenerateResponse -- client.context_documents.get(document_id) -> ContextDocumentGetResponse - -## Trees - -Types: - -```python -from hyperspell.types.context_documents import ( - TreeGenerateResponse, - TreeGetResponse, - TreeGetLatestResponse, - TreeProgressResponse, -) +from hyperspell.types import EntityListResponse, EntityGetResponse, EntitySearchResponse ``` Methods: -- client.context_documents.trees.generate(\*\*params) -> TreeGenerateResponse -- client.context_documents.trees.get(tree_id) -> TreeGetResponse -- client.context_documents.trees.get_latest(\*\*params) -> TreeGetLatestResponse -- client.context_documents.trees.progress(tree_id) -> TreeProgressResponse +- client.entities.list(\*\*params) -> SyncEntityCursorPage[EntityListResponse] +- client.entities.get(entity_id) -> EntityGetResponse +- client.entities.search(\*\*params) -> EntitySearchResponse -## Digests +# Live Types: ```python -from hyperspell.types.context_documents import DigestListResponse, DigestGenerateResponse -``` - -Methods: - -- client.context_documents.digests.list(\*\*params) -> DigestListResponse -- client.context_documents.digests.generate(\*\*params) -> DigestGenerateResponse - -## Config - -Types: - -```python -from hyperspell.types.context_documents import ( - ConfigUpdateResponse, - ConfigGetResponse, - ConfigResetResponse, +from hyperspell.types import ( + LiveGetResourceResponse, + LiveListResourcesResponse, + LiveListSourcesResponse, + LiveSearchResponse, ) ``` Methods: -- client.context_documents.config.update(\*\*params) -> ConfigUpdateResponse -- client.context_documents.config.get() -> ConfigGetResponse -- client.context_documents.config.reset() -> ConfigResetResponse +- client.live.get_resource(resource_id, \*, source, \*\*params) -> LiveGetResourceResponse +- client.live.list_resources(source, \*\*params) -> SyncCursorPage[LiveListResourcesResponse] +- client.live.list_sources() -> LiveListSourcesResponse +- client.live.search(source, \*\*params) -> LiveSearchResponse # Memories diff --git a/scripts/mock b/scripts/mock index 342ec614..e71f87b5 100755 --- a/scripts/mock +++ b/scripts/mock @@ -11,7 +11,7 @@ elif [ -n "$STAINLESS_OPENAPI_SPEC_URL" ]; then URL="$STAINLESS_OPENAPI_SPEC_URL" else # Embedded OpenAPI spec (base64-encoded, gzipped) - EMBEDDED_SPEC="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" + EMBEDDED_SPEC="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" SPEC_TMPFILE="$(mktemp)" trap 'rm -f "$SPEC_TMPFILE"' EXIT echo "$EMBEDDED_SPEC" | base64 --decode | gunzip > "$SPEC_TMPFILE" diff --git a/src/hyperspell/_client.py b/src/hyperspell/_client.py index d05ded7a..8303553f 100644 --- a/src/hyperspell/_client.py +++ b/src/hyperspell/_client.py @@ -37,26 +37,28 @@ if TYPE_CHECKING: from .resources import ( auth, + live, vaults, actions, folders, + entities, evaluate, memories, sessions, connections, integrations, - context_documents, ) from .resources.auth import AuthResource, AsyncAuthResource + from .resources.live import LiveResource, AsyncLiveResource from .resources.vaults import VaultsResource, AsyncVaultsResource from .resources.actions import ActionsResource, AsyncActionsResource from .resources.folders import FoldersResource, AsyncFoldersResource + from .resources.entities import EntitiesResource, AsyncEntitiesResource from .resources.evaluate import EvaluateResource, AsyncEvaluateResource from .resources.memories import MemoriesResource, AsyncMemoriesResource from .resources.sessions import SessionsResource, AsyncSessionsResource from .resources.connections import ConnectionsResource, AsyncConnectionsResource from .resources.integrations.integrations import IntegrationsResource, AsyncIntegrationsResource - from .resources.context_documents.context_documents import ContextDocumentsResource, AsyncContextDocumentsResource __all__ = [ "Timeout", @@ -157,10 +159,16 @@ def integrations(self) -> IntegrationsResource: return IntegrationsResource(self) @cached_property - def context_documents(self) -> ContextDocumentsResource: - from .resources.context_documents import ContextDocumentsResource + def entities(self) -> EntitiesResource: + from .resources.entities import EntitiesResource - return ContextDocumentsResource(self) + return EntitiesResource(self) + + @cached_property + def live(self) -> LiveResource: + from .resources.live import LiveResource + + return LiveResource(self) @cached_property def memories(self) -> MemoriesResource: @@ -401,10 +409,16 @@ def integrations(self) -> AsyncIntegrationsResource: return AsyncIntegrationsResource(self) @cached_property - def context_documents(self) -> AsyncContextDocumentsResource: - from .resources.context_documents import AsyncContextDocumentsResource + def entities(self) -> AsyncEntitiesResource: + from .resources.entities import AsyncEntitiesResource - return AsyncContextDocumentsResource(self) + return AsyncEntitiesResource(self) + + @cached_property + def live(self) -> AsyncLiveResource: + from .resources.live import AsyncLiveResource + + return AsyncLiveResource(self) @cached_property def memories(self) -> AsyncMemoriesResource: @@ -583,10 +597,16 @@ def integrations(self) -> integrations.IntegrationsResourceWithRawResponse: return IntegrationsResourceWithRawResponse(self._client.integrations) @cached_property - def context_documents(self) -> context_documents.ContextDocumentsResourceWithRawResponse: - from .resources.context_documents import ContextDocumentsResourceWithRawResponse + def entities(self) -> entities.EntitiesResourceWithRawResponse: + from .resources.entities import EntitiesResourceWithRawResponse + + return EntitiesResourceWithRawResponse(self._client.entities) + + @cached_property + def live(self) -> live.LiveResourceWithRawResponse: + from .resources.live import LiveResourceWithRawResponse - return ContextDocumentsResourceWithRawResponse(self._client.context_documents) + return LiveResourceWithRawResponse(self._client.live) @cached_property def memories(self) -> memories.MemoriesResourceWithRawResponse: @@ -650,10 +670,16 @@ def integrations(self) -> integrations.AsyncIntegrationsResourceWithRawResponse: return AsyncIntegrationsResourceWithRawResponse(self._client.integrations) @cached_property - def context_documents(self) -> context_documents.AsyncContextDocumentsResourceWithRawResponse: - from .resources.context_documents import AsyncContextDocumentsResourceWithRawResponse + def entities(self) -> entities.AsyncEntitiesResourceWithRawResponse: + from .resources.entities import AsyncEntitiesResourceWithRawResponse - return AsyncContextDocumentsResourceWithRawResponse(self._client.context_documents) + return AsyncEntitiesResourceWithRawResponse(self._client.entities) + + @cached_property + def live(self) -> live.AsyncLiveResourceWithRawResponse: + from .resources.live import AsyncLiveResourceWithRawResponse + + return AsyncLiveResourceWithRawResponse(self._client.live) @cached_property def memories(self) -> memories.AsyncMemoriesResourceWithRawResponse: @@ -717,10 +743,16 @@ def integrations(self) -> integrations.IntegrationsResourceWithStreamingResponse return IntegrationsResourceWithStreamingResponse(self._client.integrations) @cached_property - def context_documents(self) -> context_documents.ContextDocumentsResourceWithStreamingResponse: - from .resources.context_documents import ContextDocumentsResourceWithStreamingResponse + def entities(self) -> entities.EntitiesResourceWithStreamingResponse: + from .resources.entities import EntitiesResourceWithStreamingResponse - return ContextDocumentsResourceWithStreamingResponse(self._client.context_documents) + return EntitiesResourceWithStreamingResponse(self._client.entities) + + @cached_property + def live(self) -> live.LiveResourceWithStreamingResponse: + from .resources.live import LiveResourceWithStreamingResponse + + return LiveResourceWithStreamingResponse(self._client.live) @cached_property def memories(self) -> memories.MemoriesResourceWithStreamingResponse: @@ -784,10 +816,16 @@ def integrations(self) -> integrations.AsyncIntegrationsResourceWithStreamingRes return AsyncIntegrationsResourceWithStreamingResponse(self._client.integrations) @cached_property - def context_documents(self) -> context_documents.AsyncContextDocumentsResourceWithStreamingResponse: - from .resources.context_documents import AsyncContextDocumentsResourceWithStreamingResponse + def entities(self) -> entities.AsyncEntitiesResourceWithStreamingResponse: + from .resources.entities import AsyncEntitiesResourceWithStreamingResponse + + return AsyncEntitiesResourceWithStreamingResponse(self._client.entities) + + @cached_property + def live(self) -> live.AsyncLiveResourceWithStreamingResponse: + from .resources.live import AsyncLiveResourceWithStreamingResponse - return AsyncContextDocumentsResourceWithStreamingResponse(self._client.context_documents) + return AsyncLiveResourceWithStreamingResponse(self._client.live) @cached_property def memories(self) -> memories.AsyncMemoriesResourceWithStreamingResponse: diff --git a/src/hyperspell/pagination.py b/src/hyperspell/pagination.py index 5da3aa10..919340cb 100644 --- a/src/hyperspell/pagination.py +++ b/src/hyperspell/pagination.py @@ -5,7 +5,7 @@ from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage -__all__ = ["SyncCursorPage", "AsyncCursorPage", "SyncContextDocumentsCursorPage", "AsyncContextDocumentsCursorPage"] +__all__ = ["SyncCursorPage", "AsyncCursorPage", "SyncEntityCursorPage", "AsyncEntityCursorPage"] _T = TypeVar("_T") @@ -50,16 +50,16 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"cursor": next_cursor}) -class SyncContextDocumentsCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - documents: List[_T] +class SyncEntityCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] next_cursor: Optional[str] = None @override def _get_page_items(self) -> List[_T]: - documents = self.documents - if not documents: + items = self.items + if not items: return [] - return documents + return items @override def next_page_info(self) -> Optional[PageInfo]: @@ -70,16 +70,16 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"cursor": next_cursor}) -class AsyncContextDocumentsCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - documents: List[_T] +class AsyncEntityCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + items: List[_T] next_cursor: Optional[str] = None @override def _get_page_items(self) -> List[_T]: - documents = self.documents - if not documents: + items = self.items + if not items: return [] - return documents + return items @override def next_page_info(self) -> Optional[PageInfo]: diff --git a/src/hyperspell/resources/__init__.py b/src/hyperspell/resources/__init__.py index 2553e993..6ece35b7 100644 --- a/src/hyperspell/resources/__init__.py +++ b/src/hyperspell/resources/__init__.py @@ -8,6 +8,14 @@ AuthResourceWithStreamingResponse, AsyncAuthResourceWithStreamingResponse, ) +from .live import ( + LiveResource, + AsyncLiveResource, + LiveResourceWithRawResponse, + AsyncLiveResourceWithRawResponse, + LiveResourceWithStreamingResponse, + AsyncLiveResourceWithStreamingResponse, +) from .vaults import ( VaultsResource, AsyncVaultsResource, @@ -32,6 +40,14 @@ FoldersResourceWithStreamingResponse, AsyncFoldersResourceWithStreamingResponse, ) +from .entities import ( + EntitiesResource, + AsyncEntitiesResource, + EntitiesResourceWithRawResponse, + AsyncEntitiesResourceWithRawResponse, + EntitiesResourceWithStreamingResponse, + AsyncEntitiesResourceWithStreamingResponse, +) from .evaluate import ( EvaluateResource, AsyncEvaluateResource, @@ -72,14 +88,6 @@ IntegrationsResourceWithStreamingResponse, AsyncIntegrationsResourceWithStreamingResponse, ) -from .context_documents import ( - ContextDocumentsResource, - AsyncContextDocumentsResource, - ContextDocumentsResourceWithRawResponse, - AsyncContextDocumentsResourceWithRawResponse, - ContextDocumentsResourceWithStreamingResponse, - AsyncContextDocumentsResourceWithStreamingResponse, -) __all__ = [ "ConnectionsResource", @@ -100,12 +108,18 @@ "AsyncIntegrationsResourceWithRawResponse", "IntegrationsResourceWithStreamingResponse", "AsyncIntegrationsResourceWithStreamingResponse", - "ContextDocumentsResource", - "AsyncContextDocumentsResource", - "ContextDocumentsResourceWithRawResponse", - "AsyncContextDocumentsResourceWithRawResponse", - "ContextDocumentsResourceWithStreamingResponse", - "AsyncContextDocumentsResourceWithStreamingResponse", + "EntitiesResource", + "AsyncEntitiesResource", + "EntitiesResourceWithRawResponse", + "AsyncEntitiesResourceWithRawResponse", + "EntitiesResourceWithStreamingResponse", + "AsyncEntitiesResourceWithStreamingResponse", + "LiveResource", + "AsyncLiveResource", + "LiveResourceWithRawResponse", + "AsyncLiveResourceWithRawResponse", + "LiveResourceWithStreamingResponse", + "AsyncLiveResourceWithStreamingResponse", "MemoriesResource", "AsyncMemoriesResource", "MemoriesResourceWithRawResponse", diff --git a/src/hyperspell/resources/context_documents/__init__.py b/src/hyperspell/resources/context_documents/__init__.py deleted file mode 100644 index 34942511..00000000 --- a/src/hyperspell/resources/context_documents/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .trees import ( - TreesResource, - AsyncTreesResource, - TreesResourceWithRawResponse, - AsyncTreesResourceWithRawResponse, - TreesResourceWithStreamingResponse, - AsyncTreesResourceWithStreamingResponse, -) -from .config import ( - ConfigResource, - AsyncConfigResource, - ConfigResourceWithRawResponse, - AsyncConfigResourceWithRawResponse, - ConfigResourceWithStreamingResponse, - AsyncConfigResourceWithStreamingResponse, -) -from .digests import ( - DigestsResource, - AsyncDigestsResource, - DigestsResourceWithRawResponse, - AsyncDigestsResourceWithRawResponse, - DigestsResourceWithStreamingResponse, - AsyncDigestsResourceWithStreamingResponse, -) -from .context_documents import ( - ContextDocumentsResource, - AsyncContextDocumentsResource, - ContextDocumentsResourceWithRawResponse, - AsyncContextDocumentsResourceWithRawResponse, - ContextDocumentsResourceWithStreamingResponse, - AsyncContextDocumentsResourceWithStreamingResponse, -) - -__all__ = [ - "TreesResource", - "AsyncTreesResource", - "TreesResourceWithRawResponse", - "AsyncTreesResourceWithRawResponse", - "TreesResourceWithStreamingResponse", - "AsyncTreesResourceWithStreamingResponse", - "DigestsResource", - "AsyncDigestsResource", - "DigestsResourceWithRawResponse", - "AsyncDigestsResourceWithRawResponse", - "DigestsResourceWithStreamingResponse", - "AsyncDigestsResourceWithStreamingResponse", - "ConfigResource", - "AsyncConfigResource", - "ConfigResourceWithRawResponse", - "AsyncConfigResourceWithRawResponse", - "ConfigResourceWithStreamingResponse", - "AsyncConfigResourceWithStreamingResponse", - "ContextDocumentsResource", - "AsyncContextDocumentsResource", - "ContextDocumentsResourceWithRawResponse", - "AsyncContextDocumentsResourceWithRawResponse", - "ContextDocumentsResourceWithStreamingResponse", - "AsyncContextDocumentsResourceWithStreamingResponse", -] diff --git a/src/hyperspell/resources/context_documents/config.py b/src/hyperspell/resources/context_documents/config.py deleted file mode 100644 index 6078d950..00000000 --- a/src/hyperspell/resources/context_documents/config.py +++ /dev/null @@ -1,323 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Optional - -import httpx - -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.context_documents import config_update_params -from ...types.context_documents.config_get_response import ConfigGetResponse -from ...types.context_documents.config_reset_response import ConfigResetResponse -from ...types.context_documents.config_update_response import ConfigUpdateResponse - -__all__ = ["ConfigResource", "AsyncConfigResource"] - - -class ConfigResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ConfigResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return ConfigResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ConfigResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return ConfigResourceWithStreamingResponse(self) - - def update( - self, - *, - company_prompts: Optional[Dict[str, str]] | Omit = omit, - detection_prompt: Optional[str] | Omit = omit, - domain: Optional[str] | Omit = omit, - personal_prompt: Optional[str] | Omit = omit, - source_weights: Optional[Dict[str, str]] | Omit = omit, - structure: Optional[config_update_params.Structure] | Omit = omit, - workstream_prompts: Optional[Dict[str, str]] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigUpdateResponse: - """Update the supplied generation settings. - - Changes apply to the next generation. - - This endpoint does not start a generation - or modify existing context documents. - - Args: - structure: Per-tier document definitions for custom generation. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._patch( - "/context-documents/config", - body=maybe_transform( - { - "company_prompts": company_prompts, - "detection_prompt": detection_prompt, - "domain": domain, - "personal_prompt": personal_prompt, - "source_weights": source_weights, - "structure": structure, - "workstream_prompts": workstream_prompts, - }, - config_update_params.ConfigUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigUpdateResponse, - ) - - def get( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigGetResponse: - """Read the customer-editable generation config for the authenticated app.""" - return self._get( - "/context-documents/config", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigGetResponse, - ) - - def reset( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigResetResponse: - """ - Reset customer-editable generation settings to their defaults. - - Existing context documents remain unchanged. `detected_domain` is retained and - used for future generations unless a new domain override is set. - """ - return self._post( - "/context-documents/config/reset", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigResetResponse, - ) - - -class AsyncConfigResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncConfigResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncConfigResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return AsyncConfigResourceWithStreamingResponse(self) - - async def update( - self, - *, - company_prompts: Optional[Dict[str, str]] | Omit = omit, - detection_prompt: Optional[str] | Omit = omit, - domain: Optional[str] | Omit = omit, - personal_prompt: Optional[str] | Omit = omit, - source_weights: Optional[Dict[str, str]] | Omit = omit, - structure: Optional[config_update_params.Structure] | Omit = omit, - workstream_prompts: Optional[Dict[str, str]] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigUpdateResponse: - """Update the supplied generation settings. - - Changes apply to the next generation. - - This endpoint does not start a generation - or modify existing context documents. - - Args: - structure: Per-tier document definitions for custom generation. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._patch( - "/context-documents/config", - body=await async_maybe_transform( - { - "company_prompts": company_prompts, - "detection_prompt": detection_prompt, - "domain": domain, - "personal_prompt": personal_prompt, - "source_weights": source_weights, - "structure": structure, - "workstream_prompts": workstream_prompts, - }, - config_update_params.ConfigUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigUpdateResponse, - ) - - async def get( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigGetResponse: - """Read the customer-editable generation config for the authenticated app.""" - return await self._get( - "/context-documents/config", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigGetResponse, - ) - - async def reset( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConfigResetResponse: - """ - Reset customer-editable generation settings to their defaults. - - Existing context documents remain unchanged. `detected_domain` is retained and - used for future generations unless a new domain override is set. - """ - return await self._post( - "/context-documents/config/reset", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConfigResetResponse, - ) - - -class ConfigResourceWithRawResponse: - def __init__(self, config: ConfigResource) -> None: - self._config = config - - self.update = to_raw_response_wrapper( - config.update, - ) - self.get = to_raw_response_wrapper( - config.get, - ) - self.reset = to_raw_response_wrapper( - config.reset, - ) - - -class AsyncConfigResourceWithRawResponse: - def __init__(self, config: AsyncConfigResource) -> None: - self._config = config - - self.update = async_to_raw_response_wrapper( - config.update, - ) - self.get = async_to_raw_response_wrapper( - config.get, - ) - self.reset = async_to_raw_response_wrapper( - config.reset, - ) - - -class ConfigResourceWithStreamingResponse: - def __init__(self, config: ConfigResource) -> None: - self._config = config - - self.update = to_streamed_response_wrapper( - config.update, - ) - self.get = to_streamed_response_wrapper( - config.get, - ) - self.reset = to_streamed_response_wrapper( - config.reset, - ) - - -class AsyncConfigResourceWithStreamingResponse: - def __init__(self, config: AsyncConfigResource) -> None: - self._config = config - - self.update = async_to_streamed_response_wrapper( - config.update, - ) - self.get = async_to_streamed_response_wrapper( - config.get, - ) - self.reset = async_to_streamed_response_wrapper( - config.reset, - ) diff --git a/src/hyperspell/resources/context_documents/context_documents.py b/src/hyperspell/resources/context_documents/context_documents.py deleted file mode 100644 index ef19f965..00000000 --- a/src/hyperspell/resources/context_documents/context_documents.py +++ /dev/null @@ -1,491 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal - -import httpx - -from .trees import ( - TreesResource, - AsyncTreesResource, - TreesResourceWithRawResponse, - AsyncTreesResourceWithRawResponse, - TreesResourceWithStreamingResponse, - AsyncTreesResourceWithStreamingResponse, -) -from .config import ( - ConfigResource, - AsyncConfigResource, - ConfigResourceWithRawResponse, - AsyncConfigResourceWithRawResponse, - ConfigResourceWithStreamingResponse, - AsyncConfigResourceWithStreamingResponse, -) -from ...types import context_document_list_params, context_document_generate_params -from .digests import ( - DigestsResource, - AsyncDigestsResource, - DigestsResourceWithRawResponse, - AsyncDigestsResourceWithRawResponse, - DigestsResourceWithStreamingResponse, - AsyncDigestsResourceWithStreamingResponse, -) -from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import path_template, maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncContextDocumentsCursorPage, AsyncContextDocumentsCursorPage -from ..._base_client import AsyncPaginator, make_request_options -from ...types.context_document_get_response import ContextDocumentGetResponse -from ...types.context_document_list_response import ContextDocumentListResponse -from ...types.context_document_generate_response import ContextDocumentGenerateResponse - -__all__ = ["ContextDocumentsResource", "AsyncContextDocumentsResource"] - - -class ContextDocumentsResource(SyncAPIResource): - @cached_property - def trees(self) -> TreesResource: - return TreesResource(self._client) - - @cached_property - def digests(self) -> DigestsResource: - return DigestsResource(self._client) - - @cached_property - def config(self) -> ConfigResource: - return ConfigResource(self._client) - - @cached_property - def with_raw_response(self) -> ContextDocumentsResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return ContextDocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ContextDocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return ContextDocumentsResourceWithStreamingResponse(self) - - def list( - self, - *, - cursor: Optional[str] | Omit = omit, - limit: int | Omit = omit, - status: Optional[Literal["processing", "completed", "failed"]] | 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, - ) -> SyncContextDocumentsCursorPage[ContextDocumentListResponse]: - """ - List context documents for the authenticated app, most recent first. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/context-documents", - page=SyncContextDocumentsCursorPage[ContextDocumentListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "cursor": cursor, - "limit": limit, - "status": status, - }, - context_document_list_params.ContextDocumentListParams, - ), - ), - model=ContextDocumentListResponse, - ) - - def generate( - self, - *, - model: str | Omit = omit, - prompt: Optional[str] | Omit = omit, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - user_id: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ContextDocumentGenerateResponse: - """ - Generate an LLM-synthesized context document from the app's synced data. - - Generation runs asynchronously. The endpoint returns immediately with status - `PROCESSING`; synthesis time depends on the amount of source data. - - Args: - model: Model used for final synthesis. - - prompt: Custom prompt template. Replaces the standard summary prompt. - - sources: Integration sources to include (e.g., ['gmail', 'slack']). Defaults to all - connected integrations. - - user_id: Scope generation to a specific user's data. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/context-documents/generate", - body=maybe_transform( - { - "model": model, - "prompt": prompt, - "sources": sources, - "user_id": user_id, - }, - context_document_generate_params.ContextDocumentGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ContextDocumentGenerateResponse, - ) - - def get( - self, - document_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ContextDocumentGetResponse: - """ - Get a specific context document by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not document_id: - raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}") - return self._get( - path_template("/context-documents/{document_id}", document_id=document_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ContextDocumentGetResponse, - ) - - -class AsyncContextDocumentsResource(AsyncAPIResource): - @cached_property - def trees(self) -> AsyncTreesResource: - return AsyncTreesResource(self._client) - - @cached_property - def digests(self) -> AsyncDigestsResource: - return AsyncDigestsResource(self._client) - - @cached_property - def config(self) -> AsyncConfigResource: - return AsyncConfigResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncContextDocumentsResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncContextDocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncContextDocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return AsyncContextDocumentsResourceWithStreamingResponse(self) - - def list( - self, - *, - cursor: Optional[str] | Omit = omit, - limit: int | Omit = omit, - status: Optional[Literal["processing", "completed", "failed"]] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ContextDocumentListResponse, AsyncContextDocumentsCursorPage[ContextDocumentListResponse]]: - """ - List context documents for the authenticated app, most recent first. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/context-documents", - page=AsyncContextDocumentsCursorPage[ContextDocumentListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "cursor": cursor, - "limit": limit, - "status": status, - }, - context_document_list_params.ContextDocumentListParams, - ), - ), - model=ContextDocumentListResponse, - ) - - async def generate( - self, - *, - model: str | Omit = omit, - prompt: Optional[str] | Omit = omit, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - user_id: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ContextDocumentGenerateResponse: - """ - Generate an LLM-synthesized context document from the app's synced data. - - Generation runs asynchronously. The endpoint returns immediately with status - `PROCESSING`; synthesis time depends on the amount of source data. - - Args: - model: Model used for final synthesis. - - prompt: Custom prompt template. Replaces the standard summary prompt. - - sources: Integration sources to include (e.g., ['gmail', 'slack']). Defaults to all - connected integrations. - - user_id: Scope generation to a specific user's data. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/context-documents/generate", - body=await async_maybe_transform( - { - "model": model, - "prompt": prompt, - "sources": sources, - "user_id": user_id, - }, - context_document_generate_params.ContextDocumentGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ContextDocumentGenerateResponse, - ) - - async def get( - self, - document_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ContextDocumentGetResponse: - """ - Get a specific context document by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not document_id: - raise ValueError(f"Expected a non-empty value for `document_id` but received {document_id!r}") - return await self._get( - path_template("/context-documents/{document_id}", document_id=document_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ContextDocumentGetResponse, - ) - - -class ContextDocumentsResourceWithRawResponse: - def __init__(self, context_documents: ContextDocumentsResource) -> None: - self._context_documents = context_documents - - self.list = to_raw_response_wrapper( - context_documents.list, - ) - self.generate = to_raw_response_wrapper( - context_documents.generate, - ) - self.get = to_raw_response_wrapper( - context_documents.get, - ) - - @cached_property - def trees(self) -> TreesResourceWithRawResponse: - return TreesResourceWithRawResponse(self._context_documents.trees) - - @cached_property - def digests(self) -> DigestsResourceWithRawResponse: - return DigestsResourceWithRawResponse(self._context_documents.digests) - - @cached_property - def config(self) -> ConfigResourceWithRawResponse: - return ConfigResourceWithRawResponse(self._context_documents.config) - - -class AsyncContextDocumentsResourceWithRawResponse: - def __init__(self, context_documents: AsyncContextDocumentsResource) -> None: - self._context_documents = context_documents - - self.list = async_to_raw_response_wrapper( - context_documents.list, - ) - self.generate = async_to_raw_response_wrapper( - context_documents.generate, - ) - self.get = async_to_raw_response_wrapper( - context_documents.get, - ) - - @cached_property - def trees(self) -> AsyncTreesResourceWithRawResponse: - return AsyncTreesResourceWithRawResponse(self._context_documents.trees) - - @cached_property - def digests(self) -> AsyncDigestsResourceWithRawResponse: - return AsyncDigestsResourceWithRawResponse(self._context_documents.digests) - - @cached_property - def config(self) -> AsyncConfigResourceWithRawResponse: - return AsyncConfigResourceWithRawResponse(self._context_documents.config) - - -class ContextDocumentsResourceWithStreamingResponse: - def __init__(self, context_documents: ContextDocumentsResource) -> None: - self._context_documents = context_documents - - self.list = to_streamed_response_wrapper( - context_documents.list, - ) - self.generate = to_streamed_response_wrapper( - context_documents.generate, - ) - self.get = to_streamed_response_wrapper( - context_documents.get, - ) - - @cached_property - def trees(self) -> TreesResourceWithStreamingResponse: - return TreesResourceWithStreamingResponse(self._context_documents.trees) - - @cached_property - def digests(self) -> DigestsResourceWithStreamingResponse: - return DigestsResourceWithStreamingResponse(self._context_documents.digests) - - @cached_property - def config(self) -> ConfigResourceWithStreamingResponse: - return ConfigResourceWithStreamingResponse(self._context_documents.config) - - -class AsyncContextDocumentsResourceWithStreamingResponse: - def __init__(self, context_documents: AsyncContextDocumentsResource) -> None: - self._context_documents = context_documents - - self.list = async_to_streamed_response_wrapper( - context_documents.list, - ) - self.generate = async_to_streamed_response_wrapper( - context_documents.generate, - ) - self.get = async_to_streamed_response_wrapper( - context_documents.get, - ) - - @cached_property - def trees(self) -> AsyncTreesResourceWithStreamingResponse: - return AsyncTreesResourceWithStreamingResponse(self._context_documents.trees) - - @cached_property - def digests(self) -> AsyncDigestsResourceWithStreamingResponse: - return AsyncDigestsResourceWithStreamingResponse(self._context_documents.digests) - - @cached_property - def config(self) -> AsyncConfigResourceWithStreamingResponse: - return AsyncConfigResourceWithStreamingResponse(self._context_documents.config) diff --git a/src/hyperspell/resources/context_documents/digests.py b/src/hyperspell/resources/context_documents/digests.py deleted file mode 100644 index a3a983aa..00000000 --- a/src/hyperspell/resources/context_documents/digests.py +++ /dev/null @@ -1,319 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from datetime import datetime - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import 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.context_documents import digest_list_params, digest_generate_params -from ...types.context_documents.digest_list_response import DigestListResponse -from ...types.context_documents.digest_generate_response import DigestGenerateResponse - -__all__ = ["DigestsResource", "AsyncDigestsResource"] - - -class DigestsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> DigestsResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return DigestsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DigestsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return DigestsResourceWithStreamingResponse(self) - - def list( - self, - *, - limit: int | Omit = omit, - period: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DigestListResponse: - """ - List recent digest summaries, newest first. - - Filter by cadence with `period=daily` or `period=weekly`. Fetch full content - with `GET /context-documents/tree/by-id/{tree_id}`. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/context-documents/digest/list", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "period": period, - }, - digest_list_params.DigestListParams, - ), - ), - cast_to=DigestListResponse, - ) - - def generate( - self, - *, - period: str | Omit = omit, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - window_end: Union[str, datetime, None] | Omit = omit, - window_start: Union[str, datetime, None] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DigestGenerateResponse: - """ - Generate a date-windowed "what the company did today" digest. - - Returns immediately with a tree ID. Poll - `GET /context-documents/tree/{tree_id}/progress` for completion or fetch the - result with `GET /context-documents/tree/by-id/{tree_id}`. - - Args: - period: Digest cadence: 'daily' or 'weekly'. Sets the default window when none is given. - - sources: Integration sources to include (e.g., ['slack', 'github']). Defaults to all. - - window_end: Exclusive upper bound of the digest window. Defaults to now. - - window_start: Inclusive lower bound of the digest window. Defaults to midnight UTC today - (paired with window_end=now) when omitted. Both bounds must be supplied - together. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/context-documents/digest", - body=maybe_transform( - { - "period": period, - "sources": sources, - "window_end": window_end, - "window_start": window_start, - }, - digest_generate_params.DigestGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DigestGenerateResponse, - ) - - -class AsyncDigestsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncDigestsResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncDigestsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDigestsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return AsyncDigestsResourceWithStreamingResponse(self) - - async def list( - self, - *, - limit: int | Omit = omit, - period: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DigestListResponse: - """ - List recent digest summaries, newest first. - - Filter by cadence with `period=daily` or `period=weekly`. Fetch full content - with `GET /context-documents/tree/by-id/{tree_id}`. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/context-documents/digest/list", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "limit": limit, - "period": period, - }, - digest_list_params.DigestListParams, - ), - ), - cast_to=DigestListResponse, - ) - - async def generate( - self, - *, - period: str | Omit = omit, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - window_end: Union[str, datetime, None] | Omit = omit, - window_start: Union[str, datetime, None] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DigestGenerateResponse: - """ - Generate a date-windowed "what the company did today" digest. - - Returns immediately with a tree ID. Poll - `GET /context-documents/tree/{tree_id}/progress` for completion or fetch the - result with `GET /context-documents/tree/by-id/{tree_id}`. - - Args: - period: Digest cadence: 'daily' or 'weekly'. Sets the default window when none is given. - - sources: Integration sources to include (e.g., ['slack', 'github']). Defaults to all. - - window_end: Exclusive upper bound of the digest window. Defaults to now. - - window_start: Inclusive lower bound of the digest window. Defaults to midnight UTC today - (paired with window_end=now) when omitted. Both bounds must be supplied - together. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/context-documents/digest", - body=await async_maybe_transform( - { - "period": period, - "sources": sources, - "window_end": window_end, - "window_start": window_start, - }, - digest_generate_params.DigestGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DigestGenerateResponse, - ) - - -class DigestsResourceWithRawResponse: - def __init__(self, digests: DigestsResource) -> None: - self._digests = digests - - self.list = to_raw_response_wrapper( - digests.list, - ) - self.generate = to_raw_response_wrapper( - digests.generate, - ) - - -class AsyncDigestsResourceWithRawResponse: - def __init__(self, digests: AsyncDigestsResource) -> None: - self._digests = digests - - self.list = async_to_raw_response_wrapper( - digests.list, - ) - self.generate = async_to_raw_response_wrapper( - digests.generate, - ) - - -class DigestsResourceWithStreamingResponse: - def __init__(self, digests: DigestsResource) -> None: - self._digests = digests - - self.list = to_streamed_response_wrapper( - digests.list, - ) - self.generate = to_streamed_response_wrapper( - digests.generate, - ) - - -class AsyncDigestsResourceWithStreamingResponse: - def __init__(self, digests: AsyncDigestsResource) -> None: - self._digests = digests - - self.list = async_to_streamed_response_wrapper( - digests.list, - ) - self.generate = async_to_streamed_response_wrapper( - digests.generate, - ) diff --git a/src/hyperspell/resources/context_documents/trees.py b/src/hyperspell/resources/context_documents/trees.py deleted file mode 100644 index 3ae75cf3..00000000 --- a/src/hyperspell/resources/context_documents/trees.py +++ /dev/null @@ -1,470 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import path_template, maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.context_documents import tree_generate_params, tree_get_latest_params -from ...types.context_documents.tree_get_response import TreeGetResponse -from ...types.context_documents.tree_generate_response import TreeGenerateResponse -from ...types.context_documents.tree_progress_response import TreeProgressResponse -from ...types.context_documents.tree_get_latest_response import TreeGetLatestResponse - -__all__ = ["TreesResource", "AsyncTreesResource"] - - -class TreesResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> TreesResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return TreesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> TreesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return TreesResourceWithStreamingResponse(self) - - def generate( - self, - *, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - user_id: Optional[str] | Omit = omit, - workstream_name: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGenerateResponse: - """ - Generate a three-tier context document tree for local push delivery. - - Creates company, workstream, and personal context documents from the app's - synced data. Returns immediately with a tree ID; use - `GET /context-documents/tree/latest` to retrieve the result. - - Args: - sources: Integration sources to include (e.g., ['gmail', 'slack']). Defaults to all. - - user_id: User ID for personal tier scoping. When set, personal/context.md is generated - from this user's data only. Company and workstream tiers still use all data. - - workstream_name: Generate docs for this workstream only (skip auto-detection). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/context-documents/tree", - body=maybe_transform( - { - "sources": sources, - "user_id": user_id, - "workstream_name": workstream_name, - }, - tree_generate_params.TreeGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeGenerateResponse, - ) - - def get( - self, - tree_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGetResponse: - """ - Fetch a specific tree by its tree ID instead of selecting the latest one. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not tree_id: - raise ValueError(f"Expected a non-empty value for `tree_id` but received {tree_id!r}") - return self._get( - path_template("/context-documents/tree/by-id/{tree_id}", tree_id=tree_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeGetResponse, - ) - - def get_latest( - self, - *, - status: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGetLatestResponse: - """ - Get the most recent context document tree for the authenticated app. - - By default, the endpoint returns the latest ready tree. Readiness depends on - whether the app has `require_review` enabled: - - - `require_review=False` (default): return the latest completed tree. - - `require_review=True`: return the latest published tree. - - `status` filters to a specific status (case-insensitive). When no ready tree - exists yet, the endpoint returns the newest available generation state. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/context-documents/tree/latest", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"status": status}, tree_get_latest_params.TreeGetLatestParams), - ), - cast_to=TreeGetLatestResponse, - ) - - def progress( - self, - tree_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeProgressResponse: - """ - Return the generation progress for a single tree. - - Active generations include phase and counter data. Completed generations, and - generations without detailed progress data, return status only. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not tree_id: - raise ValueError(f"Expected a non-empty value for `tree_id` but received {tree_id!r}") - return self._get( - path_template("/context-documents/tree/{tree_id}/progress", tree_id=tree_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeProgressResponse, - ) - - -class AsyncTreesResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncTreesResourceWithRawResponse: - """ - 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncTreesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncTreesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response - """ - return AsyncTreesResourceWithStreamingResponse(self) - - async def generate( - self, - *, - sources: Optional[SequenceNotStr[str]] | Omit = omit, - user_id: Optional[str] | Omit = omit, - workstream_name: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGenerateResponse: - """ - Generate a three-tier context document tree for local push delivery. - - Creates company, workstream, and personal context documents from the app's - synced data. Returns immediately with a tree ID; use - `GET /context-documents/tree/latest` to retrieve the result. - - Args: - sources: Integration sources to include (e.g., ['gmail', 'slack']). Defaults to all. - - user_id: User ID for personal tier scoping. When set, personal/context.md is generated - from this user's data only. Company and workstream tiers still use all data. - - workstream_name: Generate docs for this workstream only (skip auto-detection). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/context-documents/tree", - body=await async_maybe_transform( - { - "sources": sources, - "user_id": user_id, - "workstream_name": workstream_name, - }, - tree_generate_params.TreeGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeGenerateResponse, - ) - - async def get( - self, - tree_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGetResponse: - """ - Fetch a specific tree by its tree ID instead of selecting the latest one. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not tree_id: - raise ValueError(f"Expected a non-empty value for `tree_id` but received {tree_id!r}") - return await self._get( - path_template("/context-documents/tree/by-id/{tree_id}", tree_id=tree_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeGetResponse, - ) - - async def get_latest( - self, - *, - status: Optional[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeGetLatestResponse: - """ - Get the most recent context document tree for the authenticated app. - - By default, the endpoint returns the latest ready tree. Readiness depends on - whether the app has `require_review` enabled: - - - `require_review=False` (default): return the latest completed tree. - - `require_review=True`: return the latest published tree. - - `status` filters to a specific status (case-insensitive). When no ready tree - exists yet, the endpoint returns the newest available generation state. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/context-documents/tree/latest", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"status": status}, tree_get_latest_params.TreeGetLatestParams), - ), - cast_to=TreeGetLatestResponse, - ) - - async def progress( - self, - tree_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TreeProgressResponse: - """ - Return the generation progress for a single tree. - - Active generations include phase and counter data. Completed generations, and - generations without detailed progress data, return status only. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not tree_id: - raise ValueError(f"Expected a non-empty value for `tree_id` but received {tree_id!r}") - return await self._get( - path_template("/context-documents/tree/{tree_id}/progress", tree_id=tree_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TreeProgressResponse, - ) - - -class TreesResourceWithRawResponse: - def __init__(self, trees: TreesResource) -> None: - self._trees = trees - - self.generate = to_raw_response_wrapper( - trees.generate, - ) - self.get = to_raw_response_wrapper( - trees.get, - ) - self.get_latest = to_raw_response_wrapper( - trees.get_latest, - ) - self.progress = to_raw_response_wrapper( - trees.progress, - ) - - -class AsyncTreesResourceWithRawResponse: - def __init__(self, trees: AsyncTreesResource) -> None: - self._trees = trees - - self.generate = async_to_raw_response_wrapper( - trees.generate, - ) - self.get = async_to_raw_response_wrapper( - trees.get, - ) - self.get_latest = async_to_raw_response_wrapper( - trees.get_latest, - ) - self.progress = async_to_raw_response_wrapper( - trees.progress, - ) - - -class TreesResourceWithStreamingResponse: - def __init__(self, trees: TreesResource) -> None: - self._trees = trees - - self.generate = to_streamed_response_wrapper( - trees.generate, - ) - self.get = to_streamed_response_wrapper( - trees.get, - ) - self.get_latest = to_streamed_response_wrapper( - trees.get_latest, - ) - self.progress = to_streamed_response_wrapper( - trees.progress, - ) - - -class AsyncTreesResourceWithStreamingResponse: - def __init__(self, trees: AsyncTreesResource) -> None: - self._trees = trees - - self.generate = async_to_streamed_response_wrapper( - trees.generate, - ) - self.get = async_to_streamed_response_wrapper( - trees.get, - ) - self.get_latest = async_to_streamed_response_wrapper( - trees.get_latest, - ) - self.progress = async_to_streamed_response_wrapper( - trees.progress, - ) diff --git a/src/hyperspell/resources/entities.py b/src/hyperspell/resources/entities.py new file mode 100644 index 00000000..995157fb --- /dev/null +++ b/src/hyperspell/resources/entities.py @@ -0,0 +1,405 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..types import entity_list_params, entity_search_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..pagination import SyncEntityCursorPage, AsyncEntityCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.entity_get_response import EntityGetResponse +from ..types.entity_list_response import EntityListResponse +from ..types.entity_search_response import EntitySearchResponse + +__all__ = ["EntitiesResource", "AsyncEntitiesResource"] + + +class EntitiesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EntitiesResourceWithRawResponse: + """ + 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers + """ + return EntitiesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EntitiesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response + """ + return EntitiesResourceWithStreamingResponse(self) + + def list( + self, + *, + cursor: Optional[str] | Omit = omit, + limit: int | Omit = omit, + min_supporting_documents: Optional[int] | Omit = omit, + name_prefix: Optional[str] | Omit = omit, + search: Optional[str] | Omit = omit, + sort_by: Literal["id", "name", "type", "prominence"] | Omit = omit, + sort_dir: Literal["asc", "desc"] | Omit = omit, + status: Optional[Literal["provisional", "confirmed"]] | Omit = omit, + type: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncEntityCursorPage[EntityListResponse]: + """ + List entities available to the current app. + + Results can be filtered by type, status, name, and supporting-document count. + Use the returned cursor to retrieve the next page. + + Args: + status: How strongly the entity's current identity has been established. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/entities", + page=SyncEntityCursorPage[EntityListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "cursor": cursor, + "limit": limit, + "min_supporting_documents": min_supporting_documents, + "name_prefix": name_prefix, + "search": search, + "sort_by": sort_by, + "sort_dir": sort_dir, + "status": status, + "type": type, + }, + entity_list_params.EntityListParams, + ), + ), + model=EntityListResponse, + ) + + def get( + self, + entity_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EntityGetResponse: + """ + Fetch a single entity belonging to the current app. + + Returns 404 when the entity does not exist or is not visible to the app. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not entity_id: + raise ValueError(f"Expected a non-empty value for `entity_id` but received {entity_id!r}") + return self._get( + path_template("/entities/{entity_id}", entity_id=entity_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EntityGetResponse, + ) + + def search( + self, + *, + query: str, + limit: int | Omit = omit, + type: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EntitySearchResponse: + """ + Search the current app's entities by meaning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/entities/search", + body=maybe_transform( + { + "query": query, + "limit": limit, + "type": type, + }, + entity_search_params.EntitySearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EntitySearchResponse, + ) + + +class AsyncEntitiesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEntitiesResourceWithRawResponse: + """ + 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncEntitiesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEntitiesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response + """ + return AsyncEntitiesResourceWithStreamingResponse(self) + + def list( + self, + *, + cursor: Optional[str] | Omit = omit, + limit: int | Omit = omit, + min_supporting_documents: Optional[int] | Omit = omit, + name_prefix: Optional[str] | Omit = omit, + search: Optional[str] | Omit = omit, + sort_by: Literal["id", "name", "type", "prominence"] | Omit = omit, + sort_dir: Literal["asc", "desc"] | Omit = omit, + status: Optional[Literal["provisional", "confirmed"]] | Omit = omit, + type: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[EntityListResponse, AsyncEntityCursorPage[EntityListResponse]]: + """ + List entities available to the current app. + + Results can be filtered by type, status, name, and supporting-document count. + Use the returned cursor to retrieve the next page. + + Args: + status: How strongly the entity's current identity has been established. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/entities", + page=AsyncEntityCursorPage[EntityListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "cursor": cursor, + "limit": limit, + "min_supporting_documents": min_supporting_documents, + "name_prefix": name_prefix, + "search": search, + "sort_by": sort_by, + "sort_dir": sort_dir, + "status": status, + "type": type, + }, + entity_list_params.EntityListParams, + ), + ), + model=EntityListResponse, + ) + + async def get( + self, + entity_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EntityGetResponse: + """ + Fetch a single entity belonging to the current app. + + Returns 404 when the entity does not exist or is not visible to the app. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not entity_id: + raise ValueError(f"Expected a non-empty value for `entity_id` but received {entity_id!r}") + return await self._get( + path_template("/entities/{entity_id}", entity_id=entity_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EntityGetResponse, + ) + + async def search( + self, + *, + query: str, + limit: int | Omit = omit, + type: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EntitySearchResponse: + """ + Search the current app's entities by meaning. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/entities/search", + body=await async_maybe_transform( + { + "query": query, + "limit": limit, + "type": type, + }, + entity_search_params.EntitySearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EntitySearchResponse, + ) + + +class EntitiesResourceWithRawResponse: + def __init__(self, entities: EntitiesResource) -> None: + self._entities = entities + + self.list = to_raw_response_wrapper( + entities.list, + ) + self.get = to_raw_response_wrapper( + entities.get, + ) + self.search = to_raw_response_wrapper( + entities.search, + ) + + +class AsyncEntitiesResourceWithRawResponse: + def __init__(self, entities: AsyncEntitiesResource) -> None: + self._entities = entities + + self.list = async_to_raw_response_wrapper( + entities.list, + ) + self.get = async_to_raw_response_wrapper( + entities.get, + ) + self.search = async_to_raw_response_wrapper( + entities.search, + ) + + +class EntitiesResourceWithStreamingResponse: + def __init__(self, entities: EntitiesResource) -> None: + self._entities = entities + + self.list = to_streamed_response_wrapper( + entities.list, + ) + self.get = to_streamed_response_wrapper( + entities.get, + ) + self.search = to_streamed_response_wrapper( + entities.search, + ) + + +class AsyncEntitiesResourceWithStreamingResponse: + def __init__(self, entities: AsyncEntitiesResource) -> None: + self._entities = entities + + self.list = async_to_streamed_response_wrapper( + entities.list, + ) + self.get = async_to_streamed_response_wrapper( + entities.get, + ) + self.search = async_to_streamed_response_wrapper( + entities.search, + ) diff --git a/src/hyperspell/resources/live.py b/src/hyperspell/resources/live.py new file mode 100644 index 00000000..027642e2 --- /dev/null +++ b/src/hyperspell/resources/live.py @@ -0,0 +1,716 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..types import live_search_params, live_get_resource_params, live_list_resources_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.live_search_response import LiveSearchResponse +from ..types.live_get_resource_response import LiveGetResourceResponse +from ..types.live_list_sources_response import LiveListSourcesResponse +from ..types.live_list_resources_response import LiveListResourcesResponse + +__all__ = ["LiveResource", "AsyncLiveResource"] + + +class LiveResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> LiveResourceWithRawResponse: + """ + 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers + """ + return LiveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LiveResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response + """ + return LiveResourceWithStreamingResponse(self) + + def get_resource( + self, + resource_id: str, + *, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + connection_id: Optional[str] | Omit = omit, + index: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveGetResourceResponse: + """Fetch one resource live by id. + + A single fetch may fan out into several resources + (e.g. a thread → its messages); all are returned. + + Args: + connection_id: Specific connection id. + + index: Also queue this resource for indexing. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + if not resource_id: + raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") + return self._get( + path_template("/live/{source}/resources/{resource_id}", source=source, resource_id=resource_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "connection_id": connection_id, + "index": index, + }, + live_get_resource_params.LiveGetResourceParams, + ), + ), + cast_to=LiveGetResourceResponse, + ) + + def list_resources( + self, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + *, + connection_id: Optional[str] | Omit = omit, + cursor: Optional[str] | Omit = omit, + size: 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, + ) -> SyncCursorPage[LiveListResourcesResponse]: + """ + Page through a source's resources live (no indexing side effect). + + The cursor is opaque and integration-defined — pass back the `next_cursor` from + the previous page verbatim. + + Args: + connection_id: Specific connection id. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + return self._get_api_list( + path_template("/live/{source}/resources", source=source), + page=SyncCursorPage[LiveListResourcesResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "connection_id": connection_id, + "cursor": cursor, + "size": size, + }, + live_list_resources_params.LiveListResourcesParams, + ), + ), + model=LiveListResourcesResponse, + ) + + def list_sources( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveListSourcesResponse: + """List the user's connected sources and the live capabilities each supports.""" + return self._get( + "/live/sources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LiveListSourcesResponse, + ) + + def search( + self, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + *, + query: str, + connection_id: Optional[str] | Omit = omit, + index: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveSearchResponse: + """Search a source live for content that may not be indexed yet. + + With `index=true`, + each hit is queued for indexing (no-op for live-only sources like Google + Calendar — see `notes` in the response). + + Args: + query: Live search query. + + connection_id: Specific connection id when the user has multiple for this source. + + index: If true, queue each hit for indexing so it's on-hand next time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + return self._post( + path_template("/live/{source}/search", source=source), + body=maybe_transform( + { + "query": query, + "connection_id": connection_id, + "index": index, + }, + live_search_params.LiveSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LiveSearchResponse, + ) + + +class AsyncLiveResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncLiveResourceWithRawResponse: + """ + 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/hyperspell/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncLiveResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLiveResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/hyperspell/python-sdk#with_streaming_response + """ + return AsyncLiveResourceWithStreamingResponse(self) + + async def get_resource( + self, + resource_id: str, + *, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + connection_id: Optional[str] | Omit = omit, + index: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveGetResourceResponse: + """Fetch one resource live by id. + + A single fetch may fan out into several resources + (e.g. a thread → its messages); all are returned. + + Args: + connection_id: Specific connection id. + + index: Also queue this resource for indexing. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + if not resource_id: + raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") + return await self._get( + path_template("/live/{source}/resources/{resource_id}", source=source, resource_id=resource_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "connection_id": connection_id, + "index": index, + }, + live_get_resource_params.LiveGetResourceParams, + ), + ), + cast_to=LiveGetResourceResponse, + ) + + def list_resources( + self, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + *, + connection_id: Optional[str] | Omit = omit, + cursor: Optional[str] | Omit = omit, + size: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[LiveListResourcesResponse, AsyncCursorPage[LiveListResourcesResponse]]: + """ + Page through a source's resources live (no indexing side effect). + + The cursor is opaque and integration-defined — pass back the `next_cursor` from + the previous page verbatim. + + Args: + connection_id: Specific connection id. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + return self._get_api_list( + path_template("/live/{source}/resources", source=source), + page=AsyncCursorPage[LiveListResourcesResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "connection_id": connection_id, + "cursor": cursor, + "size": size, + }, + live_list_resources_params.LiveListResourcesParams, + ), + ), + model=LiveListResourcesResponse, + ) + + async def list_sources( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveListSourcesResponse: + """List the user's connected sources and the live capabilities each supports.""" + return await self._get( + "/live/sources", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LiveListSourcesResponse, + ) + + async def search( + self, + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + *, + query: str, + connection_id: Optional[str] | Omit = omit, + index: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> LiveSearchResponse: + """Search a source live for content that may not be indexed yet. + + With `index=true`, + each hit is queued for indexing (no-op for live-only sources like Google + Calendar — see `notes` in the response). + + Args: + query: Live search query. + + connection_id: Specific connection id when the user has multiple for this source. + + index: If true, queue each hit for indexing so it's on-hand next time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not source: + raise ValueError(f"Expected a non-empty value for `source` but received {source!r}") + return await self._post( + path_template("/live/{source}/search", source=source), + body=await async_maybe_transform( + { + "query": query, + "connection_id": connection_id, + "index": index, + }, + live_search_params.LiveSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LiveSearchResponse, + ) + + +class LiveResourceWithRawResponse: + def __init__(self, live: LiveResource) -> None: + self._live = live + + self.get_resource = to_raw_response_wrapper( + live.get_resource, + ) + self.list_resources = to_raw_response_wrapper( + live.list_resources, + ) + self.list_sources = to_raw_response_wrapper( + live.list_sources, + ) + self.search = to_raw_response_wrapper( + live.search, + ) + + +class AsyncLiveResourceWithRawResponse: + def __init__(self, live: AsyncLiveResource) -> None: + self._live = live + + self.get_resource = async_to_raw_response_wrapper( + live.get_resource, + ) + self.list_resources = async_to_raw_response_wrapper( + live.list_resources, + ) + self.list_sources = async_to_raw_response_wrapper( + live.list_sources, + ) + self.search = async_to_raw_response_wrapper( + live.search, + ) + + +class LiveResourceWithStreamingResponse: + def __init__(self, live: LiveResource) -> None: + self._live = live + + self.get_resource = to_streamed_response_wrapper( + live.get_resource, + ) + self.list_resources = to_streamed_response_wrapper( + live.list_resources, + ) + self.list_sources = to_streamed_response_wrapper( + live.list_sources, + ) + self.search = to_streamed_response_wrapper( + live.search, + ) + + +class AsyncLiveResourceWithStreamingResponse: + def __init__(self, live: AsyncLiveResource) -> None: + self._live = live + + self.get_resource = async_to_streamed_response_wrapper( + live.get_resource, + ) + self.list_resources = async_to_streamed_response_wrapper( + live.list_resources, + ) + self.list_sources = async_to_streamed_response_wrapper( + live.list_sources, + ) + self.search = async_to_streamed_response_wrapper( + live.search, + ) diff --git a/src/hyperspell/types/__init__.py b/src/hyperspell/types/__init__.py index 9f55fd0e..d07c5a5c 100644 --- a/src/hyperspell/types/__init__.py +++ b/src/hyperspell/types/__init__.py @@ -2,7 +2,14 @@ from __future__ import annotations -from . import shared, memory_get_response, memory_list_response +from . import ( + shared, + memory_get_response, + live_search_response, + memory_list_response, + live_get_resource_response, + live_list_resources_response, +) from .. import _compat from .token import Token as Token from .shared import ( @@ -57,21 +64,29 @@ from .memory_add_params import MemoryAddParams as MemoryAddParams from .memory_get_params import MemoryGetParams as MemoryGetParams from .vault_list_params import VaultListParams as VaultListParams +from .entity_list_params import EntityListParams as EntityListParams from .folder_list_params import FolderListParams as FolderListParams +from .live_search_params import LiveSearchParams as LiveSearchParams from .memory_list_params import MemoryListParams as MemoryListParams from .session_add_params import SessionAddParams as SessionAddParams +from .entity_get_response import EntityGetResponse as EntityGetResponse from .memory_get_response import MemoryGetResponse as MemoryGetResponse from .vault_list_response import VaultListResponse as VaultListResponse +from .entity_list_response import EntityListResponse as EntityListResponse +from .entity_search_params import EntitySearchParams as EntitySearchParams from .folder_list_response import FolderListResponse as FolderListResponse +from .live_search_response import LiveSearchResponse as LiveSearchResponse from .memory_list_response import MemoryListResponse as MemoryListResponse from .memory_search_params import MemorySearchParams as MemorySearchParams from .memory_update_params import MemoryUpdateParams as MemoryUpdateParams from .memory_upload_params import MemoryUploadParams as MemoryUploadParams from .auth_user_token_params import AuthUserTokenParams as AuthUserTokenParams +from .entity_search_response import EntitySearchResponse as EntitySearchResponse from .memory_add_bulk_params import MemoryAddBulkParams as MemoryAddBulkParams from .memory_delete_response import MemoryDeleteResponse as MemoryDeleteResponse from .memory_status_response import MemoryStatusResponse as MemoryStatusResponse from .connection_list_response import ConnectionListResponse as ConnectionListResponse +from .live_get_resource_params import LiveGetResourceParams as LiveGetResourceParams from .memory_add_bulk_response import MemoryAddBulkResponse as MemoryAddBulkResponse from .auth_delete_user_response import AuthDeleteUserResponse as AuthDeleteUserResponse from .integration_list_response import IntegrationListResponse as IntegrationListResponse @@ -80,30 +95,44 @@ from .connection_revoke_response import ConnectionRevokeResponse as ConnectionRevokeResponse from .folder_set_policies_params import FolderSetPoliciesParams as FolderSetPoliciesParams from .integration_connect_params import IntegrationConnectParams as IntegrationConnectParams +from .live_get_resource_response import LiveGetResourceResponse as LiveGetResourceResponse +from .live_list_resources_params import LiveListResourcesParams as LiveListResourcesParams +from .live_list_sources_response import LiveListSourcesResponse as LiveListSourcesResponse from .evaluate_score_query_params import EvaluateScoreQueryParams as EvaluateScoreQueryParams from .action_add_reaction_response import ActionAddReactionResponse as ActionAddReactionResponse from .action_send_message_response import ActionSendMessageResponse as ActionSendMessageResponse -from .context_document_list_params import ContextDocumentListParams as ContextDocumentListParams from .evaluate_list_queries_params import EvaluateListQueriesParams as EvaluateListQueriesParams from .folder_set_policies_response import FolderSetPoliciesResponse as FolderSetPoliciesResponse from .integration_connect_response import IntegrationConnectResponse as IntegrationConnectResponse -from .context_document_get_response import ContextDocumentGetResponse as ContextDocumentGetResponse +from .live_list_resources_response import LiveListResourcesResponse as LiveListResourcesResponse from .evaluate_score_query_response import EvaluateScoreQueryResponse as EvaluateScoreQueryResponse from .folder_delete_policy_response import FolderDeletePolicyResponse as FolderDeletePolicyResponse from .folder_list_policies_response import FolderListPoliciesResponse as FolderListPoliciesResponse -from .context_document_list_response import ContextDocumentListResponse as ContextDocumentListResponse from .evaluate_list_queries_response import EvaluateListQueriesResponse as EvaluateListQueriesResponse from .evaluate_score_highlight_params import EvaluateScoreHighlightParams as EvaluateScoreHighlightParams -from .context_document_generate_params import ContextDocumentGenerateParams as ContextDocumentGenerateParams from .evaluate_score_highlight_response import EvaluateScoreHighlightResponse as EvaluateScoreHighlightResponse -from .context_document_generate_response import ContextDocumentGenerateResponse as ContextDocumentGenerateResponse # Rebuild cyclical models only after all modules are imported. # This ensures that, when building the deferred (due to cyclical references) model schema, # Pydantic can resolve the necessary references. # See: https://github.com/pydantic/pydantic/issues/11250 for more context. if _compat.PYDANTIC_V1: + live_get_resource_response.DocumentDocumentInvoice.update_forward_refs() # type: ignore + live_get_resource_response.DocumentChunk.update_forward_refs() # type: ignore + live_get_resource_response.Document.update_forward_refs() # type: ignore + live_get_resource_response.LiveGetResourceResponse.update_forward_refs() # type: ignore + live_list_resources_response.DocumentInvoice.update_forward_refs() # type: ignore + live_list_resources_response.Chunk.update_forward_refs() # type: ignore + live_list_resources_response.LiveListResourcesResponse.update_forward_refs() # type: ignore + live_search_response.DocumentDocumentInvoice.update_forward_refs() # type: ignore + live_search_response.DocumentChunk.update_forward_refs() # type: ignore + live_search_response.Document.update_forward_refs() # type: ignore + live_search_response.LiveSearchResponse.update_forward_refs() # type: ignore + memory_list_response.DocumentInvoice.update_forward_refs() # type: ignore + memory_list_response.Chunk.update_forward_refs() # type: ignore memory_list_response.MemoryListResponse.update_forward_refs() # type: ignore + memory_get_response.DocumentInvoice.update_forward_refs() # type: ignore + memory_get_response.Chunk.update_forward_refs() # type: ignore memory_get_response.MemoryGetResponse.update_forward_refs() # type: ignore shared.callout.Callout.update_forward_refs() # type: ignore shared.chunk.Chunk.update_forward_refs() # type: ignore @@ -124,6 +153,8 @@ shared.person.Person.update_forward_refs() # type: ignore shared.query_result.QueryResult.update_forward_refs() # type: ignore shared.quote.Quote.update_forward_refs() # type: ignore + shared.scored_document_response.DocumentInvoice.update_forward_refs() # type: ignore + shared.scored_document_response.Chunk.update_forward_refs() # type: ignore shared.scored_document_response.ScoredDocumentResponse.update_forward_refs() # type: ignore shared.table.Table.update_forward_refs() # type: ignore shared.table_cell.TableCell.update_forward_refs() # type: ignore @@ -134,7 +165,22 @@ shared.utterance.Utterance.update_forward_refs() # type: ignore shared.website.Website.update_forward_refs() # type: ignore else: + live_get_resource_response.DocumentDocumentInvoice.model_rebuild(_parent_namespace_depth=0) + live_get_resource_response.DocumentChunk.model_rebuild(_parent_namespace_depth=0) + live_get_resource_response.Document.model_rebuild(_parent_namespace_depth=0) + live_get_resource_response.LiveGetResourceResponse.model_rebuild(_parent_namespace_depth=0) + live_list_resources_response.DocumentInvoice.model_rebuild(_parent_namespace_depth=0) + live_list_resources_response.Chunk.model_rebuild(_parent_namespace_depth=0) + live_list_resources_response.LiveListResourcesResponse.model_rebuild(_parent_namespace_depth=0) + live_search_response.DocumentDocumentInvoice.model_rebuild(_parent_namespace_depth=0) + live_search_response.DocumentChunk.model_rebuild(_parent_namespace_depth=0) + live_search_response.Document.model_rebuild(_parent_namespace_depth=0) + live_search_response.LiveSearchResponse.model_rebuild(_parent_namespace_depth=0) + memory_list_response.DocumentInvoice.model_rebuild(_parent_namespace_depth=0) + memory_list_response.Chunk.model_rebuild(_parent_namespace_depth=0) memory_list_response.MemoryListResponse.model_rebuild(_parent_namespace_depth=0) + memory_get_response.DocumentInvoice.model_rebuild(_parent_namespace_depth=0) + memory_get_response.Chunk.model_rebuild(_parent_namespace_depth=0) memory_get_response.MemoryGetResponse.model_rebuild(_parent_namespace_depth=0) shared.callout.Callout.model_rebuild(_parent_namespace_depth=0) shared.chunk.Chunk.model_rebuild(_parent_namespace_depth=0) @@ -155,6 +201,8 @@ shared.person.Person.model_rebuild(_parent_namespace_depth=0) shared.query_result.QueryResult.model_rebuild(_parent_namespace_depth=0) shared.quote.Quote.model_rebuild(_parent_namespace_depth=0) + shared.scored_document_response.DocumentInvoice.model_rebuild(_parent_namespace_depth=0) + shared.scored_document_response.Chunk.model_rebuild(_parent_namespace_depth=0) shared.scored_document_response.ScoredDocumentResponse.model_rebuild(_parent_namespace_depth=0) shared.table.Table.model_rebuild(_parent_namespace_depth=0) shared.table_cell.TableCell.model_rebuild(_parent_namespace_depth=0) diff --git a/src/hyperspell/types/context_document_generate_params.py b/src/hyperspell/types/context_document_generate_params.py deleted file mode 100644 index 196ed0a4..00000000 --- a/src/hyperspell/types/context_document_generate_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -from .._types import SequenceNotStr - -__all__ = ["ContextDocumentGenerateParams"] - - -class ContextDocumentGenerateParams(TypedDict, total=False): - model: str - """Model used for final synthesis.""" - - prompt: Optional[str] - """Custom prompt template. Replaces the standard summary prompt.""" - - sources: Optional[SequenceNotStr[str]] - """Integration sources to include (e.g., ['gmail', 'slack']). - - Defaults to all connected integrations. - """ - - user_id: Optional[str] - """Scope generation to a specific user's data.""" diff --git a/src/hyperspell/types/context_document_generate_response.py b/src/hyperspell/types/context_document_generate_response.py deleted file mode 100644 index 7dd7059b..00000000 --- a/src/hyperspell/types/context_document_generate_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime - -from .._models import BaseModel - -__all__ = ["ContextDocumentGenerateResponse"] - - -class ContextDocumentGenerateResponse(BaseModel): - created_at: datetime - - document_id: str - - status: str diff --git a/src/hyperspell/types/context_document_get_response.py b/src/hyperspell/types/context_document_get_response.py deleted file mode 100644 index bc43de69..00000000 --- a/src/hyperspell/types/context_document_get_response.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from datetime import datetime - -from .._models import BaseModel - -__all__ = ["ContextDocumentGetResponse"] - - -class ContextDocumentGetResponse(BaseModel): - completed_at: Optional[datetime] = None - - content: Optional[str] = None - - created_at: datetime - - document_id: str - - error: Optional[str] = None - - metadata: Dict[str, object] - - model: str - - prompt: Optional[str] = None - - sources: List[str] - - status: str - - token_usage: Optional[Dict[str, object]] = None - - user_id: Optional[str] = None diff --git a/src/hyperspell/types/context_document_list_params.py b/src/hyperspell/types/context_document_list_params.py deleted file mode 100644 index ebc6f13b..00000000 --- a/src/hyperspell/types/context_document_list_params.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, TypedDict - -__all__ = ["ContextDocumentListParams"] - - -class ContextDocumentListParams(TypedDict, total=False): - cursor: Optional[str] - - limit: int - - status: Optional[Literal["processing", "completed", "failed"]] diff --git a/src/hyperspell/types/context_document_list_response.py b/src/hyperspell/types/context_document_list_response.py deleted file mode 100644 index 97cf9372..00000000 --- a/src/hyperspell/types/context_document_list_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from .._models import BaseModel - -__all__ = ["ContextDocumentListResponse"] - - -class ContextDocumentListResponse(BaseModel): - completed_at: Optional[datetime] = None - - created_at: datetime - - document_id: str - - model: str - - sources: List[str] - - status: str - - token_count: Optional[int] = None - - error: Optional[str] = None diff --git a/src/hyperspell/types/context_documents/__init__.py b/src/hyperspell/types/context_documents/__init__.py deleted file mode 100644 index 869a58d2..00000000 --- a/src/hyperspell/types/context_documents/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .tree_get_response import TreeGetResponse as TreeGetResponse -from .digest_list_params import DigestListParams as DigestListParams -from .config_get_response import ConfigGetResponse as ConfigGetResponse -from .config_update_params import ConfigUpdateParams as ConfigUpdateParams -from .digest_list_response import DigestListResponse as DigestListResponse -from .tree_generate_params import TreeGenerateParams as TreeGenerateParams -from .config_reset_response import ConfigResetResponse as ConfigResetResponse -from .config_update_response import ConfigUpdateResponse as ConfigUpdateResponse -from .digest_generate_params import DigestGenerateParams as DigestGenerateParams -from .tree_generate_response import TreeGenerateResponse as TreeGenerateResponse -from .tree_get_latest_params import TreeGetLatestParams as TreeGetLatestParams -from .tree_progress_response import TreeProgressResponse as TreeProgressResponse -from .digest_generate_response import DigestGenerateResponse as DigestGenerateResponse -from .tree_get_latest_response import TreeGetLatestResponse as TreeGetLatestResponse diff --git a/src/hyperspell/types/context_documents/config_get_response.py b/src/hyperspell/types/context_documents/config_get_response.py deleted file mode 100644 index ca56060d..00000000 --- a/src/hyperspell/types/context_documents/config_get_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from ..._models import BaseModel - -__all__ = ["ConfigGetResponse"] - - -class ConfigGetResponse(BaseModel): - """Brain-generation settings that customers can view and edit.""" - - prompts: Dict[str, object] - - source_weights: Dict[str, str] - - structure: Dict[str, object] - - detected_domain: Optional[str] = None - - domain: Optional[str] = None diff --git a/src/hyperspell/types/context_documents/config_reset_response.py b/src/hyperspell/types/context_documents/config_reset_response.py deleted file mode 100644 index 9dcb234c..00000000 --- a/src/hyperspell/types/context_documents/config_reset_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from ..._models import BaseModel - -__all__ = ["ConfigResetResponse"] - - -class ConfigResetResponse(BaseModel): - """Brain-generation settings that customers can view and edit.""" - - prompts: Dict[str, object] - - source_weights: Dict[str, str] - - structure: Dict[str, object] - - detected_domain: Optional[str] = None - - domain: Optional[str] = None diff --git a/src/hyperspell/types/context_documents/config_update_params.py b/src/hyperspell/types/context_documents/config_update_params.py deleted file mode 100644 index 0acda28e..00000000 --- a/src/hyperspell/types/context_documents/config_update_params.py +++ /dev/null @@ -1,63 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Iterable, Optional -from typing_extensions import Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["ConfigUpdateParams", "Structure", "StructureCompany", "StructureWorkstream"] - - -class ConfigUpdateParams(TypedDict, total=False): - company_prompts: Optional[Dict[str, str]] - - detection_prompt: Optional[str] - - domain: Optional[str] - - personal_prompt: Optional[str] - - source_weights: Optional[Dict[str, str]] - - structure: Optional[Structure] - """Per-tier document definitions for custom generation.""" - - workstream_prompts: Optional[Dict[str, str]] - - -class StructureCompany(TypedDict, total=False): - """ - One document in a context-tree tier: what to generate and how to retrieve for it. - """ - - filename: Required[str] - - key: Required[str] - - prompt: Required[str] - - search_queries: Required[SequenceNotStr[str]] - - -class StructureWorkstream(TypedDict, total=False): - """ - One document in a context-tree tier: what to generate and how to retrieve for it. - """ - - filename: Required[str] - - key: Required[str] - - prompt: Required[str] - - search_queries: Required[SequenceNotStr[str]] - - -class Structure(TypedDict, total=False): - """Per-tier document definitions for custom generation.""" - - company: Optional[Iterable[StructureCompany]] - - workstream: Optional[Iterable[StructureWorkstream]] diff --git a/src/hyperspell/types/context_documents/config_update_response.py b/src/hyperspell/types/context_documents/config_update_response.py deleted file mode 100644 index 3d4a6c32..00000000 --- a/src/hyperspell/types/context_documents/config_update_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from ..._models import BaseModel - -__all__ = ["ConfigUpdateResponse"] - - -class ConfigUpdateResponse(BaseModel): - """Brain-generation settings that customers can view and edit.""" - - prompts: Dict[str, object] - - source_weights: Dict[str, str] - - structure: Dict[str, object] - - detected_domain: Optional[str] = None - - domain: Optional[str] = None diff --git a/src/hyperspell/types/context_documents/digest_generate_params.py b/src/hyperspell/types/context_documents/digest_generate_params.py deleted file mode 100644 index f44ef839..00000000 --- a/src/hyperspell/types/context_documents/digest_generate_params.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from datetime import datetime -from typing_extensions import Annotated, TypedDict - -from ..._types import SequenceNotStr -from ..._utils import PropertyInfo - -__all__ = ["DigestGenerateParams"] - - -class DigestGenerateParams(TypedDict, total=False): - period: str - """Digest cadence: 'daily' or 'weekly'. - - Sets the default window when none is given. - """ - - sources: Optional[SequenceNotStr[str]] - """Integration sources to include (e.g., ['slack', 'github']). Defaults to all.""" - - window_end: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] - """Exclusive upper bound of the digest window. Defaults to now.""" - - window_start: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] - """Inclusive lower bound of the digest window. - - Defaults to midnight UTC today (paired with window_end=now) when omitted. Both - bounds must be supplied together. - """ diff --git a/src/hyperspell/types/context_documents/digest_generate_response.py b/src/hyperspell/types/context_documents/digest_generate_response.py deleted file mode 100644 index d3112463..00000000 --- a/src/hyperspell/types/context_documents/digest_generate_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime - -from ..._models import BaseModel - -__all__ = ["DigestGenerateResponse"] - - -class DigestGenerateResponse(BaseModel): - created_at: datetime - - status: str - - tree_id: str diff --git a/src/hyperspell/types/context_documents/digest_list_params.py b/src/hyperspell/types/context_documents/digest_list_params.py deleted file mode 100644 index 79f076e3..00000000 --- a/src/hyperspell/types/context_documents/digest_list_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["DigestListParams"] - - -class DigestListParams(TypedDict, total=False): - limit: int - - period: Optional[str] diff --git a/src/hyperspell/types/context_documents/digest_list_response.py b/src/hyperspell/types/context_documents/digest_list_response.py deleted file mode 100644 index a55ab243..00000000 --- a/src/hyperspell/types/context_documents/digest_list_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from ..._models import BaseModel - -__all__ = ["DigestListResponse", "Digest"] - - -class Digest(BaseModel): - """A digest summary. Fetch the full content through the tree-by-ID endpoint.""" - - completed_at: Optional[datetime] = None - - created_at: datetime - - period: Optional[str] = None - - status: str - - tree_id: str - - window_end: Optional[datetime] = None - - window_start: Optional[datetime] = None - - -class DigestListResponse(BaseModel): - digests: List[Digest] diff --git a/src/hyperspell/types/context_documents/tree_generate_params.py b/src/hyperspell/types/context_documents/tree_generate_params.py deleted file mode 100644 index 18b51da6..00000000 --- a/src/hyperspell/types/context_documents/tree_generate_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["TreeGenerateParams"] - - -class TreeGenerateParams(TypedDict, total=False): - sources: Optional[SequenceNotStr[str]] - """Integration sources to include (e.g., ['gmail', 'slack']). Defaults to all.""" - - user_id: Optional[str] - """User ID for personal tier scoping. - - When set, personal/context.md is generated from this user's data only. Company - and workstream tiers still use all data. - """ - - workstream_name: Optional[str] - """Generate docs for this workstream only (skip auto-detection).""" diff --git a/src/hyperspell/types/context_documents/tree_generate_response.py b/src/hyperspell/types/context_documents/tree_generate_response.py deleted file mode 100644 index 8c1e6906..00000000 --- a/src/hyperspell/types/context_documents/tree_generate_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime - -from ..._models import BaseModel - -__all__ = ["TreeGenerateResponse"] - - -class TreeGenerateResponse(BaseModel): - created_at: datetime - - status: str - - tree_id: str diff --git a/src/hyperspell/types/context_documents/tree_get_latest_params.py b/src/hyperspell/types/context_documents/tree_get_latest_params.py deleted file mode 100644 index 81893150..00000000 --- a/src/hyperspell/types/context_documents/tree_get_latest_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["TreeGetLatestParams"] - - -class TreeGetLatestParams(TypedDict, total=False): - status: Optional[str] diff --git a/src/hyperspell/types/context_documents/tree_get_latest_response.py b/src/hyperspell/types/context_documents/tree_get_latest_response.py deleted file mode 100644 index 75d993ad..00000000 --- a/src/hyperspell/types/context_documents/tree_get_latest_response.py +++ /dev/null @@ -1,70 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["TreeGetLatestResponse", "File", "Generating"] - - -class File(BaseModel): - content: str - - path: str - - team: Optional[str] = None - - tier: str - - updated_at: str - - error: Optional[str] = None - - provenance: Optional[Dict[str, object]] = None - - status: Optional[str] = None - - -class Generating(BaseModel): - """Status of a newer generation that is processing or recently failed. - - This can accompany the last ready tree so clients can report progress while - continuing to use ready content. - """ - - created_at: datetime - - status: Literal["processing", "failed"] - - tree_id: str - - error: Optional[str] = None - - progress: Optional[Dict[str, object]] = None - - -class TreeGetLatestResponse(BaseModel): - completed_at: Optional[datetime] = None - - created_at: datetime - - error: Optional[str] = None - - files: Optional[List[File]] = None - - meta: Dict[str, object] - - status: str - - tree_id: str - - version: int - - generating: Optional[Generating] = None - """Status of a newer generation that is processing or recently failed. - - This can accompany the last ready tree so clients can report progress while - continuing to use ready content. - """ diff --git a/src/hyperspell/types/context_documents/tree_get_response.py b/src/hyperspell/types/context_documents/tree_get_response.py deleted file mode 100644 index 99db09be..00000000 --- a/src/hyperspell/types/context_documents/tree_get_response.py +++ /dev/null @@ -1,70 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["TreeGetResponse", "File", "Generating"] - - -class File(BaseModel): - content: str - - path: str - - team: Optional[str] = None - - tier: str - - updated_at: str - - error: Optional[str] = None - - provenance: Optional[Dict[str, object]] = None - - status: Optional[str] = None - - -class Generating(BaseModel): - """Status of a newer generation that is processing or recently failed. - - This can accompany the last ready tree so clients can report progress while - continuing to use ready content. - """ - - created_at: datetime - - status: Literal["processing", "failed"] - - tree_id: str - - error: Optional[str] = None - - progress: Optional[Dict[str, object]] = None - - -class TreeGetResponse(BaseModel): - completed_at: Optional[datetime] = None - - created_at: datetime - - error: Optional[str] = None - - files: Optional[List[File]] = None - - meta: Dict[str, object] - - status: str - - tree_id: str - - version: int - - generating: Optional[Generating] = None - """Status of a newer generation that is processing or recently failed. - - This can accompany the last ready tree so clients can report progress while - continuing to use ready content. - """ diff --git a/src/hyperspell/types/context_documents/tree_progress_response.py b/src/hyperspell/types/context_documents/tree_progress_response.py deleted file mode 100644 index 95add82d..00000000 --- a/src/hyperspell/types/context_documents/tree_progress_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional - -from ..._models import BaseModel - -__all__ = ["TreeProgressResponse"] - - -class TreeProgressResponse(BaseModel): - """Response shape for GET /context-documents/tree/{tree_id}/progress.""" - - status: str - - tree_id: str - - completed_docs: Optional[int] = None - - failed_docs: Optional[int] = None - - failed_keys: Optional[List[str]] = None - - phase: Optional[str] = None - """Generation phase. - - Values: discover, search, select, synthesize, finalize, personal, done. Null - when detailed progress is unavailable. - """ - - total_docs: Optional[int] = None diff --git a/src/hyperspell/types/entity_get_response.py b/src/hyperspell/types/entity_get_response.py new file mode 100644 index 00000000..81c69450 --- /dev/null +++ b/src/hyperspell/types/entity_get_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EntityGetResponse"] + + +class EntityGetResponse(BaseModel): + id: str + + created_at: datetime + + name: str + + status: Literal["provisional", "confirmed"] + """How strongly the entity's current identity has been established.""" + + type: str + + updated_at: datetime + + attributes: Optional[Dict[str, object]] = None + + description: Optional[str] = None + + hard_linked_mention_count: Optional[int] = None + + prominence_calculated_at: Optional[datetime] = None + + prominence_version: Optional[str] = None + + record_count: Optional[int] = None + + supporting_document_count: Optional[int] = None + + supporting_scope_count: Optional[int] = None diff --git a/src/hyperspell/types/entity_list_params.py b/src/hyperspell/types/entity_list_params.py new file mode 100644 index 00000000..5964c0b7 --- /dev/null +++ b/src/hyperspell/types/entity_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["EntityListParams"] + + +class EntityListParams(TypedDict, total=False): + cursor: Optional[str] + + limit: int + + min_supporting_documents: Optional[int] + + name_prefix: Optional[str] + + search: Optional[str] + + sort_by: Literal["id", "name", "type", "prominence"] + + sort_dir: Literal["asc", "desc"] + + status: Optional[Literal["provisional", "confirmed"]] + """How strongly the entity's current identity has been established.""" + + type: Optional[str] diff --git a/src/hyperspell/types/entity_list_response.py b/src/hyperspell/types/entity_list_response.py new file mode 100644 index 00000000..03db93f6 --- /dev/null +++ b/src/hyperspell/types/entity_list_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EntityListResponse"] + + +class EntityListResponse(BaseModel): + id: str + + created_at: datetime + + name: str + + status: Literal["provisional", "confirmed"] + """How strongly the entity's current identity has been established.""" + + type: str + + updated_at: datetime + + attributes: Optional[Dict[str, object]] = None + + description: Optional[str] = None + + hard_linked_mention_count: Optional[int] = None + + prominence_calculated_at: Optional[datetime] = None + + prominence_version: Optional[str] = None + + record_count: Optional[int] = None + + supporting_document_count: Optional[int] = None + + supporting_scope_count: Optional[int] = None diff --git a/src/hyperspell/types/entity_search_params.py b/src/hyperspell/types/entity_search_params.py new file mode 100644 index 00000000..4a2f7406 --- /dev/null +++ b/src/hyperspell/types/entity_search_params.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["EntitySearchParams"] + + +class EntitySearchParams(TypedDict, total=False): + query: Required[str] + + limit: int + + type: Optional[str] diff --git a/src/hyperspell/types/entity_search_response.py b/src/hyperspell/types/entity_search_response.py new file mode 100644 index 00000000..a837a1d9 --- /dev/null +++ b/src/hyperspell/types/entity_search_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["EntitySearchResponse", "Item"] + + +class Item(BaseModel): + id: str + + created_at: datetime + + name: str + + similarity: float + + status: Literal["provisional", "confirmed"] + """How strongly the entity's current identity has been established.""" + + type: str + + updated_at: datetime + + attributes: Optional[Dict[str, object]] = None + + description: Optional[str] = None + + hard_linked_mention_count: Optional[int] = None + + prominence_calculated_at: Optional[datetime] = None + + prominence_version: Optional[str] = None + + record_count: Optional[int] = None + + supporting_document_count: Optional[int] = None + + supporting_scope_count: Optional[int] = None + + +class EntitySearchResponse(BaseModel): + items: List[Item] diff --git a/src/hyperspell/types/live_get_resource_params.py b/src/hyperspell/types/live_get_resource_params.py new file mode 100644 index 00000000..1309935d --- /dev/null +++ b/src/hyperspell/types/live_get_resource_params.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["LiveGetResourceParams"] + + +class LiveGetResourceParams(TypedDict, total=False): + source: Required[ + Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ] + ] + + connection_id: Optional[str] + """Specific connection id.""" + + index: bool + """Also queue this resource for indexing.""" diff --git a/src/hyperspell/types/live_get_resource_response.py b/src/hyperspell/types/live_get_resource_response.py new file mode 100644 index 00000000..e1889705 --- /dev/null +++ b/src/hyperspell/types/live_get_resource_response.py @@ -0,0 +1,282 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List as TypingList, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.blob import Blob +from .shared.code import Code +from .shared.link import Link +from .shared.text import Text +from .shared.image import Image +from .shared.trace import Trace +from .shared.comment import Comment +from .shared.divider import Divider +from .shared.metadata import Metadata +from .shared.tool_call import ToolCall +from .shared.line_break import LineBreak +from .shared.tool_result import ToolResult +from .shared.trace_message import TraceMessage + +__all__ = [ + "LiveGetResourceResponse", + "Document", + "DocumentDocument", + "DocumentDocumentInvoice", + "DocumentDocumentInvoiceChild", + "DocumentChunk", +] + +DocumentDocumentInvoiceChild: TypeAlias = Annotated[ + Union[ + Blob, + "Callout", + "Chunk", + Code, + Comment, + Divider, + "Equation", + "Footnote", + "Heading", + Image, + Link, + LineBreak, + "SharedList", + "ListItem", + "Page", + "Paragraph", + "Quote", + "Table", + "TableCell", + "TableRow", + Text, + "ToDo", + ToolCall, + ToolResult, + TraceMessage, + "Utterance", + ], + PropertyInfo(discriminator="type"), +] + + +class DocumentDocumentInvoice(BaseModel): + """A customer invoice, vendor bill, or credit memo. + + Line items are included in ``children``. + """ + + id: Optional[str] = None + + attachment_names: Optional[TypingList[str]] = None + + balance_amount: Optional[float] = None + + cancelled_at: Optional[datetime] = None + + children: Optional[TypingList[DocumentDocumentInvoiceChild]] = None + + contact_id: Optional[str] = None + + contact_name: Optional[str] = None + + currency: Optional[str] = None + + due_at: Optional[datetime] = None + + invoice_type: Optional[str] = None + + metadata: Optional[Metadata] = None + """Optional annotations carried by a hyperdoc node. + + Includes source provenance and human edit attribution. Unset metadata is omitted + from serialized responses. + """ + + notes: Optional[str] = None + + number: Optional[str] = None + + organization_id: Optional[str] = None + + paid_amount: Optional[float] = None + + paid_at: Optional[datetime] = None + + posted_at: Optional[datetime] = None + + reference: Optional[str] = None + + refund_amount: Optional[float] = None + + refund_reason: Optional[str] = None + + refunded_at: Optional[datetime] = None + + status: Optional[str] = None + + tax_amount: Optional[float] = None + + text: Optional[str] = None + + total_amount: Optional[float] = None + + type: Optional[Literal["invoice"]] = None + + +DocumentDocument: TypeAlias = Annotated[ + Union[ + "document.Document", + "Website", + "Task", + "Person", + "Message", + "Event", + "File", + "Conversation", + Trace, + "Transcript", + "Company", + "Deal", + DocumentDocumentInvoice, + ], + PropertyInfo(discriminator="type"), +] + + +class DocumentChunk(BaseModel): + """A searchable chunk extracted from a document during ingestion. + + `summary` is null when no summary was generated for the chunk. + """ + + chunk_id: str + """Stable identifier of the chunk.""" + + summary: Optional[str] = None + """LLM-generated summary of the chunk, if one was produced.""" + + +class Document(BaseModel): + """A document-shaped API response containing the hyperdoc tree.""" + + document: DocumentDocument + """The full hyperdoc tree. + + Switch on `type` for the document frame and recurse through `children` for the + body. + """ + + resource_id: str + + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ] + + type: str + """Hyperdoc document type discriminator (document, message, file, event, ...).""" + + chunks: Optional[TypingList[DocumentChunk]] = None + """Extracted memories (chunks with summaries) for this document, in document order. + + Present only when explicitly requested via `include_chunks`; omitted otherwise. + """ + + collection: Optional[str] = None + """The document's collection, if any.""" + + document_date: Optional[datetime] = None + """The document's own date (e.g. email sent date, event date).""" + + ingested_at: Optional[datetime] = None + """When Hyperspell first indexed the document.""" + + last_modified_at: Optional[datetime] = None + """When the source document was last modified, if supplied by the source.""" + + metadata: Optional[Dict[str, object]] = None + """Filterable custom metadata attached to the document.""" + + status: Optional[ + Literal["pending", "processing", "completed", "failed", "pending_review", "skipped", "filtered", "cancelled"] + ] = None + """Indexing status of the document.""" + + title: Optional[str] = None + """Human-readable document title.""" + + +class LiveGetResourceResponse(BaseModel): + """A fetch/search result: the live documents plus what happened to them.""" + + documents: TypingList[Document] + + indexed: Optional[bool] = None + + notes: Optional[TypingList[str]] = None + + +from .shared import document +from .shared.deal import Deal +from .shared.file import File +from .shared.list import List as SharedList +from .shared.page import Page +from .shared.task import Task +from .shared.chunk import Chunk +from .shared.event import Event +from .shared.quote import Quote +from .shared.table import Table +from .shared.to_do import ToDo +from .shared.person import Person +from .shared.callout import Callout +from .shared.company import Company +from .shared.heading import Heading +from .shared.message import Message +from .shared.website import Website +from .shared.equation import Equation +from .shared.footnote import Footnote +from .shared.list_item import ListItem +from .shared.paragraph import Paragraph +from .shared.table_row import TableRow +from .shared.utterance import Utterance +from .shared.table_cell import TableCell +from .shared.transcript import Transcript +from .shared.conversation import Conversation diff --git a/src/hyperspell/types/live_list_resources_params.py b/src/hyperspell/types/live_list_resources_params.py new file mode 100644 index 00000000..ce85b74d --- /dev/null +++ b/src/hyperspell/types/live_list_resources_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["LiveListResourcesParams"] + + +class LiveListResourcesParams(TypedDict, total=False): + connection_id: Optional[str] + """Specific connection id.""" + + cursor: Optional[str] + + size: int diff --git a/src/hyperspell/types/live_list_resources_response.py b/src/hyperspell/types/live_list_resources_response.py new file mode 100644 index 00000000..9dd705fe --- /dev/null +++ b/src/hyperspell/types/live_list_resources_response.py @@ -0,0 +1,264 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List as TypingList, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.blob import Blob +from .shared.code import Code +from .shared.link import Link +from .shared.text import Text +from .shared.image import Image +from .shared.trace import Trace +from .shared.comment import Comment +from .shared.divider import Divider +from .shared.metadata import Metadata +from .shared.tool_call import ToolCall +from .shared.line_break import LineBreak +from .shared.tool_result import ToolResult +from .shared.trace_message import TraceMessage + +__all__ = ["LiveListResourcesResponse", "Document", "DocumentInvoice", "DocumentInvoiceChild", "Chunk"] + +DocumentInvoiceChild: TypeAlias = Annotated[ + Union[ + Blob, + "Callout", + "chunk.Chunk", + Code, + Comment, + Divider, + "Equation", + "Footnote", + "Heading", + Image, + Link, + LineBreak, + "SharedList", + "ListItem", + "Page", + "Paragraph", + "Quote", + "Table", + "TableCell", + "TableRow", + Text, + "ToDo", + ToolCall, + ToolResult, + TraceMessage, + "Utterance", + ], + PropertyInfo(discriminator="type"), +] + + +class DocumentInvoice(BaseModel): + """A customer invoice, vendor bill, or credit memo. + + Line items are included in ``children``. + """ + + id: Optional[str] = None + + attachment_names: Optional[TypingList[str]] = None + + balance_amount: Optional[float] = None + + cancelled_at: Optional[datetime] = None + + children: Optional[TypingList[DocumentInvoiceChild]] = None + + contact_id: Optional[str] = None + + contact_name: Optional[str] = None + + currency: Optional[str] = None + + due_at: Optional[datetime] = None + + invoice_type: Optional[str] = None + + metadata: Optional[Metadata] = None + """Optional annotations carried by a hyperdoc node. + + Includes source provenance and human edit attribution. Unset metadata is omitted + from serialized responses. + """ + + notes: Optional[str] = None + + number: Optional[str] = None + + organization_id: Optional[str] = None + + paid_amount: Optional[float] = None + + paid_at: Optional[datetime] = None + + posted_at: Optional[datetime] = None + + reference: Optional[str] = None + + refund_amount: Optional[float] = None + + refund_reason: Optional[str] = None + + refunded_at: Optional[datetime] = None + + status: Optional[str] = None + + tax_amount: Optional[float] = None + + text: Optional[str] = None + + total_amount: Optional[float] = None + + type: Optional[Literal["invoice"]] = None + + +Document: TypeAlias = Annotated[ + Union[ + "document.Document", + "Website", + "Task", + "Person", + "Message", + "Event", + "File", + "Conversation", + Trace, + "Transcript", + "Company", + "Deal", + DocumentInvoice, + ], + PropertyInfo(discriminator="type"), +] + + +class Chunk(BaseModel): + """A searchable chunk extracted from a document during ingestion. + + `summary` is null when no summary was generated for the chunk. + """ + + chunk_id: str + """Stable identifier of the chunk.""" + + summary: Optional[str] = None + """LLM-generated summary of the chunk, if one was produced.""" + + +class LiveListResourcesResponse(BaseModel): + """A document-shaped API response containing the hyperdoc tree.""" + + document: Document + """The full hyperdoc tree. + + Switch on `type` for the document frame and recurse through `children` for the + body. + """ + + resource_id: str + + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ] + + type: str + """Hyperdoc document type discriminator (document, message, file, event, ...).""" + + chunks: Optional[TypingList[Chunk]] = None + """Extracted memories (chunks with summaries) for this document, in document order. + + Present only when explicitly requested via `include_chunks`; omitted otherwise. + """ + + collection: Optional[str] = None + """The document's collection, if any.""" + + document_date: Optional[datetime] = None + """The document's own date (e.g. email sent date, event date).""" + + ingested_at: Optional[datetime] = None + """When Hyperspell first indexed the document.""" + + last_modified_at: Optional[datetime] = None + """When the source document was last modified, if supplied by the source.""" + + metadata: Optional[Dict[str, object]] = None + """Filterable custom metadata attached to the document.""" + + status: Optional[ + Literal["pending", "processing", "completed", "failed", "pending_review", "skipped", "filtered", "cancelled"] + ] = None + """Indexing status of the document.""" + + title: Optional[str] = None + """Human-readable document title.""" + + +from .shared import chunk, document +from .shared.deal import Deal +from .shared.file import File +from .shared.list import List as SharedList +from .shared.page import Page +from .shared.task import Task +from .shared.event import Event +from .shared.quote import Quote +from .shared.table import Table +from .shared.to_do import ToDo +from .shared.person import Person +from .shared.callout import Callout +from .shared.company import Company +from .shared.heading import Heading +from .shared.message import Message +from .shared.website import Website +from .shared.equation import Equation +from .shared.footnote import Footnote +from .shared.list_item import ListItem +from .shared.paragraph import Paragraph +from .shared.table_row import TableRow +from .shared.utterance import Utterance +from .shared.table_cell import TableCell +from .shared.transcript import Transcript +from .shared.conversation import Conversation diff --git a/src/hyperspell/types/live_list_sources_response.py b/src/hyperspell/types/live_list_sources_response.py new file mode 100644 index 00000000..2507b899 --- /dev/null +++ b/src/hyperspell/types/live_list_sources_response.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["LiveListSourcesResponse", "Source"] + + +class Source(BaseModel): + auth_provider: Literal["nango", "unified", "whitelabel"] + + capabilities: List[ + Literal["list_resources", "fetch_resource", "search_live", "passthrough", "resolve", "query_structured"] + ] + + source: Union[ + Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ], + str, + ] + + +class LiveListSourcesResponse(BaseModel): + sources: List[Source] diff --git a/src/hyperspell/types/live_search_params.py b/src/hyperspell/types/live_search_params.py new file mode 100644 index 00000000..cfb05b07 --- /dev/null +++ b/src/hyperspell/types/live_search_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["LiveSearchParams"] + + +class LiveSearchParams(TypedDict, total=False): + query: Required[str] + """Live search query.""" + + connection_id: Optional[str] + """Specific connection id when the user has multiple for this source.""" + + index: bool + """If true, queue each hit for indexing so it's on-hand next time.""" diff --git a/src/hyperspell/types/live_search_response.py b/src/hyperspell/types/live_search_response.py new file mode 100644 index 00000000..85161682 --- /dev/null +++ b/src/hyperspell/types/live_search_response.py @@ -0,0 +1,282 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List as TypingList, Union, Optional +from datetime import datetime +from typing_extensions import Literal, Annotated, TypeAlias + +from .._utils import PropertyInfo +from .._models import BaseModel +from .shared.blob import Blob +from .shared.code import Code +from .shared.link import Link +from .shared.text import Text +from .shared.image import Image +from .shared.trace import Trace +from .shared.comment import Comment +from .shared.divider import Divider +from .shared.metadata import Metadata +from .shared.tool_call import ToolCall +from .shared.line_break import LineBreak +from .shared.tool_result import ToolResult +from .shared.trace_message import TraceMessage + +__all__ = [ + "LiveSearchResponse", + "Document", + "DocumentDocument", + "DocumentDocumentInvoice", + "DocumentDocumentInvoiceChild", + "DocumentChunk", +] + +DocumentDocumentInvoiceChild: TypeAlias = Annotated[ + Union[ + Blob, + "Callout", + "Chunk", + Code, + Comment, + Divider, + "Equation", + "Footnote", + "Heading", + Image, + Link, + LineBreak, + "SharedList", + "ListItem", + "Page", + "Paragraph", + "Quote", + "Table", + "TableCell", + "TableRow", + Text, + "ToDo", + ToolCall, + ToolResult, + TraceMessage, + "Utterance", + ], + PropertyInfo(discriminator="type"), +] + + +class DocumentDocumentInvoice(BaseModel): + """A customer invoice, vendor bill, or credit memo. + + Line items are included in ``children``. + """ + + id: Optional[str] = None + + attachment_names: Optional[TypingList[str]] = None + + balance_amount: Optional[float] = None + + cancelled_at: Optional[datetime] = None + + children: Optional[TypingList[DocumentDocumentInvoiceChild]] = None + + contact_id: Optional[str] = None + + contact_name: Optional[str] = None + + currency: Optional[str] = None + + due_at: Optional[datetime] = None + + invoice_type: Optional[str] = None + + metadata: Optional[Metadata] = None + """Optional annotations carried by a hyperdoc node. + + Includes source provenance and human edit attribution. Unset metadata is omitted + from serialized responses. + """ + + notes: Optional[str] = None + + number: Optional[str] = None + + organization_id: Optional[str] = None + + paid_amount: Optional[float] = None + + paid_at: Optional[datetime] = None + + posted_at: Optional[datetime] = None + + reference: Optional[str] = None + + refund_amount: Optional[float] = None + + refund_reason: Optional[str] = None + + refunded_at: Optional[datetime] = None + + status: Optional[str] = None + + tax_amount: Optional[float] = None + + text: Optional[str] = None + + total_amount: Optional[float] = None + + type: Optional[Literal["invoice"]] = None + + +DocumentDocument: TypeAlias = Annotated[ + Union[ + "document.Document", + "Website", + "Task", + "Person", + "Message", + "Event", + "File", + "Conversation", + Trace, + "Transcript", + "Company", + "Deal", + DocumentDocumentInvoice, + ], + PropertyInfo(discriminator="type"), +] + + +class DocumentChunk(BaseModel): + """A searchable chunk extracted from a document during ingestion. + + `summary` is null when no summary was generated for the chunk. + """ + + chunk_id: str + """Stable identifier of the chunk.""" + + summary: Optional[str] = None + """LLM-generated summary of the chunk, if one was produced.""" + + +class Document(BaseModel): + """A document-shaped API response containing the hyperdoc tree.""" + + document: DocumentDocument + """The full hyperdoc tree. + + Switch on `type` for the document frame and recurse through `children` for the + body. + """ + + resource_id: str + + source: Literal[ + "reddit", + "notion", + "slack", + "google_calendar", + "google_mail", + "imap", + "google_meet", + "box", + "dropbox", + "github", + "gitlab", + "google_drive", + "vault", + "web_crawler", + "trace", + "microsoft_outlook", + "microsoft_teams", + "granola", + "fathom", + "fireflies", + "figma", + "linear", + "hubspot", + "salesforce", + "coda", + "confluence", + "jira", + "metabase", + "gong", + "clickup", + "lightfield", + "pylon", + "fellow", + "odoo", + "external_mcp", + ] + + type: str + """Hyperdoc document type discriminator (document, message, file, event, ...).""" + + chunks: Optional[TypingList[DocumentChunk]] = None + """Extracted memories (chunks with summaries) for this document, in document order. + + Present only when explicitly requested via `include_chunks`; omitted otherwise. + """ + + collection: Optional[str] = None + """The document's collection, if any.""" + + document_date: Optional[datetime] = None + """The document's own date (e.g. email sent date, event date).""" + + ingested_at: Optional[datetime] = None + """When Hyperspell first indexed the document.""" + + last_modified_at: Optional[datetime] = None + """When the source document was last modified, if supplied by the source.""" + + metadata: Optional[Dict[str, object]] = None + """Filterable custom metadata attached to the document.""" + + status: Optional[ + Literal["pending", "processing", "completed", "failed", "pending_review", "skipped", "filtered", "cancelled"] + ] = None + """Indexing status of the document.""" + + title: Optional[str] = None + """Human-readable document title.""" + + +class LiveSearchResponse(BaseModel): + """A fetch/search result: the live documents plus what happened to them.""" + + documents: TypingList[Document] + + indexed: Optional[bool] = None + + notes: Optional[TypingList[str]] = None + + +from .shared import document +from .shared.deal import Deal +from .shared.file import File +from .shared.list import List as SharedList +from .shared.page import Page +from .shared.task import Task +from .shared.chunk import Chunk +from .shared.event import Event +from .shared.quote import Quote +from .shared.table import Table +from .shared.to_do import ToDo +from .shared.person import Person +from .shared.callout import Callout +from .shared.company import Company +from .shared.heading import Heading +from .shared.message import Message +from .shared.website import Website +from .shared.equation import Equation +from .shared.footnote import Footnote +from .shared.list_item import ListItem +from .shared.paragraph import Paragraph +from .shared.table_row import TableRow +from .shared.utterance import Utterance +from .shared.table_cell import TableCell +from .shared.transcript import Transcript +from .shared.conversation import Conversation diff --git a/tests/api_resources/context_documents/__init__.py b/tests/api_resources/context_documents/__init__.py deleted file mode 100644 index fd8019a9..00000000 --- a/tests/api_resources/context_documents/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/context_documents/test_config.py b/tests/api_resources/context_documents/test_config.py deleted file mode 100644 index 1ba55a06..00000000 --- a/tests/api_resources/context_documents/test_config.py +++ /dev/null @@ -1,238 +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 hyperspell import Hyperspell, AsyncHyperspell -from tests.utils import assert_matches_type -from hyperspell.types.context_documents import ( - ConfigGetResponse, - ConfigResetResponse, - ConfigUpdateResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestConfig: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_update(self, client: Hyperspell) -> None: - config = client.context_documents.config.update() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - def test_method_update_with_all_params(self, client: Hyperspell) -> None: - config = client.context_documents.config.update( - company_prompts={"foo": "string"}, - detection_prompt="detection_prompt", - domain="domain", - personal_prompt="personal_prompt", - source_weights={"foo": "string"}, - structure={ - "company": [ - { - "filename": "filename", - "key": "key", - "prompt": "prompt", - "search_queries": ["string"], - } - ], - "workstream": [ - { - "filename": "filename", - "key": "key", - "prompt": "prompt", - "search_queries": ["string"], - } - ], - }, - workstream_prompts={"foo": "string"}, - ) - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - def test_raw_response_update(self, client: Hyperspell) -> None: - response = client.context_documents.config.with_raw_response.update() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = response.parse() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - def test_streaming_response_update(self, client: Hyperspell) -> None: - with client.context_documents.config.with_streaming_response.update() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = response.parse() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_get(self, client: Hyperspell) -> None: - config = client.context_documents.config.get() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - @parametrize - def test_raw_response_get(self, client: Hyperspell) -> None: - response = client.context_documents.config.with_raw_response.get() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = response.parse() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - @parametrize - def test_streaming_response_get(self, client: Hyperspell) -> None: - with client.context_documents.config.with_streaming_response.get() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = response.parse() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_reset(self, client: Hyperspell) -> None: - config = client.context_documents.config.reset() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - @parametrize - def test_raw_response_reset(self, client: Hyperspell) -> None: - response = client.context_documents.config.with_raw_response.reset() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = response.parse() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - @parametrize - def test_streaming_response_reset(self, client: Hyperspell) -> None: - with client.context_documents.config.with_streaming_response.reset() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = response.parse() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncConfig: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_update(self, async_client: AsyncHyperspell) -> None: - config = await async_client.context_documents.config.update() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncHyperspell) -> None: - config = await async_client.context_documents.config.update( - company_prompts={"foo": "string"}, - detection_prompt="detection_prompt", - domain="domain", - personal_prompt="personal_prompt", - source_weights={"foo": "string"}, - structure={ - "company": [ - { - "filename": "filename", - "key": "key", - "prompt": "prompt", - "search_queries": ["string"], - } - ], - "workstream": [ - { - "filename": "filename", - "key": "key", - "prompt": "prompt", - "search_queries": ["string"], - } - ], - }, - workstream_prompts={"foo": "string"}, - ) - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - async def test_raw_response_update(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.config.with_raw_response.update() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = await response.parse() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - @parametrize - async def test_streaming_response_update(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.config.with_streaming_response.update() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = await response.parse() - assert_matches_type(ConfigUpdateResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_get(self, async_client: AsyncHyperspell) -> None: - config = await async_client.context_documents.config.get() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - @parametrize - async def test_raw_response_get(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.config.with_raw_response.get() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = await response.parse() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - @parametrize - async def test_streaming_response_get(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.config.with_streaming_response.get() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = await response.parse() - assert_matches_type(ConfigGetResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_reset(self, async_client: AsyncHyperspell) -> None: - config = await async_client.context_documents.config.reset() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - @parametrize - async def test_raw_response_reset(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.config.with_raw_response.reset() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - config = await response.parse() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - @parametrize - async def test_streaming_response_reset(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.config.with_streaming_response.reset() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - config = await response.parse() - assert_matches_type(ConfigResetResponse, config, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/context_documents/test_digests.py b/tests/api_resources/context_documents/test_digests.py deleted file mode 100644 index c895de3e..00000000 --- a/tests/api_resources/context_documents/test_digests.py +++ /dev/null @@ -1,164 +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 hyperspell import Hyperspell, AsyncHyperspell -from tests.utils import assert_matches_type -from hyperspell._utils import parse_datetime -from hyperspell.types.context_documents import ( - DigestListResponse, - DigestGenerateResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestDigests: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_list(self, client: Hyperspell) -> None: - digest = client.context_documents.digests.list() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - def test_method_list_with_all_params(self, client: Hyperspell) -> None: - digest = client.context_documents.digests.list( - limit=0, - period="period", - ) - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: Hyperspell) -> None: - response = client.context_documents.digests.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - digest = response.parse() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: Hyperspell) -> None: - with client.context_documents.digests.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - digest = response.parse() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_generate(self, client: Hyperspell) -> None: - digest = client.context_documents.digests.generate() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - def test_method_generate_with_all_params(self, client: Hyperspell) -> None: - digest = client.context_documents.digests.generate( - period="period", - sources=["string"], - window_end=parse_datetime("2019-12-27T18:11:19.117Z"), - window_start=parse_datetime("2019-12-27T18:11:19.117Z"), - ) - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - def test_raw_response_generate(self, client: Hyperspell) -> None: - response = client.context_documents.digests.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - digest = response.parse() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - def test_streaming_response_generate(self, client: Hyperspell) -> None: - with client.context_documents.digests.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - digest = response.parse() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncDigests: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_list(self, async_client: AsyncHyperspell) -> None: - digest = await async_client.context_documents.digests.list() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncHyperspell) -> None: - digest = await async_client.context_documents.digests.list( - limit=0, - period="period", - ) - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.digests.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - digest = await response.parse() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.digests.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - digest = await response.parse() - assert_matches_type(DigestListResponse, digest, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_generate(self, async_client: AsyncHyperspell) -> None: - digest = await async_client.context_documents.digests.generate() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - async def test_method_generate_with_all_params(self, async_client: AsyncHyperspell) -> None: - digest = await async_client.context_documents.digests.generate( - period="period", - sources=["string"], - window_end=parse_datetime("2019-12-27T18:11:19.117Z"), - window_start=parse_datetime("2019-12-27T18:11:19.117Z"), - ) - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - async def test_raw_response_generate(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.digests.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - digest = await response.parse() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - @parametrize - async def test_streaming_response_generate(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.digests.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - digest = await response.parse() - assert_matches_type(DigestGenerateResponse, digest, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/context_documents/test_trees.py b/tests/api_resources/context_documents/test_trees.py deleted file mode 100644 index 835953ec..00000000 --- a/tests/api_resources/context_documents/test_trees.py +++ /dev/null @@ -1,313 +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 hyperspell import Hyperspell, AsyncHyperspell -from tests.utils import assert_matches_type -from hyperspell.types.context_documents import ( - TreeGetResponse, - TreeGenerateResponse, - TreeProgressResponse, - TreeGetLatestResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestTrees: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_generate(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.generate() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - def test_method_generate_with_all_params(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.generate( - sources=["string"], - user_id="user_id", - workstream_name="workstream_name", - ) - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - def test_raw_response_generate(self, client: Hyperspell) -> None: - response = client.context_documents.trees.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = response.parse() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - def test_streaming_response_generate(self, client: Hyperspell) -> None: - with client.context_documents.trees.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = response.parse() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_get(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.get( - "tree_id", - ) - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - @parametrize - def test_raw_response_get(self, client: Hyperspell) -> None: - response = client.context_documents.trees.with_raw_response.get( - "tree_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = response.parse() - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - @parametrize - def test_streaming_response_get(self, client: Hyperspell) -> None: - with client.context_documents.trees.with_streaming_response.get( - "tree_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = response.parse() - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_get(self, client: Hyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `tree_id` but received ''"): - client.context_documents.trees.with_raw_response.get( - "", - ) - - @parametrize - def test_method_get_latest(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.get_latest() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - def test_method_get_latest_with_all_params(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.get_latest( - status="status", - ) - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - def test_raw_response_get_latest(self, client: Hyperspell) -> None: - response = client.context_documents.trees.with_raw_response.get_latest() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = response.parse() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - def test_streaming_response_get_latest(self, client: Hyperspell) -> None: - with client.context_documents.trees.with_streaming_response.get_latest() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = response.parse() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_progress(self, client: Hyperspell) -> None: - tree = client.context_documents.trees.progress( - "tree_id", - ) - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - @parametrize - def test_raw_response_progress(self, client: Hyperspell) -> None: - response = client.context_documents.trees.with_raw_response.progress( - "tree_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = response.parse() - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - @parametrize - def test_streaming_response_progress(self, client: Hyperspell) -> None: - with client.context_documents.trees.with_streaming_response.progress( - "tree_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = response.parse() - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_progress(self, client: Hyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `tree_id` but received ''"): - client.context_documents.trees.with_raw_response.progress( - "", - ) - - -class TestAsyncTrees: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_generate(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.generate() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - async def test_method_generate_with_all_params(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.generate( - sources=["string"], - user_id="user_id", - workstream_name="workstream_name", - ) - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - async def test_raw_response_generate(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.trees.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = await response.parse() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - @parametrize - async def test_streaming_response_generate(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.trees.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = await response.parse() - assert_matches_type(TreeGenerateResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_get(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.get( - "tree_id", - ) - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - @parametrize - async def test_raw_response_get(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.trees.with_raw_response.get( - "tree_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = await response.parse() - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - @parametrize - async def test_streaming_response_get(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.trees.with_streaming_response.get( - "tree_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = await response.parse() - assert_matches_type(TreeGetResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_get(self, async_client: AsyncHyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `tree_id` but received ''"): - await async_client.context_documents.trees.with_raw_response.get( - "", - ) - - @parametrize - async def test_method_get_latest(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.get_latest() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - async def test_method_get_latest_with_all_params(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.get_latest( - status="status", - ) - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - async def test_raw_response_get_latest(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.trees.with_raw_response.get_latest() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = await response.parse() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - @parametrize - async def test_streaming_response_get_latest(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.trees.with_streaming_response.get_latest() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = await response.parse() - assert_matches_type(TreeGetLatestResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_progress(self, async_client: AsyncHyperspell) -> None: - tree = await async_client.context_documents.trees.progress( - "tree_id", - ) - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - @parametrize - async def test_raw_response_progress(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.trees.with_raw_response.progress( - "tree_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - tree = await response.parse() - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - @parametrize - async def test_streaming_response_progress(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.trees.with_streaming_response.progress( - "tree_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - tree = await response.parse() - assert_matches_type(TreeProgressResponse, tree, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_progress(self, async_client: AsyncHyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `tree_id` but received ''"): - await async_client.context_documents.trees.with_raw_response.progress( - "", - ) diff --git a/tests/api_resources/test_context_documents.py b/tests/api_resources/test_context_documents.py deleted file mode 100644 index f22e9c93..00000000 --- a/tests/api_resources/test_context_documents.py +++ /dev/null @@ -1,259 +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 hyperspell import Hyperspell, AsyncHyperspell -from tests.utils import assert_matches_type -from hyperspell.types import ( - ContextDocumentGetResponse, - ContextDocumentListResponse, - ContextDocumentGenerateResponse, -) -from hyperspell.pagination import SyncContextDocumentsCursorPage, AsyncContextDocumentsCursorPage - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestContextDocuments: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_list(self, client: Hyperspell) -> None: - context_document = client.context_documents.list() - assert_matches_type( - SyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - def test_method_list_with_all_params(self, client: Hyperspell) -> None: - context_document = client.context_documents.list( - cursor="cursor", - limit=0, - status="processing", - ) - assert_matches_type( - SyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - def test_raw_response_list(self, client: Hyperspell) -> None: - response = client.context_documents.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = response.parse() - assert_matches_type( - SyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - def test_streaming_response_list(self, client: Hyperspell) -> None: - with client.context_documents.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = response.parse() - assert_matches_type( - SyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_generate(self, client: Hyperspell) -> None: - context_document = client.context_documents.generate() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - def test_method_generate_with_all_params(self, client: Hyperspell) -> None: - context_document = client.context_documents.generate( - model="model", - prompt="prompt", - sources=["string"], - user_id="user_id", - ) - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - def test_raw_response_generate(self, client: Hyperspell) -> None: - response = client.context_documents.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = response.parse() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - def test_streaming_response_generate(self, client: Hyperspell) -> None: - with client.context_documents.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = response.parse() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_get(self, client: Hyperspell) -> None: - context_document = client.context_documents.get( - "document_id", - ) - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - @parametrize - def test_raw_response_get(self, client: Hyperspell) -> None: - response = client.context_documents.with_raw_response.get( - "document_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = response.parse() - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - @parametrize - def test_streaming_response_get(self, client: Hyperspell) -> None: - with client.context_documents.with_streaming_response.get( - "document_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = response.parse() - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_get(self, client: Hyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_id` but received ''"): - client.context_documents.with_raw_response.get( - "", - ) - - -class TestAsyncContextDocuments: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_list(self, async_client: AsyncHyperspell) -> None: - context_document = await async_client.context_documents.list() - assert_matches_type( - AsyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncHyperspell) -> None: - context_document = await async_client.context_documents.list( - cursor="cursor", - limit=0, - status="processing", - ) - assert_matches_type( - AsyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = await response.parse() - assert_matches_type( - AsyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = await response.parse() - assert_matches_type( - AsyncContextDocumentsCursorPage[ContextDocumentListResponse], context_document, path=["response"] - ) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_generate(self, async_client: AsyncHyperspell) -> None: - context_document = await async_client.context_documents.generate() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - async def test_method_generate_with_all_params(self, async_client: AsyncHyperspell) -> None: - context_document = await async_client.context_documents.generate( - model="model", - prompt="prompt", - sources=["string"], - user_id="user_id", - ) - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - async def test_raw_response_generate(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.with_raw_response.generate() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = await response.parse() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - @parametrize - async def test_streaming_response_generate(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.with_streaming_response.generate() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = await response.parse() - assert_matches_type(ContextDocumentGenerateResponse, context_document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_get(self, async_client: AsyncHyperspell) -> None: - context_document = await async_client.context_documents.get( - "document_id", - ) - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - @parametrize - async def test_raw_response_get(self, async_client: AsyncHyperspell) -> None: - response = await async_client.context_documents.with_raw_response.get( - "document_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - context_document = await response.parse() - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - @parametrize - async def test_streaming_response_get(self, async_client: AsyncHyperspell) -> None: - async with async_client.context_documents.with_streaming_response.get( - "document_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - context_document = await response.parse() - assert_matches_type(ContextDocumentGetResponse, context_document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_get(self, async_client: AsyncHyperspell) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `document_id` but received ''"): - await async_client.context_documents.with_raw_response.get( - "", - ) diff --git a/tests/api_resources/test_entities.py b/tests/api_resources/test_entities.py new file mode 100644 index 00000000..61c0f4f9 --- /dev/null +++ b/tests/api_resources/test_entities.py @@ -0,0 +1,265 @@ +# 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 hyperspell import Hyperspell, AsyncHyperspell +from tests.utils import assert_matches_type +from hyperspell.types import ( + EntityGetResponse, + EntityListResponse, + EntitySearchResponse, +) +from hyperspell.pagination import SyncEntityCursorPage, AsyncEntityCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEntities: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: Hyperspell) -> None: + entity = client.entities.list() + assert_matches_type(SyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Hyperspell) -> None: + entity = client.entities.list( + cursor="cursor", + limit=1, + min_supporting_documents=0, + name_prefix="name_prefix", + search="search", + sort_by="id", + sort_dir="asc", + status="provisional", + type="type", + ) + assert_matches_type(SyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Hyperspell) -> None: + response = client.entities.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = response.parse() + assert_matches_type(SyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Hyperspell) -> None: + with client.entities.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = response.parse() + assert_matches_type(SyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_get(self, client: Hyperspell) -> None: + entity = client.entities.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Hyperspell) -> None: + response = client.entities.with_raw_response.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = response.parse() + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Hyperspell) -> None: + with client.entities.with_streaming_response.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = response.parse() + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Hyperspell) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `entity_id` but received ''"): + client.entities.with_raw_response.get( + "", + ) + + @parametrize + def test_method_search(self, client: Hyperspell) -> None: + entity = client.entities.search( + query="query", + ) + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + def test_method_search_with_all_params(self, client: Hyperspell) -> None: + entity = client.entities.search( + query="query", + limit=1, + type="type", + ) + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + def test_raw_response_search(self, client: Hyperspell) -> None: + response = client.entities.with_raw_response.search( + query="query", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = response.parse() + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + def test_streaming_response_search(self, client: Hyperspell) -> None: + with client.entities.with_streaming_response.search( + query="query", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = response.parse() + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncEntities: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncHyperspell) -> None: + entity = await async_client.entities.list() + assert_matches_type(AsyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncHyperspell) -> None: + entity = await async_client.entities.list( + cursor="cursor", + limit=1, + min_supporting_documents=0, + name_prefix="name_prefix", + search="search", + sort_by="id", + sort_dir="asc", + status="provisional", + type="type", + ) + assert_matches_type(AsyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncHyperspell) -> None: + response = await async_client.entities.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = await response.parse() + assert_matches_type(AsyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncHyperspell) -> None: + async with async_client.entities.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = await response.parse() + assert_matches_type(AsyncEntityCursorPage[EntityListResponse], entity, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get(self, async_client: AsyncHyperspell) -> None: + entity = await async_client.entities.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncHyperspell) -> None: + response = await async_client.entities.with_raw_response.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = await response.parse() + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncHyperspell) -> None: + async with async_client.entities.with_streaming_response.get( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = await response.parse() + assert_matches_type(EntityGetResponse, entity, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncHyperspell) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `entity_id` but received ''"): + await async_client.entities.with_raw_response.get( + "", + ) + + @parametrize + async def test_method_search(self, async_client: AsyncHyperspell) -> None: + entity = await async_client.entities.search( + query="query", + ) + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + async def test_method_search_with_all_params(self, async_client: AsyncHyperspell) -> None: + entity = await async_client.entities.search( + query="query", + limit=1, + type="type", + ) + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + async def test_raw_response_search(self, async_client: AsyncHyperspell) -> None: + response = await async_client.entities.with_raw_response.search( + query="query", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + entity = await response.parse() + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + @parametrize + async def test_streaming_response_search(self, async_client: AsyncHyperspell) -> None: + async with async_client.entities.with_streaming_response.search( + query="query", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + entity = await response.parse() + assert_matches_type(EntitySearchResponse, entity, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_live.py b/tests/api_resources/test_live.py new file mode 100644 index 00000000..07d226a7 --- /dev/null +++ b/tests/api_resources/test_live.py @@ -0,0 +1,354 @@ +# 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 hyperspell import Hyperspell, AsyncHyperspell +from tests.utils import assert_matches_type +from hyperspell.types import ( + LiveSearchResponse, + LiveGetResourceResponse, + LiveListSourcesResponse, + LiveListResourcesResponse, +) +from hyperspell.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLive: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_get_resource(self, client: Hyperspell) -> None: + live = client.live.get_resource( + resource_id="resource_id", + source="reddit", + ) + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + def test_method_get_resource_with_all_params(self, client: Hyperspell) -> None: + live = client.live.get_resource( + resource_id="resource_id", + source="reddit", + connection_id="connection_id", + index=True, + ) + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + def test_raw_response_get_resource(self, client: Hyperspell) -> None: + response = client.live.with_raw_response.get_resource( + resource_id="resource_id", + source="reddit", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = response.parse() + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + def test_streaming_response_get_resource(self, client: Hyperspell) -> None: + with client.live.with_streaming_response.get_resource( + resource_id="resource_id", + source="reddit", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = response.parse() + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get_resource(self, client: Hyperspell) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): + client.live.with_raw_response.get_resource( + resource_id="", + source="reddit", + ) + + @parametrize + def test_method_list_resources(self, client: Hyperspell) -> None: + live = client.live.list_resources( + source="reddit", + ) + assert_matches_type(SyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + def test_method_list_resources_with_all_params(self, client: Hyperspell) -> None: + live = client.live.list_resources( + source="reddit", + connection_id="connection_id", + cursor="cursor", + size=0, + ) + assert_matches_type(SyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + def test_raw_response_list_resources(self, client: Hyperspell) -> None: + response = client.live.with_raw_response.list_resources( + source="reddit", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = response.parse() + assert_matches_type(SyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + def test_streaming_response_list_resources(self, client: Hyperspell) -> None: + with client.live.with_streaming_response.list_resources( + source="reddit", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = response.parse() + assert_matches_type(SyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_list_sources(self, client: Hyperspell) -> None: + live = client.live.list_sources() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + @parametrize + def test_raw_response_list_sources(self, client: Hyperspell) -> None: + response = client.live.with_raw_response.list_sources() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = response.parse() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + @parametrize + def test_streaming_response_list_sources(self, client: Hyperspell) -> None: + with client.live.with_streaming_response.list_sources() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = response.parse() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_search(self, client: Hyperspell) -> None: + live = client.live.search( + source="reddit", + query="query", + ) + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + def test_method_search_with_all_params(self, client: Hyperspell) -> None: + live = client.live.search( + source="reddit", + query="query", + connection_id="connection_id", + index=True, + ) + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + def test_raw_response_search(self, client: Hyperspell) -> None: + response = client.live.with_raw_response.search( + source="reddit", + query="query", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = response.parse() + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + def test_streaming_response_search(self, client: Hyperspell) -> None: + with client.live.with_streaming_response.search( + source="reddit", + query="query", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = response.parse() + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncLive: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_get_resource(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.get_resource( + resource_id="resource_id", + source="reddit", + ) + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + async def test_method_get_resource_with_all_params(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.get_resource( + resource_id="resource_id", + source="reddit", + connection_id="connection_id", + index=True, + ) + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + async def test_raw_response_get_resource(self, async_client: AsyncHyperspell) -> None: + response = await async_client.live.with_raw_response.get_resource( + resource_id="resource_id", + source="reddit", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = await response.parse() + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + @parametrize + async def test_streaming_response_get_resource(self, async_client: AsyncHyperspell) -> None: + async with async_client.live.with_streaming_response.get_resource( + resource_id="resource_id", + source="reddit", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = await response.parse() + assert_matches_type(LiveGetResourceResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get_resource(self, async_client: AsyncHyperspell) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): + await async_client.live.with_raw_response.get_resource( + resource_id="", + source="reddit", + ) + + @parametrize + async def test_method_list_resources(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.list_resources( + source="reddit", + ) + assert_matches_type(AsyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + async def test_method_list_resources_with_all_params(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.list_resources( + source="reddit", + connection_id="connection_id", + cursor="cursor", + size=0, + ) + assert_matches_type(AsyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + async def test_raw_response_list_resources(self, async_client: AsyncHyperspell) -> None: + response = await async_client.live.with_raw_response.list_resources( + source="reddit", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = await response.parse() + assert_matches_type(AsyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + @parametrize + async def test_streaming_response_list_resources(self, async_client: AsyncHyperspell) -> None: + async with async_client.live.with_streaming_response.list_resources( + source="reddit", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = await response.parse() + assert_matches_type(AsyncCursorPage[LiveListResourcesResponse], live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_list_sources(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.list_sources() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + @parametrize + async def test_raw_response_list_sources(self, async_client: AsyncHyperspell) -> None: + response = await async_client.live.with_raw_response.list_sources() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = await response.parse() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + @parametrize + async def test_streaming_response_list_sources(self, async_client: AsyncHyperspell) -> None: + async with async_client.live.with_streaming_response.list_sources() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = await response.parse() + assert_matches_type(LiveListSourcesResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_search(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.search( + source="reddit", + query="query", + ) + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + async def test_method_search_with_all_params(self, async_client: AsyncHyperspell) -> None: + live = await async_client.live.search( + source="reddit", + query="query", + connection_id="connection_id", + index=True, + ) + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + async def test_raw_response_search(self, async_client: AsyncHyperspell) -> None: + response = await async_client.live.with_raw_response.search( + source="reddit", + query="query", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + live = await response.parse() + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + @parametrize + async def test_streaming_response_search(self, async_client: AsyncHyperspell) -> None: + async with async_client.live.with_streaming_response.search( + source="reddit", + query="query", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + live = await response.parse() + assert_matches_type(LiveSearchResponse, live, path=["response"]) + + assert cast(Any, response.is_closed) is True