diff --git a/docs/fern_integration.md b/docs/fern_integration.md new file mode 100644 index 000000000..4812c2281 --- /dev/null +++ b/docs/fern_integration.md @@ -0,0 +1,199 @@ +# Fern integration: custom auth + Vertex path routing + +This doc explains how two pieces of custom functionality landed for the existing Stainless `_interactions/` SDK are mirrored for the new Fern `_fern_interactions/` SDK in `google/genai/client.py`. + +The Stainless and Fern paths share the same `BaseApiClient` (API key / OAuth credentials / project / location) but each SDK has its own seam for plugging dynamic auth and Vertex URL routing in. The behaviour on the wire is identical. + +--- + +## 1. Per-request auth injection + +### Stainless seam + +`_interactions/_client_adapter.py` defines abstract `GeminiNextGenAPIClientAdapter` / `AsyncGeminiNextGenAPIClientAdapter` with: + +- `is_vertex_ai()` +- `get_project()` +- `get_location()` +- `get_auth_headers()` / `async_get_auth_headers()` + +The concrete subclass passed into `GeminiNextGenAPIClient(client_adapter=...)` lives in `client.py`. The Stainless SDK's `_prepare_options` override calls `client_adapter.get_auth_headers()` per request, merging the result into the outgoing `options.headers`. + +### Fern seam + +Fern's `SyncClientWrapper` / `AsyncClientWrapper` pass their own `get_headers` (sync) and `async_get_headers` (async) as **callables** to the underlying `HttpClient` — the callable is invoked **per request** to produce the header dict. + +We subclass both wrappers and override those callables: + +```python +# google/genai/client.py +class _FernGenAiSyncClientWrapper(_FernSyncWrapper): + def __init__(self, *, api_client: BaseApiClient, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._api_client = api_client + + def get_headers(self) -> dict[str, str]: + headers = super().get_headers() + if self._api_client.api_key: + headers['x-goog-api-key'] = self._api_client.api_key + return headers + headers.pop('x-goog-api-key', None) + token = self._api_client._access_token() + headers['Authorization'] = f'Bearer {token}' + if (creds := self._api_client._credentials) and creds.quota_project_id: + headers['x-goog-user-project'] = creds.quota_project_id + return headers + + +class _FernGenAiAsyncClientWrapper(_FernAsyncWrapper): + async def async_get_headers(self) -> dict[str, str]: + headers = self.get_headers() + if not self._api_client.api_key: + token = await self._api_client._async_access_token() + headers['Authorization'] = f'Bearer {token}' + if (creds := self._api_client._credentials) and creds.quota_project_id: + headers['x-goog-user-project'] = creds.quota_project_id + return headers +``` + +Fern's `GeminiNextGenAPIClient.__init__` always builds its own `SyncClientWrapper` first. We immediately swap it for our subclass and clear the lazily-built resource caches so the new wrapper takes effect: + +```python +# _build_sync_fern_nextgen, client.py +nextgen = _FernSyncClient(base_url=..., api_key=api_key, api_version=wrapper_api_version, ...) +nextgen._client_wrapper = _FernGenAiSyncClientWrapper( + api_client=api_client, + base_url=base_url, + api_version=wrapper_api_version, + api_key=api_key, + headers=http_opts.headers, + httpx_client=api_client._httpx_client, + ... +) +nextgen._fern_interactions = None +nextgen._fern_webhooks = None +nextgen._fern_agents = None +``` + +### API-key placeholder + +Fern's `GeminiNextGenAPIClient.__init__` raises if `api_key is None`. When running on Vertex we pass the sentinel string `"vertex-oauth"`. Our `get_headers()` override pops `x-goog-api-key` from the base headers before adding the Bearer token, so the placeholder never reaches the wire. + +### Behaviour parity + +| Concern | Result | +|---|---| +| Gemini sends `x-goog-api-key` | yes (`test_fern_auth.py::test_fern_interactions_gemini_url`) | +| Vertex sends `Authorization: Bearer ` | yes (`test_fern_interactions_vertex_auth_header`) | +| Vertex sends `x-goog-user-project` when creds carry quota | yes | +| Token refresh per retry | yes (`test_fern_interactions_vertex_auth_refresh_on_retry`) — `_access_token()` invoked on every call | +| Gemini path skips OAuth fetch | yes (`test_fern_interactions_gemini_no_vertex_auth`) — `_access_token()` never called | + +--- + +## 2. Vertex path routing + +### Stainless seam + +`_interactions/_base_client.py:418` — `_build_maybe_vertex_path` is called **per request** by every resource method: + +```python +def _build_maybe_vertex_path(self, *, api_version: str, path: str) -> str: + if not self._is_vertex or not self._vertex_location or not self._vertex_project: + return f'/{api_version}/{path}' + return f'{api_version}/projects/{self._vertex_project}/locations/{self._vertex_location}/{path}' +``` + +The base_url stays at `https://{location}-aiplatform.googleapis.com/`, the per-call path embeds project + location. + +### Fern seam + +Fern's generated raw clients now interpolate a **single** version segment from the wrapper: + +```python +# _fern_interactions/fern_interactions/raw_client.py +self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions", + method="POST", + ... +) +``` + +`encode_path_param` does not URL-encode slashes — `str(jsonable_encoder(obj))`. That means whatever we put in `wrapper._api_version` lands in the path verbatim, and the resource methods (`create`, `get`, `cancel`, etc.) take no `api_version` argument at all. + +Workaround: for Vertex we augment `base_url` with `/{version}/projects/{p}/locations/{l}` at construction time and leave `wrapper._api_version` empty. For Gemini we keep `base_url` at the host root and put the version in `wrapper._api_version`. Either way the wire URL ends up correct without any per-call boilerplate. + +```python +# client.py +def _fern_base_url(api_client: BaseApiClient) -> str: + http_opts = api_client._http_options + if api_client.vertexai: + # User already supplied a fully-formed URL with project/location -> trust as-is. + if http_opts.base_url and '/projects/' in http_opts.base_url: + return http_opts.base_url.rstrip('/') + # Trust the host BaseApiClient already picked (handles global, + # multi-regional, api_key+vertex, custom base_url). Append the + # project/location path Fern's per-call template will be joined onto. + host = (http_opts.base_url or f'https://{api_client.location}-aiplatform.googleapis.com').rstrip('/') + version = http_opts.api_version or 'v1beta1' + return f'{host}/{version}/projects/{api_client.project}/locations/{api_client.location}' + if http_opts.base_url: + return _VERSION_SUFFIX.sub('', http_opts.base_url).rstrip('/') + return 'https://generativelanguage.googleapis.com' +``` + +```python +# _build_sync_fern_nextgen, client.py +wrapper_api_version = '' if api_client.vertexai else (http_opts.api_version or 'v1beta') +``` + +### Why the wrapper api_version is empty for Vertex + +If we left `wrapper._api_version = "v1beta1"` for Vertex, the version would appear twice in the URL: + +``` +https://us-central1-aiplatform.googleapis.com/v1beta1/projects/p/locations/us-central1/v1beta1/interactions + ^^^^^^^ ^^^^^^^ + in base_url re-added by Fern +``` + +Empty wrapper api_version makes Fern interpolate an empty string into the path template: + +```python +f"{encode_path_param('')}/interactions" # -> "/interactions" +``` + +`_build_url` then joins that against the pre-augmented Vertex base_url with a single `/`. + +### Final URLs on the wire + +| Surface | base_url | wrapper.api_version | path interpolation | wire URL | +|---|---|---|---|---| +| Gemini | `https://generativelanguage.googleapis.com` | `v1beta` | `v1beta/interactions` | `https://generativelanguage.googleapis.com/v1beta/interactions` | +| Vertex | `https://us-central1-aiplatform.googleapis.com/v1beta1/projects/

/locations/us-central1` | `""` | `/interactions` | `https://us-central1-aiplatform.googleapis.com/v1beta1/projects/

/locations/us-central1/interactions` | +| Vertex `global` | `https://aiplatform.googleapis.com/v1beta1/projects/

/locations/global` | `""` | `/interactions` | `https://aiplatform.googleapis.com/v1beta1/projects/

/locations/global/interactions` | + +Identical to what Stainless produces via `_build_maybe_vertex_path`. + +Verified by: +- `test_fern_paths.py` — sync + async URL assertions +- `test_fern_auth.py::test_fern_interactions_vertex_url` +- `ete-test.py` live run against both Gemini and Vertex global + +--- + +## Tradeoffs + +| | Stainless | Fern | +|---|---|---| +| Auth seam | adapter passed to constructor; `_prepare_options` hook called per-request | `*ClientWrapper` subclass; `get_headers` / `async_get_headers` callbacks invoked per-request | +| Path routing seam | per-request `_build_maybe_vertex_path` | construction-time `_fern_base_url` augments base_url; `wrapper._api_version` interpolated per request by Fern codegen | +| User-supplied `base_url` override on Vertex | trusted as-is | trusted only when it contains `/projects/` (otherwise treated as host root and augmented) | +| Adapter introspection (`is_vertex_ai`, `get_project`, `get_location`) | yes, abstract interface | not exposed — wrapper holds `BaseApiClient` directly | +| Token refresh on retry | yes | yes | +| Resource methods take `api_version` arg | yes (passed as `api_version=`) | no — codegen baked into wrapper | +| Shim layer in `client.py` | none | none (resource clients exposed directly via `Client.fern_interactions` / `fern_webhooks` / `fern_agents`) | + +Functionally equivalent for the POC. Stainless's per-request hooks are more flexible if project/location ever need to vary per request; Fern's per-client baking keeps the hot path simpler. Today neither matters. + +`_fern_base_url` could be eliminated by stuffing `{version}/projects/.../locations/...` straight into `wrapper._api_version` (slashes pass through `encode_path_param` unchanged). Today the function still exists for clarity; refactor is tracked but not blocking. diff --git a/docs/fern_vs_stainless_breaking_changes.md b/docs/fern_vs_stainless_breaking_changes.md new file mode 100644 index 000000000..170ac9cca --- /dev/null +++ b/docs/fern_vs_stainless_breaking_changes.md @@ -0,0 +1,227 @@ +# Breaking changes: `client.fern_*` (Fern) vs `client.` (Stainless `_interactions`) + +This document enumerates the interface deltas between the **Fern‑generated** surface (exposed on `genai.Client` as `client.fern_interactions`, `client.fern_webhooks`, `client.fern_agents` via direct delegation in `google/genai/client.py`) and the **Stainless‑generated** surface (exposed as `client.interactions`, `client.webhooks`, `client.agents` and implemented under `google/genai/_interactions/`). + +Source files compared: + +- Fern client methods: `google/genai/_fern_interactions/fern_interactions/client.py`, `google/genai/_fern_interactions/fern_webhooks/client.py`, `google/genai/_fern_interactions/fern_agents/client.py` +- Fern accessor properties: `google/genai/client.py` (`AsyncClient.fern_interactions`, `SyncClient.fern_interactions`, etc. — direct delegation to `self._fern_nextgen_client.*`, no shim wrapper) +- Stainless resources: `google/genai/_interactions/resources/{interactions,webhooks,agents}.py` + +The comparison is intentionally framed as "if I swap `client.fern_X` for `client.X`, what breaks?" + +> **Note (architecture):** The shim wrapper classes (`_FernInteractionsShim`, `_FernWebhooksShim`, `_FernAgentsShim`) described in earlier versions of this document no longer exist. `client.fern_interactions` now directly returns `self._fern_nextgen_client.fern_interactions` (a `FernInteractionsClient` / `AsyncFernInteractionsClient`). + +--- + +## 1. Per‑method breaking changes + +### 1.1 `interactions` + +| Method | Fern signature (`client.fern_interactions.*`) | Stainless signature (`client.interactions.*`) | Breaking? | +|---|---|---|---| +| `create` | `create(*, request: CreateFernInteractionsRequest, request_options=None)` where `CreateFernInteractionsRequest = Union[CreateModelInteractionParams, CreateAgentInteractionParams]` | `create(*, input, model \| agent, [stream, background, environment, generation_config, response_format, response_mime_type, response_modalities, service_tier, store, system_instruction, tools, webhook_config, agent_config, previous_interaction_id, api_version, extra_headers, extra_query, extra_body, timeout])` with `@required_args(["input","model"], ["input","model","stream"], ["agent","input"], ["agent","input","stream"])` | **Yes — completely different calling convention** | +| `create_stream` | `create_stream(*, request: CreateFernInteractionsStreamRequest, request_options=None) -> Iterator[InteractionSseEvent]` | **does not exist**; use `create(..., stream=True)` which returns `Stream[InteractionSSEEvent]` | **Yes — method missing on Stainless** | +| `get` | `get(id: str, *, stream, last_event_id, include_input, request_options) -> Interaction` | `get(id: str, *, include_input, last_event_id, stream, api_version, extra_*…) -> Interaction \| Stream[InteractionSSEEvent]` | No — `id` positional on both, kwargs mostly align. | +| `get_stream` | `get_stream(id: str, *, stream, last_event_id, include_input, request_options) -> Iterator[InteractionSseEvent]` | **does not exist**; use `get(id, stream=True)` returning `Stream[InteractionSSEEvent]` | **Yes — method missing on Stainless** | +| `cancel` | `cancel(id: str, *, request_options=None) -> Interaction` | `cancel(id, *, api_version, extra_*…) -> Interaction` | No. | +| `delete` | `delete(id: str, *, request_options=None) -> typing.Dict[str, Any]` | `delete(id, *, api_version, extra_*…) -> object` | No (typing‑only). | + +#### Concrete consequences for calling code + +```python +# Fern — pass a typed Pydantic model +from google.genai._fern_interactions.types import CreateModelInteractionParams, TextContent + +client.fern_interactions.create( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent(text="Hello from Fern", type="text"), + ) +) + +# Stainless equivalent — loose kwargs +client.interactions.create(input="Hello from Fern", model="gemini-2.5-computer-use-preview-10-2025") + +# Fern streaming +client.fern_interactions.create_stream( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent(text="Hello", type="text"), + stream=True, + ) +) +# Stainless equivalent +client.interactions.create(input="Hello", model="gemini-2.5-computer-use-preview-10-2025", stream=True) + +# get_stream: Fern +client.fern_interactions.get_stream(created_id) +# Stainless equivalent +client.interactions.get(created_id, stream=True) +``` + +### 1.2 `webhooks` + +| Method | Fern signature | Stainless signature | Breaking? | +|---|---|---|---| +| `list` | `list(*, page_size, page_token, request_options) -> ListWebhooksResponse` | `list(*, page_size, page_token, api_version, extra_*…) -> WebhookListResponse` | Class name only. | +| `create` | `create(*, subscribed_events, uri, name=OMIT, request_options) -> Webhook` | `create(*, subscribed_events, uri, name, api_version, extra_*…) -> Webhook` | No — same required fields. | +| `get` | `get(id: str, *, request_options) -> Webhook` | `get(id, *, api_version, extra_*…) -> Webhook` | No. | +| `update` | `update(id: str, *, update_mask, name, state, subscribed_events, uri, request_options) -> Webhook` | `update(id, *, update_mask, name, state, subscribed_events, uri, api_version, extra_*…) -> Webhook` | No — fields match. | +| `rotate_signing_secret` | `rotate_signing_secret(id: str, *, revocation_behavior=OMIT, request_options) -> RotateSigningSecretResponse` | `rotate_signing_secret(id, *, revocation_behavior, api_version, extra_*…) -> WebhookRotateSigningSecretResponse` | Class name only. | +| `ping` | `ping(id: str, *, request: PingWebhookRequest, request_options) -> PingWebhookResponse` | `ping(id, *, body: webhook_ping_params.Body \| Omit = omit, api_version, extra_*…) -> WebhookPingResponse` | **Yes — kwarg named `request` on Fern vs `body` on Stainless. Also required on Fern, optional on Stainless.** | +| `delete` | `delete(id: str, *, request_options) -> Empty` | `delete(id, *, api_version, extra_*…) -> WebhookDeleteResponse` | Class name only. | + +#### Concrete consequence + +```python +# Fern (current) +client.fern_webhooks.ping(id=webhook_id, request={}) +# Stainless equivalent +client.webhooks.ping(id=webhook_id, body={}) +# Calling Stainless with `request={}` raises: +# TypeError: ping() got an unexpected keyword argument 'request' +``` + +### 1.3 `agents` + +| Method | Fern signature | Stainless signature | Breaking? | +|---|---|---|---| +| `list` | `list(*, parent, page_size, page_token, request_options) -> ListAgentsResponse` | `list(*, page_size, page_token, parent, api_version, extra_*…) -> AgentListResponse` | Class name only. `parent` now explicit on both. | +| `create` | `create(*, id, base_agent, base_environment, description, system_instruction, tools, request_options) -> Agent` | `create(*, id, base_agent, base_environment, description, system_instruction, tools, api_version, extra_*…) -> Agent` | No — field names match 1:1. | +| `get` | `get(id: str, *, request_options) -> Agent` | `get(id, *, api_version, extra_*…) -> Agent` | No. | +| `delete` | `delete(id: str, *, request_options) -> Empty` | `delete(id, *, api_version, extra_*…) -> AgentDeleteResponse` | Class name only. | + +--- + +## 2. Cross‑cutting differences (apply to every method) + +### 2.1 `api_version` + +- **Stainless**: every method accepts `api_version: str | None = None` as a keyword. If `None`, falls back to `self._client._get_api_version_path_param()`. You can override per call. +- **Fern**: `api_version` is **pre‑bound** at construction time in `_build_sync_fern_nextgen` / `_build_async_fern_nextgen` (set to `''` for Vertex, `http_opts.api_version or 'v1beta'` for Gemini). It is not exposed on individual method calls — you cannot override it per call without dropping down to `_fern_nextgen_client.fern_interactions.(...)` directly and constructing the path yourself. + +### 2.2 Per‑request escape hatch + +| Concern | Stainless kwarg | Fern field (inside `RequestOptions`) | +|---|---|---| +| Extra headers | `extra_headers=` | `additional_headers=` | +| Extra query params | `extra_query=` | `additional_query_parameters=` | +| Extra body fields | `extra_body=` | `additional_body_parameters=` | +| Per‑call timeout | `timeout=` (`float \| httpx.Timeout \| None`) | `timeout_in_seconds=` (`int`) | +| Retries | (client‑level only) | `max_retries=` per call | + +The structure differs too: Stainless takes loose kwargs on each method; Fern takes a single `request_options=RequestOptions(...)` object. **Naming is incompatible across the board.** + +### 2.3 Streaming model + +- **Stainless**: streaming is an overload of the same method. `create(stream=True)` returns `Stream[InteractionSSEEvent]`; `get(stream=True)` returns `Stream[InteractionSSEEvent]`. Async variants return `AsyncStream[...]`. There is also Vertex‑specific stream subclassing — `LegacyLyriaInteractionStream`, `LegacyLyriaInteractionDetectingStream` — that activates the per‑event SSE remap for legacy Lyria models. +- **Fern**: streaming is a **separate method** (`create_stream`, `get_stream`) returning `Iterator[InteractionSseEvent]` / `AsyncIterator[InteractionSseEvent]`. No Vertex Lyria stream coercion. + +Also note the event‑class casing: `InteractionSSEEvent` (Stainless) vs `InteractionSseEvent` (Fern). Same JSON shape, different Python classes from different modules. + +### 2.4 Required‑arg validation + +- **Stainless** `interactions.create` is wrapped with `@required_args(["input","model"], ["input","model","stream"], ["agent","input"], ["agent","input","stream"])` and explicitly rejects mixing `model`+`agent_config` or `agent`+`generation_config`. These checks run client‑side before the HTTP call. +- **Fern** `fern_interactions.create` accepts `request: Union[CreateModelInteractionParams, CreateAgentInteractionParams]`. The Pydantic model (`CreateModelInteractionParams`) validates required fields (`model`, `input`) at instantiation time, so field errors surface at Python level — but there is no Fern‑side guard against mixing `model`+`agent_config`. + +### 2.5 `with_raw_response` / `with_streaming_response` + +- **Stainless**: every resource exposes both `with_raw_response` (a `*WithRawResponse` wrapper) and `with_streaming_response` (a `*WithStreamingResponse` wrapper) as `cached_property`. +- **Fern**: exposes only `with_raw_response`. There is no `with_streaming_response`. + +### 2.6 Return‑type identity + +Every response model lives in a different module: + +| Concept | Stainless class | Fern class | +|---|---|---| +| Interaction | `google.genai._interactions.types.interaction.Interaction` | `google.genai._fern_interactions.types.interaction.Interaction` | +| Interaction SSE event | `InteractionSSEEvent` | `InteractionSseEvent` | +| Webhook | `_interactions.types.webhook.Webhook` | `_fern_interactions.types.webhook.Webhook` | +| Webhook list | `WebhookListResponse` | `ListWebhooksResponse` | +| Webhook delete | `WebhookDeleteResponse` | `Empty` | +| Webhook ping | `WebhookPingResponse` | `PingWebhookResponse` | +| Webhook rotate secret | `WebhookRotateSigningSecretResponse` | `RotateSigningSecretResponse` | +| Agent | `_interactions.types.agent.Agent` | `_fern_interactions.types.agent.Agent` | +| Agent list | `AgentListResponse` | `ListAgentsResponse` | +| Agent delete | `AgentDeleteResponse` | `Empty` | + +Same JSON shape, **different Python classes**. Code that does `isinstance(x, Webhook)`, imports the model for type hints, or depends on `model_dump`/`model_dump_json` (Stainless) vs `.json()` / `.dict()` (Fern Pydantic) semantics will break on swap. + +### 2.7 `id` parameter binding + +Both Fern and Stainless now expose `id` as **positional‑or‑keyword** (`def get(self, id: str, *, …)`). This was a breaking difference in earlier versions but is no longer the case. Positional or keyword calls work on both surfaces. + +### 2.8 Vertex path/routing differences + +- **Stainless**: `_build_maybe_vertex_path(api_version=…, path=…)` handles the `projects/{project}/locations/{location}` prefix automatically, and the `LegacyLyriaInteractionStream` / `LegacyLyriaInteractionDetectingStream` classes rewrite legacy Lyria SSE events for Vertex. Detection is driven by `self._client._is_vertex` and `is_legacy_lyria_request(...)`. +- **Fern**: no equivalent legacy‑Lyria coercion path. Vertex base URL has the version embedded so the wrapper `api_version` is set to `''`; callers can't override this per request. + +--- + +## 3. Summary cheat‑sheet (Fern → Stainless) + +```python +# 1) create — Fern uses a typed Pydantic request object +# Fern: +from google.genai._fern_interactions.types import CreateModelInteractionParams, TextContent +client.fern_interactions.create( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent(text="Hello", type="text"), + ) +) +# Stainless: +client.interactions.create(input="Hello", model="gemini-2.5-computer-use-preview-10-2025") + +# 2) create_stream (method does not exist on Stainless) +stream = client.interactions.create(input="Hello", model="gemini-2.5-computer-use-preview-10-2025", stream=True) +for chunk in stream: ... + +# 3) get +client.interactions.get(created_id) + +# 4) get_stream (method does not exist on Stainless) +stream = client.interactions.get(created_id, stream=True) +for chunk in stream: ... + +# 5) cancel / delete — identical shape +client.interactions.cancel(created_id) +client.interactions.delete(created_id) + +# 6) webhooks.list / create / get / update / delete / rotate_signing_secret — identical shape (class names differ) +client.webhooks.list() +client.webhooks.create(uri="https://example.com/hook", subscribed_events=["interaction.completed"]) +client.webhooks.get(webhook_id) +client.webhooks.update(webhook_id, state="enabled") +client.webhooks.rotate_signing_secret(webhook_id) +client.webhooks.delete(webhook_id) + +# 7) webhooks.ping — kwarg renamed +client.webhooks.ping(webhook_id, body={}) # Stainless +# client.webhooks.ping(webhook_id, request={}) # would TypeError on Stainless + +# 8) agents.list / create / get / delete — identical shape (class names differ) +vertex_client.agents.list() +vertex_client.agents.create(id="poc-agent", system_instruction="You are a helpful assistant.") +vertex_client.agents.get(agent_id) +vertex_client.agents.delete(agent_id) +``` + +## 4. Hard breaks (a literal swap will fail) + +1. **`fern_interactions.create_stream`** — no Stainless equivalent; rewrite as `create(..., stream=True)`. +2. **`fern_interactions.get_stream`** — no Stainless equivalent; rewrite as `get(id, stream=True)`. +3. **`fern_interactions.create(request=CreateModelInteractionParams(...))`** — Stainless has no `request` kwarg; rewrite to loose typed kwargs (`input`, `model`, …) and `extra_body=` for unmodeled fields. +4. **`fern_webhooks.ping(request=...)`** — Stainless uses `body=` (and the body is optional, not required). +5. **`RequestOptions` field names** — `additional_headers / additional_query_parameters / additional_body_parameters / timeout_in_seconds / max_retries` must be translated to `extra_headers / extra_query / extra_body / timeout` (and `max_retries` is client‑level only on Stainless). +6. **`with_streaming_response`** — only exists on Stainless. Code that relies on it can't be written against `client.fern_*`. +7. **Return‑type imports** — any `from google.genai._interactions.types.* import ...` (or vice versa) is module‑specific. Swapping providers requires changing every import and every `isinstance` check. + +## 5. Soft breaks (code runs, behavior differs) + +1. **Pre‑bound `api_version`** on Fern: callers can't override per request. +2. **Vertex Lyria SSE remap** is Stainless‑only. +3. **Client‑side required‑arg mutual‑exclusion validation** (`model`+`agent_config` guard) is Stainless‑only — Fern lets you construct an invalid request that the server then rejects. +4. **`delete` return type**: Stainless returns `object` / `WebhookDeleteResponse` / `AgentDeleteResponse`; Fern returns `typing.Dict[str, Any]` / `Empty`. diff --git a/google/genai/_fern_interactions/__init__.py b/google/genai/_fern_interactions/__init__.py new file mode 100644 index 000000000..d67a9c27d --- /dev/null +++ b/google/genai/_fern_interactions/__init__.py @@ -0,0 +1,1000 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + Agent, + AgentBaseEnvironment, + AgentEgressAllowlist, + AgentEgressRule, + AgentOption, + AgentOptionOne, + AgentOptionThree, + AgentOptionTwo, + AgentTool, + AllowedTools, + AllowlistEntry, + Annotation, + ArgumentsDelta, + ArgumentsDeltaType, + AudioContent, + AudioContentMimeType, + AudioContentType, + AudioDelta, + AudioDeltaMimeType, + AudioDeltaType, + AudioResponseFormat, + AudioResponseFormatDelivery, + AudioResponseFormatMimeType, + AudioResponseFormatType, + CodeExecution, + CodeExecutionCallArguments, + CodeExecutionCallArgumentsLanguage, + CodeExecutionCallDelta, + CodeExecutionCallDeltaType, + CodeExecutionCallStep, + CodeExecutionCallStepArguments, + CodeExecutionCallStepArgumentsLanguage, + CodeExecutionCallStepType, + CodeExecutionResultDelta, + CodeExecutionResultDeltaType, + CodeExecutionResultStep, + CodeExecutionResultStepType, + CodeExecutionType, + ComputerUse, + ComputerUseEnvironment, + ComputerUseType, + Content, + ContentDelta, + ContentDeltaData, + ContentDeltaEventType, + ContentStart, + ContentStartEventType, + ContentStop, + ContentStopEventType, + CreateAgentInteractionParams, + CreateAgentInteractionParamsAgentConfig, + CreateAgentInteractionParamsEnvironment, + CreateAgentInteractionParamsResponseFormat, + CreateModelInteractionParams, + CreateModelInteractionParamsEnvironment, + CreateModelInteractionParamsResponseFormat, + DeepResearchAgentConfig, + DeepResearchAgentConfigType, + DeepResearchAgentConfigVisualization, + DocumentContent, + DocumentContentMimeType, + DocumentContentType, + DocumentDelta, + DocumentDeltaMimeType, + DocumentDeltaType, + DynamicAgentConfig, + DynamicAgentConfigType, + Empty, + EnvironmentConfig, + EnvironmentConfigNetwork, + EnvironmentConfigNetworkOne, + EnvironmentConfigType, + EnvironmentNetworkEgressAllowlist, + EnvironmentNetworkEgressAllowlistAllowlist, + EnvironmentNetworkEgressAllowlistOne, + EnvironmentSource, + EnvironmentSourceType, + Error, + ErrorEvent, + ErrorEventEventType, + FileCitation, + FileCitationType, + FileSearch, + FileSearchCallDelta, + FileSearchCallDeltaType, + FileSearchCallStep, + FileSearchCallStepType, + FileSearchResult, + FileSearchResultDelta, + FileSearchResultDeltaType, + FileSearchResultStep, + FileSearchResultStepType, + FileSearchType, + Function, + FunctionCallDelta, + FunctionCallDeltaType, + FunctionCallStep, + FunctionCallStepType, + FunctionResultDelta, + FunctionResultDeltaResult, + FunctionResultDeltaType, + FunctionResultStep, + FunctionResultStepResult, + FunctionResultStepType, + FunctionResultSubcontent, + FunctionType, + GenerationConfig, + GenerationConfigToolChoice, + GoogleMaps, + GoogleMapsCallArguments, + GoogleMapsCallDelta, + GoogleMapsCallDeltaType, + GoogleMapsCallStep, + GoogleMapsCallStepArguments, + GoogleMapsCallStepType, + GoogleMapsResult, + GoogleMapsResultDelta, + GoogleMapsResultDeltaType, + GoogleMapsResultItem, + GoogleMapsResultPlaces, + GoogleMapsResultStep, + GoogleMapsResultStepType, + GoogleMapsType, + GoogleSearch, + GoogleSearchCallArguments, + GoogleSearchCallDelta, + GoogleSearchCallDeltaType, + GoogleSearchCallStep, + GoogleSearchCallStepArguments, + GoogleSearchCallStepSearchType, + GoogleSearchCallStepType, + GoogleSearchResult, + GoogleSearchResultDelta, + GoogleSearchResultDeltaType, + GoogleSearchResultItem, + GoogleSearchResultStep, + GoogleSearchResultStepType, + GoogleSearchSearchTypesItem, + GoogleSearchType, + GroundingToolCount, + GroundingToolCountType, + ImageConfig, + ImageConfigAspectRatio, + ImageConfigImageSize, + ImageContent, + ImageContentMimeType, + ImageContentType, + ImageDelta, + ImageDeltaMimeType, + ImageDeltaType, + ImageResponseFormat, + ImageResponseFormatAspectRatio, + ImageResponseFormatDelivery, + ImageResponseFormatImageSize, + ImageResponseFormatMimeType, + ImageResponseFormatType, + Interaction, + InteractionAgentConfig, + InteractionCompletedEvent, + InteractionCompletedEventEventType, + InteractionCreatedEvent, + InteractionCreatedEventEventType, + InteractionEnvironment, + InteractionResponseFormat, + InteractionSseEvent, + InteractionStatus, + InteractionStatusUpdate, + InteractionStatusUpdateEventType, + InteractionStatusUpdateStatus, + InteractionsInput, + ListAgentsResponse, + ListWebhooksResponse, + McpServer, + McpServerToolCallDelta, + McpServerToolCallDeltaType, + McpServerToolCallStep, + McpServerToolCallStepType, + McpServerToolResultDelta, + McpServerToolResultDeltaResult, + McpServerToolResultDeltaType, + McpServerToolResultStep, + McpServerToolResultStepResult, + McpServerToolResultStepType, + McpServerType, + MediaResolution, + ModalityTokens, + ModelOption, + ModelOptionEight, + ModelOptionEighteen, + ModelOptionEleven, + ModelOptionFifteen, + ModelOptionFive, + ModelOptionFour, + ModelOptionFourteen, + ModelOptionNine, + ModelOptionNineteen, + ModelOptionOne, + ModelOptionSeven, + ModelOptionSeventeen, + ModelOptionSix, + ModelOptionSixteen, + ModelOptionTen, + ModelOptionThirteen, + ModelOptionThree, + ModelOptionTwelve, + ModelOptionTwenty, + ModelOptionTwo, + ModelOutputStep, + ModelOutputStepType, + PingWebhookRequest, + PingWebhookResponse, + PlaceCitation, + PlaceCitationType, + Places, + ResponseFormat, + ResponseFormatList, + ResponseModality, + Retrieval, + RetrievalRetrievalTypesItem, + RetrievalType, + ReviewSnippet, + RotateSigningSecretResponse, + ServiceTier, + SigningSecret, + SigningSecretRead, + Source, + SourceType, + SpeechConfig, + Step, + StepDelta, + StepDeltaData, + StepDeltaEventType, + StepStart, + StepStartEventType, + StepStop, + StepStopEventType, + TextAnnotationDelta, + TextAnnotationDeltaType, + TextContent, + TextContentType, + TextDelta, + TextDeltaType, + TextResponseFormat, + TextResponseFormatMimeType, + TextResponseFormatType, + ThinkingLevel, + ThinkingSummaries, + ThoughtContent, + ThoughtContentType, + ThoughtSignatureDelta, + ThoughtSignatureDeltaType, + ThoughtStep, + ThoughtStepType, + ThoughtSummaryContent, + ThoughtSummaryDelta, + ThoughtSummaryDeltaType, + Tool, + ToolChoiceConfig, + ToolChoiceType, + Turn, + TurnContent, + UrlCitation, + UrlCitationType, + UrlContext, + UrlContextCallArguments, + UrlContextCallDelta, + UrlContextCallDeltaType, + UrlContextCallStep, + UrlContextCallStepArguments, + UrlContextCallStepType, + UrlContextResult, + UrlContextResultDelta, + UrlContextResultDeltaType, + UrlContextResultItem, + UrlContextResultItemStatus, + UrlContextResultStatus, + UrlContextResultStep, + UrlContextResultStepType, + UrlContextType, + Usage, + UserInputStep, + UserInputStepType, + VertexAiSearchConfig, + VideoContent, + VideoContentMimeType, + VideoContentType, + VideoDelta, + VideoDeltaMimeType, + VideoDeltaType, + Webhook, + WebhookConfig, + WebhookRead, + WebhookState, + WebhookSubscribedEventsItem, + WebhookSubscribedEventsItemFive, + WebhookSubscribedEventsItemFour, + WebhookSubscribedEventsItemOne, + WebhookSubscribedEventsItemSeven, + WebhookSubscribedEventsItemSix, + WebhookSubscribedEventsItemThree, + WebhookSubscribedEventsItemTwo, + ) + from . import fern_agents, fern_interactions, fern_webhooks + from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient + from .client import AsyncGeminiNextGenAPIClient, GeminiNextGenAPIClient + from .environment import GeminiNextGenAPIClientEnvironment + from .fern_interactions import CreateFernInteractionsRequest, CreateFernInteractionsStreamRequest + from .fern_webhooks import ( + RotateSigningSecretRequestRevocationBehavior, + WebhookUpdateState, + WebhookUpdateSubscribedEventsItem, + WebhookUpdateSubscribedEventsItemFive, + WebhookUpdateSubscribedEventsItemFour, + WebhookUpdateSubscribedEventsItemOne, + WebhookUpdateSubscribedEventsItemSeven, + WebhookUpdateSubscribedEventsItemSix, + WebhookUpdateSubscribedEventsItemThree, + WebhookUpdateSubscribedEventsItemTwo, + ) + from .version import __version__ +_dynamic_imports: typing.Dict[str, str] = { + "Agent": ".types", + "AgentBaseEnvironment": ".types", + "AgentEgressAllowlist": ".types", + "AgentEgressRule": ".types", + "AgentOption": ".types", + "AgentOptionOne": ".types", + "AgentOptionThree": ".types", + "AgentOptionTwo": ".types", + "AgentTool": ".types", + "AllowedTools": ".types", + "AllowlistEntry": ".types", + "Annotation": ".types", + "ArgumentsDelta": ".types", + "ArgumentsDeltaType": ".types", + "AsyncGeminiNextGenAPIClient": ".client", + "AudioContent": ".types", + "AudioContentMimeType": ".types", + "AudioContentType": ".types", + "AudioDelta": ".types", + "AudioDeltaMimeType": ".types", + "AudioDeltaType": ".types", + "AudioResponseFormat": ".types", + "AudioResponseFormatDelivery": ".types", + "AudioResponseFormatMimeType": ".types", + "AudioResponseFormatType": ".types", + "CodeExecution": ".types", + "CodeExecutionCallArguments": ".types", + "CodeExecutionCallArgumentsLanguage": ".types", + "CodeExecutionCallDelta": ".types", + "CodeExecutionCallDeltaType": ".types", + "CodeExecutionCallStep": ".types", + "CodeExecutionCallStepArguments": ".types", + "CodeExecutionCallStepArgumentsLanguage": ".types", + "CodeExecutionCallStepType": ".types", + "CodeExecutionResultDelta": ".types", + "CodeExecutionResultDeltaType": ".types", + "CodeExecutionResultStep": ".types", + "CodeExecutionResultStepType": ".types", + "CodeExecutionType": ".types", + "ComputerUse": ".types", + "ComputerUseEnvironment": ".types", + "ComputerUseType": ".types", + "Content": ".types", + "ContentDelta": ".types", + "ContentDeltaData": ".types", + "ContentDeltaEventType": ".types", + "ContentStart": ".types", + "ContentStartEventType": ".types", + "ContentStop": ".types", + "ContentStopEventType": ".types", + "CreateAgentInteractionParams": ".types", + "CreateAgentInteractionParamsAgentConfig": ".types", + "CreateAgentInteractionParamsEnvironment": ".types", + "CreateAgentInteractionParamsResponseFormat": ".types", + "CreateFernInteractionsRequest": ".fern_interactions", + "CreateFernInteractionsStreamRequest": ".fern_interactions", + "CreateModelInteractionParams": ".types", + "CreateModelInteractionParamsEnvironment": ".types", + "CreateModelInteractionParamsResponseFormat": ".types", + "DeepResearchAgentConfig": ".types", + "DeepResearchAgentConfigType": ".types", + "DeepResearchAgentConfigVisualization": ".types", + "DefaultAioHttpClient": "._default_clients", + "DefaultAsyncHttpxClient": "._default_clients", + "DocumentContent": ".types", + "DocumentContentMimeType": ".types", + "DocumentContentType": ".types", + "DocumentDelta": ".types", + "DocumentDeltaMimeType": ".types", + "DocumentDeltaType": ".types", + "DynamicAgentConfig": ".types", + "DynamicAgentConfigType": ".types", + "Empty": ".types", + "EnvironmentConfig": ".types", + "EnvironmentConfigNetwork": ".types", + "EnvironmentConfigNetworkOne": ".types", + "EnvironmentConfigType": ".types", + "EnvironmentNetworkEgressAllowlist": ".types", + "EnvironmentNetworkEgressAllowlistAllowlist": ".types", + "EnvironmentNetworkEgressAllowlistOne": ".types", + "EnvironmentSource": ".types", + "EnvironmentSourceType": ".types", + "Error": ".types", + "ErrorEvent": ".types", + "ErrorEventEventType": ".types", + "FileCitation": ".types", + "FileCitationType": ".types", + "FileSearch": ".types", + "FileSearchCallDelta": ".types", + "FileSearchCallDeltaType": ".types", + "FileSearchCallStep": ".types", + "FileSearchCallStepType": ".types", + "FileSearchResult": ".types", + "FileSearchResultDelta": ".types", + "FileSearchResultDeltaType": ".types", + "FileSearchResultStep": ".types", + "FileSearchResultStepType": ".types", + "FileSearchType": ".types", + "Function": ".types", + "FunctionCallDelta": ".types", + "FunctionCallDeltaType": ".types", + "FunctionCallStep": ".types", + "FunctionCallStepType": ".types", + "FunctionResultDelta": ".types", + "FunctionResultDeltaResult": ".types", + "FunctionResultDeltaType": ".types", + "FunctionResultStep": ".types", + "FunctionResultStepResult": ".types", + "FunctionResultStepType": ".types", + "FunctionResultSubcontent": ".types", + "FunctionType": ".types", + "GeminiNextGenAPIClient": ".client", + "GeminiNextGenAPIClientEnvironment": ".environment", + "GenerationConfig": ".types", + "GenerationConfigToolChoice": ".types", + "GoogleMaps": ".types", + "GoogleMapsCallArguments": ".types", + "GoogleMapsCallDelta": ".types", + "GoogleMapsCallDeltaType": ".types", + "GoogleMapsCallStep": ".types", + "GoogleMapsCallStepArguments": ".types", + "GoogleMapsCallStepType": ".types", + "GoogleMapsResult": ".types", + "GoogleMapsResultDelta": ".types", + "GoogleMapsResultDeltaType": ".types", + "GoogleMapsResultItem": ".types", + "GoogleMapsResultPlaces": ".types", + "GoogleMapsResultStep": ".types", + "GoogleMapsResultStepType": ".types", + "GoogleMapsType": ".types", + "GoogleSearch": ".types", + "GoogleSearchCallArguments": ".types", + "GoogleSearchCallDelta": ".types", + "GoogleSearchCallDeltaType": ".types", + "GoogleSearchCallStep": ".types", + "GoogleSearchCallStepArguments": ".types", + "GoogleSearchCallStepSearchType": ".types", + "GoogleSearchCallStepType": ".types", + "GoogleSearchResult": ".types", + "GoogleSearchResultDelta": ".types", + "GoogleSearchResultDeltaType": ".types", + "GoogleSearchResultItem": ".types", + "GoogleSearchResultStep": ".types", + "GoogleSearchResultStepType": ".types", + "GoogleSearchSearchTypesItem": ".types", + "GoogleSearchType": ".types", + "GroundingToolCount": ".types", + "GroundingToolCountType": ".types", + "ImageConfig": ".types", + "ImageConfigAspectRatio": ".types", + "ImageConfigImageSize": ".types", + "ImageContent": ".types", + "ImageContentMimeType": ".types", + "ImageContentType": ".types", + "ImageDelta": ".types", + "ImageDeltaMimeType": ".types", + "ImageDeltaType": ".types", + "ImageResponseFormat": ".types", + "ImageResponseFormatAspectRatio": ".types", + "ImageResponseFormatDelivery": ".types", + "ImageResponseFormatImageSize": ".types", + "ImageResponseFormatMimeType": ".types", + "ImageResponseFormatType": ".types", + "Interaction": ".types", + "InteractionAgentConfig": ".types", + "InteractionCompletedEvent": ".types", + "InteractionCompletedEventEventType": ".types", + "InteractionCreatedEvent": ".types", + "InteractionCreatedEventEventType": ".types", + "InteractionEnvironment": ".types", + "InteractionResponseFormat": ".types", + "InteractionSseEvent": ".types", + "InteractionStatus": ".types", + "InteractionStatusUpdate": ".types", + "InteractionStatusUpdateEventType": ".types", + "InteractionStatusUpdateStatus": ".types", + "InteractionsInput": ".types", + "ListAgentsResponse": ".types", + "ListWebhooksResponse": ".types", + "McpServer": ".types", + "McpServerToolCallDelta": ".types", + "McpServerToolCallDeltaType": ".types", + "McpServerToolCallStep": ".types", + "McpServerToolCallStepType": ".types", + "McpServerToolResultDelta": ".types", + "McpServerToolResultDeltaResult": ".types", + "McpServerToolResultDeltaType": ".types", + "McpServerToolResultStep": ".types", + "McpServerToolResultStepResult": ".types", + "McpServerToolResultStepType": ".types", + "McpServerType": ".types", + "MediaResolution": ".types", + "ModalityTokens": ".types", + "ModelOption": ".types", + "ModelOptionEight": ".types", + "ModelOptionEighteen": ".types", + "ModelOptionEleven": ".types", + "ModelOptionFifteen": ".types", + "ModelOptionFive": ".types", + "ModelOptionFour": ".types", + "ModelOptionFourteen": ".types", + "ModelOptionNine": ".types", + "ModelOptionNineteen": ".types", + "ModelOptionOne": ".types", + "ModelOptionSeven": ".types", + "ModelOptionSeventeen": ".types", + "ModelOptionSix": ".types", + "ModelOptionSixteen": ".types", + "ModelOptionTen": ".types", + "ModelOptionThirteen": ".types", + "ModelOptionThree": ".types", + "ModelOptionTwelve": ".types", + "ModelOptionTwenty": ".types", + "ModelOptionTwo": ".types", + "ModelOutputStep": ".types", + "ModelOutputStepType": ".types", + "PingWebhookRequest": ".types", + "PingWebhookResponse": ".types", + "PlaceCitation": ".types", + "PlaceCitationType": ".types", + "Places": ".types", + "ResponseFormat": ".types", + "ResponseFormatList": ".types", + "ResponseModality": ".types", + "Retrieval": ".types", + "RetrievalRetrievalTypesItem": ".types", + "RetrievalType": ".types", + "ReviewSnippet": ".types", + "RotateSigningSecretRequestRevocationBehavior": ".fern_webhooks", + "RotateSigningSecretResponse": ".types", + "ServiceTier": ".types", + "SigningSecret": ".types", + "SigningSecretRead": ".types", + "Source": ".types", + "SourceType": ".types", + "SpeechConfig": ".types", + "Step": ".types", + "StepDelta": ".types", + "StepDeltaData": ".types", + "StepDeltaEventType": ".types", + "StepStart": ".types", + "StepStartEventType": ".types", + "StepStop": ".types", + "StepStopEventType": ".types", + "TextAnnotationDelta": ".types", + "TextAnnotationDeltaType": ".types", + "TextContent": ".types", + "TextContentType": ".types", + "TextDelta": ".types", + "TextDeltaType": ".types", + "TextResponseFormat": ".types", + "TextResponseFormatMimeType": ".types", + "TextResponseFormatType": ".types", + "ThinkingLevel": ".types", + "ThinkingSummaries": ".types", + "ThoughtContent": ".types", + "ThoughtContentType": ".types", + "ThoughtSignatureDelta": ".types", + "ThoughtSignatureDeltaType": ".types", + "ThoughtStep": ".types", + "ThoughtStepType": ".types", + "ThoughtSummaryContent": ".types", + "ThoughtSummaryDelta": ".types", + "ThoughtSummaryDeltaType": ".types", + "Tool": ".types", + "ToolChoiceConfig": ".types", + "ToolChoiceType": ".types", + "Turn": ".types", + "TurnContent": ".types", + "UrlCitation": ".types", + "UrlCitationType": ".types", + "UrlContext": ".types", + "UrlContextCallArguments": ".types", + "UrlContextCallDelta": ".types", + "UrlContextCallDeltaType": ".types", + "UrlContextCallStep": ".types", + "UrlContextCallStepArguments": ".types", + "UrlContextCallStepType": ".types", + "UrlContextResult": ".types", + "UrlContextResultDelta": ".types", + "UrlContextResultDeltaType": ".types", + "UrlContextResultItem": ".types", + "UrlContextResultItemStatus": ".types", + "UrlContextResultStatus": ".types", + "UrlContextResultStep": ".types", + "UrlContextResultStepType": ".types", + "UrlContextType": ".types", + "Usage": ".types", + "UserInputStep": ".types", + "UserInputStepType": ".types", + "VertexAiSearchConfig": ".types", + "VideoContent": ".types", + "VideoContentMimeType": ".types", + "VideoContentType": ".types", + "VideoDelta": ".types", + "VideoDeltaMimeType": ".types", + "VideoDeltaType": ".types", + "Webhook": ".types", + "WebhookConfig": ".types", + "WebhookRead": ".types", + "WebhookState": ".types", + "WebhookSubscribedEventsItem": ".types", + "WebhookSubscribedEventsItemFive": ".types", + "WebhookSubscribedEventsItemFour": ".types", + "WebhookSubscribedEventsItemOne": ".types", + "WebhookSubscribedEventsItemSeven": ".types", + "WebhookSubscribedEventsItemSix": ".types", + "WebhookSubscribedEventsItemThree": ".types", + "WebhookSubscribedEventsItemTwo": ".types", + "WebhookUpdateState": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItem": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemFive": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemFour": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemOne": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemSeven": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemSix": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemThree": ".fern_webhooks", + "WebhookUpdateSubscribedEventsItemTwo": ".fern_webhooks", + "__version__": ".version", + "fern_agents": ".fern_agents", + "fern_interactions": ".fern_interactions", + "fern_webhooks": ".fern_webhooks", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Agent", + "AgentBaseEnvironment", + "AgentEgressAllowlist", + "AgentEgressRule", + "AgentOption", + "AgentOptionOne", + "AgentOptionThree", + "AgentOptionTwo", + "AgentTool", + "AllowedTools", + "AllowlistEntry", + "Annotation", + "ArgumentsDelta", + "ArgumentsDeltaType", + "AsyncGeminiNextGenAPIClient", + "AudioContent", + "AudioContentMimeType", + "AudioContentType", + "AudioDelta", + "AudioDeltaMimeType", + "AudioDeltaType", + "AudioResponseFormat", + "AudioResponseFormatDelivery", + "AudioResponseFormatMimeType", + "AudioResponseFormatType", + "CodeExecution", + "CodeExecutionCallArguments", + "CodeExecutionCallArgumentsLanguage", + "CodeExecutionCallDelta", + "CodeExecutionCallDeltaType", + "CodeExecutionCallStep", + "CodeExecutionCallStepArguments", + "CodeExecutionCallStepArgumentsLanguage", + "CodeExecutionCallStepType", + "CodeExecutionResultDelta", + "CodeExecutionResultDeltaType", + "CodeExecutionResultStep", + "CodeExecutionResultStepType", + "CodeExecutionType", + "ComputerUse", + "ComputerUseEnvironment", + "ComputerUseType", + "Content", + "ContentDelta", + "ContentDeltaData", + "ContentDeltaEventType", + "ContentStart", + "ContentStartEventType", + "ContentStop", + "ContentStopEventType", + "CreateAgentInteractionParams", + "CreateAgentInteractionParamsAgentConfig", + "CreateAgentInteractionParamsEnvironment", + "CreateAgentInteractionParamsResponseFormat", + "CreateFernInteractionsRequest", + "CreateFernInteractionsStreamRequest", + "CreateModelInteractionParams", + "CreateModelInteractionParamsEnvironment", + "CreateModelInteractionParamsResponseFormat", + "DeepResearchAgentConfig", + "DeepResearchAgentConfigType", + "DeepResearchAgentConfigVisualization", + "DefaultAioHttpClient", + "DefaultAsyncHttpxClient", + "DocumentContent", + "DocumentContentMimeType", + "DocumentContentType", + "DocumentDelta", + "DocumentDeltaMimeType", + "DocumentDeltaType", + "DynamicAgentConfig", + "DynamicAgentConfigType", + "Empty", + "EnvironmentConfig", + "EnvironmentConfigNetwork", + "EnvironmentConfigNetworkOne", + "EnvironmentConfigType", + "EnvironmentNetworkEgressAllowlist", + "EnvironmentNetworkEgressAllowlistAllowlist", + "EnvironmentNetworkEgressAllowlistOne", + "EnvironmentSource", + "EnvironmentSourceType", + "Error", + "ErrorEvent", + "ErrorEventEventType", + "FileCitation", + "FileCitationType", + "FileSearch", + "FileSearchCallDelta", + "FileSearchCallDeltaType", + "FileSearchCallStep", + "FileSearchCallStepType", + "FileSearchResult", + "FileSearchResultDelta", + "FileSearchResultDeltaType", + "FileSearchResultStep", + "FileSearchResultStepType", + "FileSearchType", + "Function", + "FunctionCallDelta", + "FunctionCallDeltaType", + "FunctionCallStep", + "FunctionCallStepType", + "FunctionResultDelta", + "FunctionResultDeltaResult", + "FunctionResultDeltaType", + "FunctionResultStep", + "FunctionResultStepResult", + "FunctionResultStepType", + "FunctionResultSubcontent", + "FunctionType", + "GeminiNextGenAPIClient", + "GeminiNextGenAPIClientEnvironment", + "GenerationConfig", + "GenerationConfigToolChoice", + "GoogleMaps", + "GoogleMapsCallArguments", + "GoogleMapsCallDelta", + "GoogleMapsCallDeltaType", + "GoogleMapsCallStep", + "GoogleMapsCallStepArguments", + "GoogleMapsCallStepType", + "GoogleMapsResult", + "GoogleMapsResultDelta", + "GoogleMapsResultDeltaType", + "GoogleMapsResultItem", + "GoogleMapsResultPlaces", + "GoogleMapsResultStep", + "GoogleMapsResultStepType", + "GoogleMapsType", + "GoogleSearch", + "GoogleSearchCallArguments", + "GoogleSearchCallDelta", + "GoogleSearchCallDeltaType", + "GoogleSearchCallStep", + "GoogleSearchCallStepArguments", + "GoogleSearchCallStepSearchType", + "GoogleSearchCallStepType", + "GoogleSearchResult", + "GoogleSearchResultDelta", + "GoogleSearchResultDeltaType", + "GoogleSearchResultItem", + "GoogleSearchResultStep", + "GoogleSearchResultStepType", + "GoogleSearchSearchTypesItem", + "GoogleSearchType", + "GroundingToolCount", + "GroundingToolCountType", + "ImageConfig", + "ImageConfigAspectRatio", + "ImageConfigImageSize", + "ImageContent", + "ImageContentMimeType", + "ImageContentType", + "ImageDelta", + "ImageDeltaMimeType", + "ImageDeltaType", + "ImageResponseFormat", + "ImageResponseFormatAspectRatio", + "ImageResponseFormatDelivery", + "ImageResponseFormatImageSize", + "ImageResponseFormatMimeType", + "ImageResponseFormatType", + "Interaction", + "InteractionAgentConfig", + "InteractionCompletedEvent", + "InteractionCompletedEventEventType", + "InteractionCreatedEvent", + "InteractionCreatedEventEventType", + "InteractionEnvironment", + "InteractionResponseFormat", + "InteractionSseEvent", + "InteractionStatus", + "InteractionStatusUpdate", + "InteractionStatusUpdateEventType", + "InteractionStatusUpdateStatus", + "InteractionsInput", + "ListAgentsResponse", + "ListWebhooksResponse", + "McpServer", + "McpServerToolCallDelta", + "McpServerToolCallDeltaType", + "McpServerToolCallStep", + "McpServerToolCallStepType", + "McpServerToolResultDelta", + "McpServerToolResultDeltaResult", + "McpServerToolResultDeltaType", + "McpServerToolResultStep", + "McpServerToolResultStepResult", + "McpServerToolResultStepType", + "McpServerType", + "MediaResolution", + "ModalityTokens", + "ModelOption", + "ModelOptionEight", + "ModelOptionEighteen", + "ModelOptionEleven", + "ModelOptionFifteen", + "ModelOptionFive", + "ModelOptionFour", + "ModelOptionFourteen", + "ModelOptionNine", + "ModelOptionNineteen", + "ModelOptionOne", + "ModelOptionSeven", + "ModelOptionSeventeen", + "ModelOptionSix", + "ModelOptionSixteen", + "ModelOptionTen", + "ModelOptionThirteen", + "ModelOptionThree", + "ModelOptionTwelve", + "ModelOptionTwenty", + "ModelOptionTwo", + "ModelOutputStep", + "ModelOutputStepType", + "PingWebhookRequest", + "PingWebhookResponse", + "PlaceCitation", + "PlaceCitationType", + "Places", + "ResponseFormat", + "ResponseFormatList", + "ResponseModality", + "Retrieval", + "RetrievalRetrievalTypesItem", + "RetrievalType", + "ReviewSnippet", + "RotateSigningSecretRequestRevocationBehavior", + "RotateSigningSecretResponse", + "ServiceTier", + "SigningSecret", + "SigningSecretRead", + "Source", + "SourceType", + "SpeechConfig", + "Step", + "StepDelta", + "StepDeltaData", + "StepDeltaEventType", + "StepStart", + "StepStartEventType", + "StepStop", + "StepStopEventType", + "TextAnnotationDelta", + "TextAnnotationDeltaType", + "TextContent", + "TextContentType", + "TextDelta", + "TextDeltaType", + "TextResponseFormat", + "TextResponseFormatMimeType", + "TextResponseFormatType", + "ThinkingLevel", + "ThinkingSummaries", + "ThoughtContent", + "ThoughtContentType", + "ThoughtSignatureDelta", + "ThoughtSignatureDeltaType", + "ThoughtStep", + "ThoughtStepType", + "ThoughtSummaryContent", + "ThoughtSummaryDelta", + "ThoughtSummaryDeltaType", + "Tool", + "ToolChoiceConfig", + "ToolChoiceType", + "Turn", + "TurnContent", + "UrlCitation", + "UrlCitationType", + "UrlContext", + "UrlContextCallArguments", + "UrlContextCallDelta", + "UrlContextCallDeltaType", + "UrlContextCallStep", + "UrlContextCallStepArguments", + "UrlContextCallStepType", + "UrlContextResult", + "UrlContextResultDelta", + "UrlContextResultDeltaType", + "UrlContextResultItem", + "UrlContextResultItemStatus", + "UrlContextResultStatus", + "UrlContextResultStep", + "UrlContextResultStepType", + "UrlContextType", + "Usage", + "UserInputStep", + "UserInputStepType", + "VertexAiSearchConfig", + "VideoContent", + "VideoContentMimeType", + "VideoContentType", + "VideoDelta", + "VideoDeltaMimeType", + "VideoDeltaType", + "Webhook", + "WebhookConfig", + "WebhookRead", + "WebhookState", + "WebhookSubscribedEventsItem", + "WebhookSubscribedEventsItemFive", + "WebhookSubscribedEventsItemFour", + "WebhookSubscribedEventsItemOne", + "WebhookSubscribedEventsItemSeven", + "WebhookSubscribedEventsItemSix", + "WebhookSubscribedEventsItemThree", + "WebhookSubscribedEventsItemTwo", + "WebhookUpdateState", + "WebhookUpdateSubscribedEventsItem", + "WebhookUpdateSubscribedEventsItemFive", + "WebhookUpdateSubscribedEventsItemFour", + "WebhookUpdateSubscribedEventsItemOne", + "WebhookUpdateSubscribedEventsItemSeven", + "WebhookUpdateSubscribedEventsItemSix", + "WebhookUpdateSubscribedEventsItemThree", + "WebhookUpdateSubscribedEventsItemTwo", + "__version__", + "fern_agents", + "fern_interactions", + "fern_webhooks", +] diff --git a/google/genai/_fern_interactions/_default_clients.py b/google/genai/_fern_interactions/_default_clients.py new file mode 100644 index 000000000..f3dfacf5c --- /dev/null +++ b/google/genai/_fern_interactions/_default_clients.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx + +SDK_DEFAULT_TIMEOUT = 60 + +try: + import httpx_aiohttp # type: ignore[import-not-found] +except ImportError: + + class DefaultAioHttpClient(httpx.AsyncClient): # type: ignore + def __init__(self, **kwargs: typing.Any) -> None: + raise RuntimeError( + "To use the aiohttp client, install the aiohttp extra: pip install gemini_next_gen_api[aiohttp]" + ) + +else: + + class DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: typing.Any) -> None: + kwargs.setdefault("timeout", SDK_DEFAULT_TIMEOUT) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +class DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: typing.Any) -> None: + kwargs.setdefault("timeout", SDK_DEFAULT_TIMEOUT) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) diff --git a/google/genai/_fern_interactions/client.py b/google/genai/_fern_interactions/client.py new file mode 100644 index 000000000..9805d0346 --- /dev/null +++ b/google/genai/_fern_interactions/client.py @@ -0,0 +1,274 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import os +import typing + +import httpx +from .core.api_error import ApiError +from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .core.logging import LogConfig, Logger +from .environment import GeminiNextGenAPIClientEnvironment + +if typing.TYPE_CHECKING: + from .fern_agents.client import AsyncFernAgentsClient, FernAgentsClient + from .fern_interactions.client import AsyncFernInteractionsClient, FernInteractionsClient + from .fern_webhooks.client import AsyncFernWebhooksClient, FernWebhooksClient + + +class GeminiNextGenAPIClient: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : typing.Optional[str] + The base url to use for requests from the client. + + environment : GeminiNextGenAPIClientEnvironment + The environment to use for requests from the client. from .environment import GeminiNextGenAPIClientEnvironment + + + + Defaults to GeminiNextGenAPIClientEnvironment.DEFAULT + + + + api_version : typing.Optional[str] + api_key : typing.Optional[str] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + max_retries : typing.Optional[int] + The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.Client] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + + api_revision : typing.Optional[str] + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + """ + + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: GeminiNextGenAPIClientEnvironment = GeminiNextGenAPIClientEnvironment.DEFAULT, + api_version: typing.Optional[str] = "v1beta", + api_key: typing.Optional[str] = os.getenv("GEMINI_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + max_retries: typing.Optional[int] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + api_revision: typing.Optional[str] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + _defaulted_max_retries = max_retries if max_retries is not None else 2 + if api_key is None: + raise ApiError( + body="The client must be instantiated be either passing in api_key or setting GEMINI_API_KEY" + ) + self._client_wrapper = SyncClientWrapper( + base_url=_get_base_url(base_url=base_url, environment=environment), + api_version=api_version, + api_key=api_key, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.Client(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + max_retries=_defaulted_max_retries, + logging=logging, + api_revision=api_revision, + ) + self._fern_interactions: typing.Optional[FernInteractionsClient] = None + self._fern_webhooks: typing.Optional[FernWebhooksClient] = None + self._fern_agents: typing.Optional[FernAgentsClient] = None + + @property + def fern_interactions(self): + if self._fern_interactions is None: + from .fern_interactions.client import FernInteractionsClient # noqa: E402 + + self._fern_interactions = FernInteractionsClient(client_wrapper=self._client_wrapper) + return self._fern_interactions + + @property + def fern_webhooks(self): + if self._fern_webhooks is None: + from .fern_webhooks.client import FernWebhooksClient # noqa: E402 + + self._fern_webhooks = FernWebhooksClient(client_wrapper=self._client_wrapper) + return self._fern_webhooks + + @property + def fern_agents(self): + if self._fern_agents is None: + from .fern_agents.client import FernAgentsClient # noqa: E402 + + self._fern_agents = FernAgentsClient(client_wrapper=self._client_wrapper) + return self._fern_agents + + +def _make_default_async_client( + timeout: typing.Optional[float], + follow_redirects: typing.Optional[bool], +) -> httpx.AsyncClient: + try: + import httpx_aiohttp # type: ignore[import-not-found] + except ImportError: + pass + else: + if follow_redirects is not None: + return httpx_aiohttp.HttpxAiohttpClient(timeout=timeout, follow_redirects=follow_redirects) + return httpx_aiohttp.HttpxAiohttpClient(timeout=timeout) + + if follow_redirects is not None: + return httpx.AsyncClient(timeout=timeout, follow_redirects=follow_redirects) + return httpx.AsyncClient(timeout=timeout) + + +class AsyncGeminiNextGenAPIClient: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : typing.Optional[str] + The base url to use for requests from the client. + + environment : GeminiNextGenAPIClientEnvironment + The environment to use for requests from the client. from .environment import GeminiNextGenAPIClientEnvironment + + + + Defaults to GeminiNextGenAPIClientEnvironment.DEFAULT + + + + api_version : typing.Optional[str] + api_key : typing.Optional[str] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + max_retries : typing.Optional[int] + The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.AsyncClient] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + + api_revision : typing.Optional[str] + Examples + -------- + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + """ + + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: GeminiNextGenAPIClientEnvironment = GeminiNextGenAPIClientEnvironment.DEFAULT, + api_version: typing.Optional[str] = "v1beta", + api_key: typing.Optional[str] = os.getenv("GEMINI_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + max_retries: typing.Optional[int] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + api_revision: typing.Optional[str] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + _defaulted_max_retries = max_retries if max_retries is not None else 2 + if api_key is None: + raise ApiError( + body="The client must be instantiated be either passing in api_key or setting GEMINI_API_KEY" + ) + self._client_wrapper = AsyncClientWrapper( + base_url=_get_base_url(base_url=base_url, environment=environment), + api_version=api_version, + api_key=api_key, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else _make_default_async_client(timeout=_defaulted_timeout, follow_redirects=follow_redirects), + timeout=_defaulted_timeout, + max_retries=_defaulted_max_retries, + logging=logging, + api_revision=api_revision, + ) + self._fern_interactions: typing.Optional[AsyncFernInteractionsClient] = None + self._fern_webhooks: typing.Optional[AsyncFernWebhooksClient] = None + self._fern_agents: typing.Optional[AsyncFernAgentsClient] = None + + @property + def fern_interactions(self): + if self._fern_interactions is None: + from .fern_interactions.client import AsyncFernInteractionsClient # noqa: E402 + + self._fern_interactions = AsyncFernInteractionsClient(client_wrapper=self._client_wrapper) + return self._fern_interactions + + @property + def fern_webhooks(self): + if self._fern_webhooks is None: + from .fern_webhooks.client import AsyncFernWebhooksClient # noqa: E402 + + self._fern_webhooks = AsyncFernWebhooksClient(client_wrapper=self._client_wrapper) + return self._fern_webhooks + + @property + def fern_agents(self): + if self._fern_agents is None: + from .fern_agents.client import AsyncFernAgentsClient # noqa: E402 + + self._fern_agents = AsyncFernAgentsClient(client_wrapper=self._client_wrapper) + return self._fern_agents + + +def _get_base_url(*, base_url: typing.Optional[str] = None, environment: GeminiNextGenAPIClientEnvironment) -> str: + if base_url is not None: + return base_url + elif environment is not None: + return environment.value + else: + raise Exception("Please pass in either base_url or environment to construct the client") diff --git a/google/genai/_fern_interactions/core/__init__.py b/google/genai/_fern_interactions/core/__init__.py new file mode 100644 index 000000000..5bc159a11 --- /dev/null +++ b/google/genai/_fern_interactions/core/__init__.py @@ -0,0 +1,127 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import Rfc2822DateTime, parse_rfc2822_datetime, serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import encode_path_param, jsonable_encoder + from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger + from .parse_error import ParsingError + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "BaseClientWrapper": ".client_wrapper", + "ConsoleLogger": ".logging", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "ILogger": ".logging", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "LogConfig": ".logging", + "LogLevel": ".logging", + "Logger": ".logging", + "ParsingError": ".parse_error", + "RequestOptions": ".request_options", + "Rfc2822DateTime": ".datetime_utils", + "SyncClientWrapper": ".client_wrapper", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "create_logger": ".logging", + "encode_path_param": ".jsonable_encoder", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "parse_rfc2822_datetime": ".datetime_utils", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ApiError", + "AsyncClientWrapper", + "AsyncHttpClient", + "AsyncHttpResponse", + "BaseClientWrapper", + "ConsoleLogger", + "FieldMetadata", + "File", + "HttpClient", + "HttpResponse", + "ILogger", + "IS_PYDANTIC_V2", + "LogConfig", + "LogLevel", + "Logger", + "ParsingError", + "RequestOptions", + "Rfc2822DateTime", + "SyncClientWrapper", + "UniversalBaseModel", + "UniversalRootModel", + "convert_and_respect_annotation_metadata", + "convert_file_dict_to_httpx_tuples", + "create_logger", + "encode_path_param", + "encode_query", + "jsonable_encoder", + "parse_obj_as", + "parse_rfc2822_datetime", + "remove_none_from_dict", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", + "with_content_type", +] diff --git a/google/genai/_fern_interactions/core/api_error.py b/google/genai/_fern_interactions/core/api_error.py new file mode 100644 index 000000000..6f850a60c --- /dev/null +++ b/google/genai/_fern_interactions/core/api_error.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ApiError(Exception): + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + + def __str__(self) -> str: + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}" diff --git a/google/genai/_fern_interactions/core/client_wrapper.py b/google/genai/_fern_interactions/core/client_wrapper.py new file mode 100644 index 000000000..4b2f6bf2a --- /dev/null +++ b/google/genai/_fern_interactions/core/client_wrapper.py @@ -0,0 +1,135 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx +from .http_client import AsyncHttpClient, HttpClient +from .logging import LogConfig, Logger + + +class BaseClientWrapper: + def __init__( + self, + *, + api_version: typing.Optional[str] = None, + api_key: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + max_retries: int = 2, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + api_revision: typing.Optional[str] = None, + ): + self._api_version = api_version + self.api_key = api_key + self._headers = headers + self._base_url = base_url + self._timeout = timeout + self._max_retries = max_retries + self._logging = logging + self._api_revision = api_revision + + def get_headers(self) -> typing.Dict[str, str]: + import platform + + headers: typing.Dict[str, str] = { + "X-Fern-Language": "Python", + "X-Fern-Runtime": f"python/{platform.python_version()}", + "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", + "X-Fern-SDK-Name": "gemini_next_gen_api", + "X-Fern-SDK-Version": "0.0.49", + **(self.get_custom_headers() or {}), + } + headers["x-goog-api-key"] = self.api_key + headers["Api-Revision"] = self._api_revision if self._api_revision is not None else "2026-05-20" + return headers + + def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]: + return self._headers + + def get_base_url(self) -> str: + return self._base_url + + def get_timeout(self) -> typing.Optional[float]: + return self._timeout + + def get_max_retries(self) -> int: + return self._max_retries + + +class SyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + api_version: typing.Optional[str] = None, + api_key: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + max_retries: int = 2, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + api_revision: typing.Optional[str] = None, + httpx_client: httpx.Client, + ): + super().__init__( + api_version=api_version, + api_key=api_key, + headers=headers, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + logging=logging, + api_revision=api_revision, + ) + self.httpx_client = HttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + base_max_retries=self.get_max_retries(), + logging_config=self._logging, + ) + + +class AsyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + api_version: typing.Optional[str] = None, + api_key: str, + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + max_retries: int = 2, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + api_revision: typing.Optional[str] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, + httpx_client: httpx.AsyncClient, + ): + super().__init__( + api_version=api_version, + api_key=api_key, + headers=headers, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + logging=logging, + api_revision=api_revision, + ) + self._async_token = async_token + self.httpx_client = AsyncHttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + base_max_retries=self.get_max_retries(), + async_base_headers=self.async_get_headers, + logging_config=self._logging, + ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/google/genai/_fern_interactions/core/datetime_utils.py b/google/genai/_fern_interactions/core/datetime_utils.py new file mode 100644 index 000000000..a12b2ad03 --- /dev/null +++ b/google/genai/_fern_interactions/core/datetime_utils.py @@ -0,0 +1,70 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +from email.utils import parsedate_to_datetime +from typing import Any + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + + +def parse_rfc2822_datetime(v: Any) -> dt.datetime: + """ + Parse an RFC 2822 datetime string (e.g., "Wed, 02 Oct 2002 13:00:00 GMT") + into a datetime object. If the value is already a datetime, return it as-is. + Falls back to ISO 8601 parsing if RFC 2822 parsing fails. + """ + if isinstance(v, dt.datetime): + return v + if isinstance(v, str): + try: + return parsedate_to_datetime(v) + except Exception: + pass + # Fallback to ISO 8601 parsing + return dt.datetime.fromisoformat(v.replace("Z", "+00:00")) + raise ValueError(f"Expected str or datetime, got {type(v)}") + + +class Rfc2822DateTime(dt.datetime): + """A datetime subclass that parses RFC 2822 date strings. + + On Pydantic V1, uses __get_validators__ for pre-validation. + On Pydantic V2, uses __get_pydantic_core_schema__ for BeforeValidator-style parsing. + """ + + @classmethod + def __get_validators__(cls): # type: ignore[no-untyped-def] + yield parse_rfc2822_datetime + + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: # type: ignore[override] + from pydantic_core import core_schema + + return core_schema.no_info_before_validator_function(parse_rfc2822_datetime, core_schema.datetime_schema()) + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/google/genai/_fern_interactions/core/file.py b/google/genai/_fern_interactions/core/file.py new file mode 100644 index 000000000..44b0d27c0 --- /dev/null +++ b/google/genai/_fern_interactions/core/file.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast + +# File typing inspired by the flexibility of types within the httpx library +# https://github.com/encode/httpx/blob/master/httpx/_types.py +FileContent = Union[IO[bytes], bytes, str] +File = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[ + Optional[str], + FileContent, + Optional[str], + Mapping[str, str], + ], +] + + +def convert_file_dict_to_httpx_tuples( + d: Dict[str, Union[File, List[File]]], +) -> List[Tuple[str, File]]: + """ + The format we use is a list of tuples, where the first element is the + name of the file and the second is the file object. Typically HTTPX wants + a dict, but to be able to send lists of files, you have to use the list + approach (which also works for non-lists) + https://github.com/encode/httpx/pull/1032 + """ + + httpx_tuples = [] + for key, file_like in d.items(): + if isinstance(file_like, list): + for file_like_item in file_like: + httpx_tuples.append((key, file_like_item)) + else: + httpx_tuples.append((key, file_like)) + return httpx_tuples + + +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ + if isinstance(file, tuple): + if len(file) == 2: + filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore + return (filename, content, default_content_type) + elif len(file) == 3: + filename, content, file_content_type = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) + elif len(file) == 4: + filename, content, file_content_type, headers = cast( # type: ignore + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file + ) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) + else: + raise ValueError(f"Unexpected tuple length: {len(file)}") + return (None, file, default_content_type) diff --git a/google/genai/_fern_interactions/core/force_multipart.py b/google/genai/_fern_interactions/core/force_multipart.py new file mode 100644 index 000000000..5440913fd --- /dev/null +++ b/google/genai/_fern_interactions/core/force_multipart.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + + +class ForceMultipartDict(Dict[str, Any]): + """ + A dictionary subclass that always evaluates to True in boolean contexts. + + This is used to force multipart/form-data encoding in HTTP requests even when + the dictionary is empty, which would normally evaluate to False. + """ + + def __bool__(self) -> bool: + return True + + +FORCE_MULTIPART = ForceMultipartDict() diff --git a/google/genai/_fern_interactions/core/http_client.py b/google/genai/_fern_interactions/core/http_client.py new file mode 100644 index 000000000..f686c5710 --- /dev/null +++ b/google/genai/_fern_interactions/core/http_client.py @@ -0,0 +1,839 @@ +# This file was auto-generated by Fern from our API Definition. + +import asyncio +import email.utils +import re +import time +import typing +from contextlib import asynccontextmanager, contextmanager +from random import random + +import httpx +from .file import File, convert_file_dict_to_httpx_tuples +from .force_multipart import FORCE_MULTIPART +from .jsonable_encoder import jsonable_encoder +from .logging import LogConfig, Logger, create_logger +from .query_encoder import encode_query +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict +from .request_options import RequestOptions +from httpx._types import RequestFiles + +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter + + +def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait. + + Inspired by the urllib3 retry implementation. + """ + retry_after_ms = response_headers.get("retry-after-ms") + if retry_after_ms is not None: + try: + return int(retry_after_ms) / 1000 if retry_after_ms > 0 else 0 + except Exception: + pass + + retry_after = response_headers.get("retry-after") + if retry_after is None: + return None + + # Attempt to parse the header as an int. + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = float(retry_after) + # Fallback to parsing it as a date. + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + return None + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + +def _retry_timeout(response: httpx.Response, retries: int) -> float: + """ + Determine the amount of time to wait before retrying a request. + This function begins by trying to parse a retry-after header from the response, and then proceeds to use exponential backoff + with a jitter to determine the number of seconds to wait. + """ + + # 1. Check Retry-After header first + retry_after = _parse_retry_after(response.headers) + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) + + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) + + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) + + +def _retry_timeout_from_retries(retries: int) -> float: + """Determine retry timeout using exponential backoff when no response is available.""" + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) + + +def _should_retry(response: httpx.Response) -> bool: + return response.status_code >= 500 or response.status_code in [429, 408, 409] + + +_SENSITIVE_HEADERS = frozenset( + { + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", + } +) + + +def _redact_headers(headers: typing.Dict[str, str]) -> typing.Dict[str, str]: + return {k: ("[REDACTED]" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()} + + +def _build_url(base_url: str, path: typing.Optional[str]) -> str: + """ + Build a full URL by joining a base URL with a path. + + This function correctly handles base URLs that contain path prefixes (e.g., tenant-based URLs) + by using string concatenation instead of urllib.parse.urljoin(), which would incorrectly + strip path components when the path starts with '/'. + + Example: + >>> _build_url("https://cloud.example.com/org/tenant/api", "/users") + 'https://cloud.example.com/org/tenant/api/users' + + Args: + base_url: The base URL, which may contain path prefixes. + path: The path to append. Can be None or empty string. + + Returns: + The full URL with base_url and path properly joined. + """ + if not path: + return base_url + return f"{base_url.rstrip('/')}/{path.lstrip('/')}" + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + +def remove_omit_from_dict( + original: typing.Dict[str, typing.Optional[typing.Any]], + omit: typing.Optional[typing.Any], +) -> typing.Dict[str, typing.Any]: + if omit is None: + return original + new: typing.Dict[str, typing.Any] = {} + for key, value in original.items(): + if value is not omit: + new[key] = value + return new + + +def maybe_filter_request_body( + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Optional[typing.Any]: + if data is None: + return ( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else None + ) + elif not isinstance(data, typing.Mapping): + data_content = jsonable_encoder(data) + else: + data_content = { + **(jsonable_encoder(remove_omit_from_dict(data, omit))), # type: ignore + **( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else {} + ), + } + return data_content + + +# Abstracted out for testing purposes +def get_request_body( + *, + json: typing.Optional[typing.Any], + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]: + json_body = None + data_body = None + if data is not None: + data_body = maybe_filter_request_body(data, request_options, omit) + else: + # If both data and json are None, we send json data in the event extra properties are specified + json_body = maybe_filter_request_body(json, request_options, omit) + + has_additional_body_parameters = bool( + request_options is not None and request_options.get("additional_body_parameters") + ) + + # Only collapse empty dict to None when the body was not explicitly provided + # and there are no additional body parameters. This preserves explicit empty + # bodies (e.g., when an endpoint has a request body type but all fields are optional). + if json_body == {} and json is None and not has_additional_body_parameters: + json_body = None + if data_body == {} and data is None and not has_additional_body_parameters: + data_body = None + + return json_body, data_body + + +class HttpClient: + def __init__( + self, + *, + httpx_client: httpx.Client, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + base_max_retries: int = 2, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.base_max_retries = base_max_retries + self.httpx_client = httpx_client + self.logger = create_logger(logging_config) + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 0, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + max_retries: int = ( + request_options.get("max_retries", self.base_max_retries) + if request_options is not None + else self.base_max_retries + ) + + try: + response = self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + except (httpx.ConnectError, httpx.RemoteProtocolError): + if retries < max_retries: + time.sleep(_retry_timeout_from_retries(retries=retries)) + return self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + data=data, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + force_multipart=force_multipart, + ) + raise + + if _should_retry(response=response): + if retries < max_retries: + time.sleep(_retry_timeout(response=response, retries=retries)) + return self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + data=data, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + force_multipart=force_multipart, + ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + return response + + @contextmanager + def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 0, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.Iterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream + + +class AsyncHttpClient: + def __init__( + self, + *, + httpx_client: httpx.AsyncClient, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + base_max_retries: int = 2, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.base_max_retries = base_max_retries + self.async_base_headers = async_base_headers + self.httpx_client = httpx_client + self.logger = create_logger(logging_config) + + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + async def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 0, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + max_retries: int = ( + request_options.get("max_retries", self.base_max_retries) + if request_options is not None + else self.base_max_retries + ) + + try: + response = await self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + except (httpx.ConnectError, httpx.RemoteProtocolError): + if retries < max_retries: + await asyncio.sleep(_retry_timeout_from_retries(retries=retries)) + return await self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + data=data, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + force_multipart=force_multipart, + ) + raise + + if _should_retry(response=response): + if retries < max_retries: + await asyncio.sleep(_retry_timeout(response=response, retries=retries)) + return await self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + data=data, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + force_multipart=force_multipart, + ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + return response + + @asynccontextmanager + async def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 0, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.AsyncIterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + async with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream diff --git a/google/genai/_fern_interactions/core/http_response.py b/google/genai/_fern_interactions/core/http_response.py new file mode 100644 index 000000000..00bb1096d --- /dev/null +++ b/google/genai/_fern_interactions/core/http_response.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Dict, Generic, TypeVar + +import httpx + +# Generic to represent the underlying type of the data wrapped by the HTTP response. +T = TypeVar("T") + + +class BaseHttpResponse: + """Minimalist HTTP response wrapper that exposes response headers and status code.""" + + _response: httpx.Response + + def __init__(self, response: httpx.Response): + self._response = response + + @property + def headers(self) -> Dict[str, str]: + return dict(self._response.headers) + + @property + def status_code(self) -> int: + return self._response.status_code + + +class HttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + def close(self) -> None: + self._response.close() + + +class AsyncHttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + async def close(self) -> None: + await self._response.aclose() diff --git a/google/genai/_fern_interactions/core/http_sse/__init__.py b/google/genai/_fern_interactions/core/http_sse/__init__.py new file mode 100644 index 000000000..730e5a338 --- /dev/null +++ b/google/genai/_fern_interactions/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/google/genai/_fern_interactions/core/http_sse/_api.py b/google/genai/_fern_interactions/core/http_sse/_api.py new file mode 100644 index 000000000..b37539914 --- /dev/null +++ b/google/genai/_fern_interactions/core/http_sse/_api.py @@ -0,0 +1,148 @@ +# This file was auto-generated by Fern from our API Definition. + +import codecs +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + @staticmethod + def _normalize_sse_line_endings(buf: str) -> str: + """Normalize line endings per the SSE spec (\\r\\n → \\n, bare \\r → \\n). + + A trailing \\r is preserved because it may pair with a leading \\n in + the next chunk to form a single \\r\\n terminator. + """ + buf = buf.replace("\r\n", "\n") + if buf.endswith("\r"): + return buf[:-1].replace("\r", "\n") + "\r" + return buf.replace("\r", "\n") + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + text_decoder = codecs.getincrementaldecoder(charset)(errors="replace") + + buf = "" + for chunk in self._response.iter_bytes(): + buf += text_decoder.decode(chunk) + buf = self._normalize_sse_line_endings(buf) + + while "\n" in buf: + line, buf = buf.split("\n", 1) + sse = decoder.decode(line) + if sse is not None: + yield sse + + # Flush any remaining bytes from the incremental decoder + buf += text_decoder.decode(b"", final=True) + buf = buf.replace("\r\n", "\n").replace("\r", "\n") + + while "\n" in buf: + line, buf = buf.split("\n", 1) + sse = decoder.decode(line) + if sse is not None: + yield sse + + if buf.strip(): + sse = decoder.decode(buf) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + text_decoder = codecs.getincrementaldecoder(charset)(errors="replace") + + buf = "" + async for chunk in self._response.aiter_bytes(): + buf += text_decoder.decode(chunk) + buf = self._normalize_sse_line_endings(buf) + + while "\n" in buf: + line, buf = buf.split("\n", 1) + sse = decoder.decode(line) + if sse is not None: + yield sse + + # Flush any remaining bytes from the incremental decoder + buf += text_decoder.decode(b"", final=True) + buf = buf.replace("\r\n", "\n").replace("\r", "\n") + + while "\n" in buf: + line, buf = buf.split("\n", 1) + sse = decoder.decode(line) + if sse is not None: + yield sse + + if buf.strip(): + sse = decoder.decode(buf) + if sse is not None: + yield sse + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/google/genai/_fern_interactions/core/http_sse/_decoders.py b/google/genai/_fern_interactions/core/http_sse/_decoders.py new file mode 100644 index 000000000..339b08901 --- /dev/null +++ b/google/genai/_fern_interactions/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/google/genai/_fern_interactions/core/http_sse/_exceptions.py b/google/genai/_fern_interactions/core/http_sse/_exceptions.py new file mode 100644 index 000000000..81605a8a6 --- /dev/null +++ b/google/genai/_fern_interactions/core/http_sse/_exceptions.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/google/genai/_fern_interactions/core/http_sse/_models.py b/google/genai/_fern_interactions/core/http_sse/_models.py new file mode 100644 index 000000000..1af57f8fd --- /dev/null +++ b/google/genai/_fern_interactions/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/google/genai/_fern_interactions/core/jsonable_encoder.py b/google/genai/_fern_interactions/core/jsonable_encoder.py new file mode 100644 index 000000000..5b0902ebc --- /dev/null +++ b/google/genai/_fern_interactions/core/jsonable_encoder.py @@ -0,0 +1,120 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +jsonable_encoder converts a Python object to a JSON-friendly dict +(e.g. datetimes to strings, Pydantic models to dicts). + +Taken from FastAPI, and made a bit simpler +https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py +""" + +import base64 +import dataclasses +import datetime as dt +from enum import Enum +from pathlib import PurePath +from types import GeneratorType +from typing import Any, Callable, Dict, List, Optional, Set, Union + +import pydantic +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + encode_by_type, + to_jsonable_with_fallback, +) + +SetIntStr = Set[Union[int, str]] +DictIntStrAny = Dict[Union[int, str], Any] + + +def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: + custom_encoder = custom_encoder or {} + # Generated SDKs use Ellipsis (`...`) as the sentinel value for "OMIT". + # OMIT values should be excluded from serialized payloads. + if obj is Ellipsis: + return None + if custom_encoder: + if type(obj) in custom_encoder: + return custom_encoder[type(obj)](obj) + else: + for encoder_type, encoder_instance in custom_encoder.items(): + if isinstance(obj, encoder_type): + return encoder_instance(obj) + if isinstance(obj, pydantic.BaseModel): + if IS_PYDANTIC_V2: + encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2 + else: + encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1 + if custom_encoder: + encoder.update(custom_encoder) + obj_dict = obj.dict(by_alias=True) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + if "root" in obj_dict: + obj_dict = obj_dict["root"] + return jsonable_encoder(obj_dict, custom_encoder=encoder) + if dataclasses.is_dataclass(obj): + obj_dict = dataclasses.asdict(obj) # type: ignore + return jsonable_encoder(obj_dict, custom_encoder=custom_encoder) + if isinstance(obj, bytes): + return base64.b64encode(obj).decode("utf-8") + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (str, int, float, type(None))): + return obj + if isinstance(obj, dt.datetime): + return serialize_datetime(obj) + if isinstance(obj, dt.date): + return str(obj) + if isinstance(obj, dict): + encoded_dict = {} + allowed_keys = set(obj.keys()) + for key, value in obj.items(): + if key in allowed_keys: + if value is Ellipsis: + continue + encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) + encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): + encoded_list = [] + for item in obj: + if item is Ellipsis: + continue + encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) + return encoded_list + + def fallback_serializer(o: Any) -> Any: + attempt_encode = encode_by_type(o) + if attempt_encode is not None: + return attempt_encode + + try: + data = dict(o) + except Exception as e: + errors: List[Exception] = [] + errors.append(e) + try: + data = vars(o) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder(data, custom_encoder=custom_encoder) + + return to_jsonable_with_fallback(obj, fallback_serializer) + + +def encode_path_param(obj: Any) -> str: + """Encode a value for use in a URL path segment. + + Ensures proper string conversion for all types, including + booleans which need lowercase 'true'/'false' rather than + Python's 'True'/'False'. + """ + if isinstance(obj, bool): + return "true" if obj else "false" + return str(jsonable_encoder(obj)) diff --git a/google/genai/_fern_interactions/core/logging.py b/google/genai/_fern_interactions/core/logging.py new file mode 100644 index 000000000..e5e572458 --- /dev/null +++ b/google/genai/_fern_interactions/core/logging.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import logging +import typing + +LogLevel = typing.Literal["debug", "info", "warn", "error"] + +_LOG_LEVEL_MAP: typing.Dict[LogLevel, int] = { + "debug": 1, + "info": 2, + "warn": 3, + "error": 4, +} + + +class ILogger(typing.Protocol): + def debug(self, message: str, **kwargs: typing.Any) -> None: ... + def info(self, message: str, **kwargs: typing.Any) -> None: ... + def warn(self, message: str, **kwargs: typing.Any) -> None: ... + def error(self, message: str, **kwargs: typing.Any) -> None: ... + + +class ConsoleLogger: + _logger: logging.Logger + + def __init__(self) -> None: + self._logger = logging.getLogger("fern") + if not self._logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) + + def debug(self, message: str, **kwargs: typing.Any) -> None: + self._logger.debug(message, extra=kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + self._logger.info(message, extra=kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + self._logger.warning(message, extra=kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + self._logger.error(message, extra=kwargs) + + +class LogConfig(typing.TypedDict, total=False): + level: LogLevel + logger: ILogger + silent: bool + + +class Logger: + _level: int + _logger: ILogger + _silent: bool + + def __init__(self, *, level: LogLevel, logger: ILogger, silent: bool) -> None: + self._level = _LOG_LEVEL_MAP[level] + self._logger = logger + self._silent = silent + + def _should_log(self, level: LogLevel) -> bool: + return not self._silent and self._level <= _LOG_LEVEL_MAP[level] + + def is_debug(self) -> bool: + return self._should_log("debug") + + def is_info(self) -> bool: + return self._should_log("info") + + def is_warn(self) -> bool: + return self._should_log("warn") + + def is_error(self) -> bool: + return self._should_log("error") + + def debug(self, message: str, **kwargs: typing.Any) -> None: + if self.is_debug(): + self._logger.debug(message, **kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + if self.is_info(): + self._logger.info(message, **kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + if self.is_warn(): + self._logger.warn(message, **kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + if self.is_error(): + self._logger.error(message, **kwargs) + + +_default_logger: Logger = Logger(level="info", logger=ConsoleLogger(), silent=True) + + +def create_logger(config: typing.Optional[typing.Union[LogConfig, Logger]] = None) -> Logger: + if config is None: + return _default_logger + if isinstance(config, Logger): + return config + return Logger( + level=config.get("level", "info"), + logger=config.get("logger", ConsoleLogger()), + silent=config.get("silent", True), + ) diff --git a/google/genai/_fern_interactions/core/parse_error.py b/google/genai/_fern_interactions/core/parse_error.py new file mode 100644 index 000000000..4527c6a8a --- /dev/null +++ b/google/genai/_fern_interactions/core/parse_error.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ParsingError(Exception): + """ + Raised when the SDK fails to parse/validate a response from the server. + This typically indicates that the server returned a response whose shape + does not match the expected schema. + """ + + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + cause: Optional[Exception] + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + cause: Optional[Exception] = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + self.cause = cause + super().__init__() + if cause is not None: + self.__cause__ = cause + + def __str__(self) -> str: + cause_str = f", cause: {self.cause}" if self.cause is not None else "" + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}{cause_str}" diff --git a/google/genai/_fern_interactions/core/pydantic_utilities.py b/google/genai/_fern_interactions/core/pydantic_utilities.py new file mode 100644 index 000000000..fea3a08d3 --- /dev/null +++ b/google/genai/_fern_interactions/core/pydantic_utilities.py @@ -0,0 +1,634 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +import inspect +import json +import logging +from collections import defaultdict +from dataclasses import asdict +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +import pydantic +import typing_extensions +from pydantic.fields import FieldInfo as _FieldInfo + +_logger = logging.getLogger(__name__) + +if TYPE_CHECKING: + from .http_sse._models import ServerSentEvent + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + _datetime_adapter = pydantic.TypeAdapter(dt.datetime) # type: ignore[attr-defined] + _date_adapter = pydantic.TypeAdapter(dt.date) # type: ignore[attr-defined] + + def parse_datetime(value: Any) -> dt.datetime: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value + return _datetime_adapter.validate_python(value) + + def parse_date(value: Any) -> dt.date: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value.date() + if isinstance(value, dt.date): + return value + return _date_adapter.validate_python(value) + + # Avoid importing from pydantic.v1 to maintain Python 3.14 compatibility. + from typing import get_args as get_args # type: ignore[assignment] + from typing import get_origin as get_origin # type: ignore[assignment] + + def is_literal_type(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return typing_extensions.get_origin(tp) is typing_extensions.Literal + + def is_union(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return tp is Union or typing_extensions.get_origin(tp) is Union # type: ignore[comparison-overlap] + + # Inline encoders_by_type to avoid importing from pydantic.v1.json + import re as _re + from collections import deque as _deque + from decimal import Decimal as _Decimal + from enum import Enum as _Enum + from ipaddress import ( + IPv4Address as _IPv4Address, + ) + from ipaddress import ( + IPv4Interface as _IPv4Interface, + ) + from ipaddress import ( + IPv4Network as _IPv4Network, + ) + from ipaddress import ( + IPv6Address as _IPv6Address, + ) + from ipaddress import ( + IPv6Interface as _IPv6Interface, + ) + from ipaddress import ( + IPv6Network as _IPv6Network, + ) + from pathlib import Path as _Path + from types import GeneratorType as _GeneratorType + from uuid import UUID as _UUID + + from pydantic.fields import FieldInfo as ModelField # type: ignore[no-redef, assignment] + + def _decimal_encoder(dec_value: Any) -> Any: + if dec_value.as_tuple().exponent >= 0: + return int(dec_value) + return float(dec_value) + + encoders_by_type: Dict[Type[Any], Callable[[Any], Any]] = { # type: ignore[no-redef] + bytes: lambda o: o.decode(), + dt.date: lambda o: o.isoformat(), + dt.datetime: lambda o: o.isoformat(), + dt.time: lambda o: o.isoformat(), + dt.timedelta: lambda td: td.total_seconds(), + _Decimal: _decimal_encoder, + _Enum: lambda o: o.value, + frozenset: list, + _deque: list, + _GeneratorType: list, + _IPv4Address: str, + _IPv4Interface: str, + _IPv4Network: str, + _IPv6Address: str, + _IPv6Interface: str, + _IPv6Network: str, + _Path: str, + _re.Pattern: lambda o: o.pattern, + set: list, + _UUID: str, + } +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef, assignment] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef, assignment] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from .serialization import convert_and_respect_annotation_metadata +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def _get_discriminator_and_variants(type_: Type[Any]) -> Tuple[Optional[str], Optional[List[Type[Any]]]]: + """ + Extract the discriminator field name and union variants from a discriminated union type. + Supports Annotated[Union[...], Field(discriminator=...)] patterns. + Returns (discriminator, variants) or (None, None) if not a discriminated union. + """ + origin = typing_extensions.get_origin(type_) + + if origin is typing_extensions.Annotated: + args = typing_extensions.get_args(type_) + if len(args) >= 2: + inner_type = args[0] + # Check annotations for discriminator + discriminator = None + for annotation in args[1:]: + if hasattr(annotation, "discriminator"): + discriminator = getattr(annotation, "discriminator", None) + break + + if discriminator: + inner_origin = typing_extensions.get_origin(inner_type) + if inner_origin is Union: + variants = list(typing_extensions.get_args(inner_type)) + return discriminator, variants + return None, None + + +def _get_field_annotation(model: Type[Any], field_name: str) -> Optional[Type[Any]]: + """Get the type annotation of a field from a Pydantic model.""" + if IS_PYDANTIC_V2: + fields = getattr(model, "model_fields", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.annotation) + else: + fields = getattr(model, "__fields__", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.outer_type_) + return None + + +def _find_variant_by_discriminator( + variants: List[Type[Any]], + discriminator: str, + discriminator_value: Any, +) -> Optional[Type[Any]]: + """Find the union variant that matches the discriminator value.""" + for variant in variants: + if not (inspect.isclass(variant) and issubclass(variant, pydantic.BaseModel)): + continue + + disc_annotation = _get_field_annotation(variant, discriminator) + if disc_annotation and is_literal_type(disc_annotation): + literal_args = get_args(disc_annotation) + if literal_args and literal_args[0] == discriminator_value: + return variant + return None + + +def _is_string_type(type_: Type[Any]) -> bool: + """Check if a type is str or Optional[str].""" + if type_ is str: + return True + + origin = typing_extensions.get_origin(type_) + if origin is Union: + args = typing_extensions.get_args(type_) + # Optional[str] = Union[str, None] + non_none_args = [a for a in args if a is not type(None)] + if len(non_none_args) == 1 and non_none_args[0] is str: + return True + + return False + + +def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T: + """ + Parse a ServerSentEvent into the appropriate type. + + Handles two scenarios based on where the discriminator field is located: + + 1. Data-level discrimination: The discriminator (e.g., 'type') is inside the 'data' payload. + The union describes the data content, not the SSE envelope. + -> Returns: json.loads(data) parsed into the type + + Example: ChatStreamResponse with discriminator='type' + Input: ServerSentEvent(event="message", data='{"type": "content-delta", ...}', id="") + Output: ContentDeltaEvent (parsed from data, SSE envelope stripped) + + 2. Event-level discrimination: The discriminator (e.g., 'event') is at the SSE event level. + The union describes the full SSE event structure. + -> Returns: SSE envelope with 'data' field JSON-parsed only if the variant expects non-string + + Example: JobStreamResponse with discriminator='event' + Input: ServerSentEvent(event="ERROR", data='{"code": "FAILED", ...}', id="123") + Output: JobStreamResponse_Error with data as ErrorData object + + But for variants where data is str (like STATUS_UPDATE): + Input: ServerSentEvent(event="STATUS_UPDATE", data='{"status": "processing"}', id="1") + Output: JobStreamResponse_StatusUpdate with data as string (not parsed) + + Args: + sse: The ServerSentEvent object to parse + type_: The target discriminated union type + + Returns: + The parsed object of type T + + Note: + This function is only available in SDK contexts where http_sse module exists. + """ + sse_event = asdict(sse) + discriminator, variants = _get_discriminator_and_variants(type_) + + if discriminator is None or variants is None: + # Not a discriminated union - parse the data field as JSON + data_value = sse_event.get("data") + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + data_value = sse_event.get("data") + + # Check if discriminator is at the top level (event-level discrimination) + if discriminator in sse_event: + # Case 2: Event-level discrimination + # Find the matching variant to check if 'data' field needs JSON parsing + disc_value = sse_event.get(discriminator) + matching_variant = _find_variant_by_discriminator(variants, discriminator, disc_value) + + if matching_variant is not None: + # Check what type the variant expects for 'data' + data_type = _get_field_annotation(matching_variant, "data") + if data_type is not None and not _is_string_type(data_type): + # Variant expects non-string data - parse JSON + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + new_object = dict(sse_event) + new_object["data"] = parsed_data + return parse_obj_as(type_, new_object) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for event-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + # Either no matching variant, data is string type, or JSON parse failed + return parse_obj_as(type_, sse_event) + + else: + # Case 1: Data-level discrimination + # The discriminator is inside the data payload - extract and parse data only + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for data-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + # convert_and_respect_annotation_metadata is required for TypedDict aliasing. + # + # For Pydantic models, whether we should pre-dealias depends on how the model encodes aliasing: + # - If the model uses real Pydantic aliases (pydantic.Field(alias=...)), then we must pass wire keys through + # unchanged so Pydantic can validate them. + # - If the model encodes aliasing only via FieldMetadata annotations, then we MUST pre-dealias because Pydantic + # will not recognize those aliases during validation. + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + has_pydantic_aliases = False + if IS_PYDANTIC_V2: + for field_name, field_info in getattr(type_, "model_fields", {}).items(): # type: ignore[attr-defined] + alias = getattr(field_info, "alias", None) + if alias is not None and alias != field_name: + has_pydantic_aliases = True + break + else: + for field in getattr(type_, "__fields__", {}).values(): + alias = getattr(field, "alias", None) + name = getattr(field, "name", None) + if alias is not None and name is not None and alias != name: + has_pydantic_aliases = True + break + + dealiased_object = ( + object_ + if has_pydantic_aliases + else convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + ) + else: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(dealiased_object) + return pydantic.parse_obj_as(type_, dealiased_object) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + if IS_PYDANTIC_V2: + model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key] + # Allow fields beginning with `model_` to be used in the model + protected_namespaces=(), + ) + + @pydantic.model_validator(mode="before") # type: ignore[attr-defined] + @classmethod + def _coerce_field_names_to_aliases(cls, data: Any) -> Any: + """ + Accept Python field names in input by rewriting them to their Pydantic aliases, + while avoiding silent collisions when a key could refer to multiple fields. + """ + if not isinstance(data, Mapping): + return data + + fields = getattr(cls, "model_fields", {}) # type: ignore[attr-defined] + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field_info in fields.items(): + alias = getattr(field_info, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + # Detect ambiguous keys: a key that is an alias for one field and a name for another. + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in data and name_to_alias[key] not in data: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(data.keys()) + rewritten: Dict[str, Any] = dict(data) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] + def serialize_model(self) -> Any: # type: ignore[name-defined] + serialized = self.dict() # type: ignore[attr-defined] + data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} + return data + + else: + + class Config: + smart_union = True + json_encoders = {dt.datetime: serialize_datetime} + + @pydantic.root_validator(pre=True) + def _coerce_field_names_to_aliases(cls, values: Any) -> Any: + """ + Pydantic v1 equivalent of _coerce_field_names_to_aliases. + """ + if not isinstance(values, Mapping): + return values + + fields = getattr(cls, "__fields__", {}) + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field in fields.items(): + alias = getattr(field, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in values and name_to_alias[key] not in values: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(values.keys()) + rewritten: Dict[str, Any] = dict(values) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + + @classmethod + def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + return cls.construct(_fields_set, **dealiased_object) + + @classmethod + def construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + if IS_PYDANTIC_V2: + return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc] + return super().construct(_fields_set, **dealiased_object) + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + dict_dump = deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + else: + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc] +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, _FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/google/genai/_fern_interactions/core/query_encoder.py b/google/genai/_fern_interactions/core/query_encoder.py new file mode 100644 index 000000000..3183001d4 --- /dev/null +++ b/google/genai/_fern_interactions/core/query_encoder.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, List, Optional, Tuple + +import pydantic + + +# Flattens dicts to be of the form {"key[subkey][subkey2]": value} where value is not a dict +def traverse_query_dict(dict_flat: Dict[str, Any], key_prefix: Optional[str] = None) -> List[Tuple[str, Any]]: + result = [] + for k, v in dict_flat.items(): + key = f"{key_prefix}[{k}]" if key_prefix is not None else k + if isinstance(v, dict): + result.extend(traverse_query_dict(v, key)) + elif isinstance(v, list): + for arr_v in v: + if isinstance(arr_v, dict): + result.extend(traverse_query_dict(arr_v, key)) + else: + result.append((key, arr_v)) + else: + result.append((key, v)) + return result + + +def single_query_encoder(query_key: str, query_value: Any) -> List[Tuple[str, Any]]: + if isinstance(query_value, pydantic.BaseModel) or isinstance(query_value, dict): + if isinstance(query_value, pydantic.BaseModel): + obj_dict = query_value.dict(by_alias=True) + else: + obj_dict = query_value + return traverse_query_dict(obj_dict, query_key) + elif isinstance(query_value, list): + encoded_values: List[Tuple[str, Any]] = [] + for value in query_value: + if isinstance(value, pydantic.BaseModel) or isinstance(value, dict): + if isinstance(value, pydantic.BaseModel): + obj_dict = value.dict(by_alias=True) + elif isinstance(value, dict): + obj_dict = value + + encoded_values.extend(single_query_encoder(query_key, obj_dict)) + else: + encoded_values.append((query_key, value)) + + return encoded_values + + return [(query_key, query_value)] + + +def encode_query(query: Optional[Dict[str, Any]]) -> Optional[List[Tuple[str, Any]]]: + if query is None: + return None + + encoded_query = [] + for k, v in query.items(): + encoded_query.extend(single_query_encoder(k, v)) + return encoded_query diff --git a/google/genai/_fern_interactions/core/remove_none_from_dict.py b/google/genai/_fern_interactions/core/remove_none_from_dict.py new file mode 100644 index 000000000..c2298143f --- /dev/null +++ b/google/genai/_fern_interactions/core/remove_none_from_dict.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Mapping, Optional + + +def remove_none_from_dict(original: Mapping[str, Optional[Any]]) -> Dict[str, Any]: + new: Dict[str, Any] = {} + for key, value in original.items(): + if value is not None: + new[key] = value + return new diff --git a/google/genai/_fern_interactions/core/request_options.py b/google/genai/_fern_interactions/core/request_options.py new file mode 100644 index 000000000..1b3880443 --- /dev/null +++ b/google/genai/_fern_interactions/core/request_options.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +try: + from typing import NotRequired # type: ignore +except ImportError: + from typing_extensions import NotRequired + + +class RequestOptions(typing.TypedDict, total=False): + """ + Additional options for request-specific configuration when calling APIs via the SDK. + This is used primarily as an optional final parameter for service functions. + + Attributes: + - timeout_in_seconds: int. The number of seconds to await an API call before timing out. + + - max_retries: int. The max number of retries to attempt if the API call fails. + + - additional_headers: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's header dict + + - additional_query_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's query parameters dict + + - additional_body_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's body parameters dict + + - chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads. + """ + + timeout_in_seconds: NotRequired[int] + max_retries: NotRequired[int] + additional_headers: NotRequired[typing.Dict[str, typing.Any]] + additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]] + additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]] + chunk_size: NotRequired[int] diff --git a/google/genai/_fern_interactions/core/serialization.py b/google/genai/_fern_interactions/core/serialization.py new file mode 100644 index 000000000..c36e865cc --- /dev/null +++ b/google/genai/_fern_interactions/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/google/genai/_fern_interactions/environment.py b/google/genai/_fern_interactions/environment.py new file mode 100644 index 000000000..9baff0fad --- /dev/null +++ b/google/genai/_fern_interactions/environment.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum + + +class GeminiNextGenAPIClientEnvironment(enum.Enum): + DEFAULT = "https://generativelanguage.googleapis.com" diff --git a/google/genai/_fern_interactions/fern_agents/__init__.py b/google/genai/_fern_interactions/fern_agents/__init__.py new file mode 100644 index 000000000..5cde0202d --- /dev/null +++ b/google/genai/_fern_interactions/fern_agents/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/google/genai/_fern_interactions/fern_agents/client.py b/google/genai/_fern_interactions/fern_agents/client.py new file mode 100644 index 000000000..7b2c1a90f --- /dev/null +++ b/google/genai/_fern_interactions/fern_agents/client.py @@ -0,0 +1,413 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.agent import Agent +from ..types.agent_base_environment import AgentBaseEnvironment +from ..types.agent_tool import AgentTool +from ..types.empty import Empty +from ..types.list_agents_response import ListAgentsResponse +from .raw_client import AsyncRawFernAgentsClient, RawFernAgentsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class FernAgentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawFernAgentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawFernAgentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawFernAgentsClient + """ + return self._raw_client + + def list( + self, + *, + parent: typing.Optional[str] = None, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ListAgentsResponse: + """ + Lists all Agents. + + Parameters + ---------- + parent : typing.Optional[str] + + page_size : typing.Optional[int] + + page_token : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListAgentsResponse + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_agents.list() + """ + _response = self._raw_client.list( + parent=parent, page_size=page_size, page_token=page_token, request_options=request_options + ) + return _response.data + + def create( + self, + *, + base_agent: typing.Optional[str] = OMIT, + base_environment: typing.Optional[AgentBaseEnvironment] = OMIT, + description: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + system_instruction: typing.Optional[str] = OMIT, + tools: typing.Optional[typing.Sequence[AgentTool]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Agent: + """ + Creates a new Agent (Typed version for SDK). + + Parameters + ---------- + base_agent : typing.Optional[str] + The base agent to extend. + + base_environment : typing.Optional[AgentBaseEnvironment] + The environment configuration for the agent. + + description : typing.Optional[str] + Agent description for developers to quickly read and understand. + + id : typing.Optional[str] + The unique identifier for the agent. + + system_instruction : typing.Optional[str] + System instruction for the agent. + + tools : typing.Optional[typing.Sequence[AgentTool]] + The tools available to the agent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Agent + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_agents.create() + """ + _response = self._raw_client.create( + base_agent=base_agent, + base_environment=base_environment, + description=description, + id=id, + system_instruction=system_instruction, + tools=tools, + request_options=request_options, + ) + return _response.data + + def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Agent: + """ + Gets a specific Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Agent + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_agents.get( + id="id", + ) + """ + _response = self._raw_client.get(id, request_options=request_options) + return _response.data + + def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Empty: + """ + Deletes an Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Empty + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_agents.delete( + id="id", + ) + """ + _response = self._raw_client.delete(id, request_options=request_options) + return _response.data + + +class AsyncFernAgentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawFernAgentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawFernAgentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawFernAgentsClient + """ + return self._raw_client + + async def list( + self, + *, + parent: typing.Optional[str] = None, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ListAgentsResponse: + """ + Lists all Agents. + + Parameters + ---------- + parent : typing.Optional[str] + + page_size : typing.Optional[int] + + page_token : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListAgentsResponse + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_agents.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + parent=parent, page_size=page_size, page_token=page_token, request_options=request_options + ) + return _response.data + + async def create( + self, + *, + base_agent: typing.Optional[str] = OMIT, + base_environment: typing.Optional[AgentBaseEnvironment] = OMIT, + description: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + system_instruction: typing.Optional[str] = OMIT, + tools: typing.Optional[typing.Sequence[AgentTool]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Agent: + """ + Creates a new Agent (Typed version for SDK). + + Parameters + ---------- + base_agent : typing.Optional[str] + The base agent to extend. + + base_environment : typing.Optional[AgentBaseEnvironment] + The environment configuration for the agent. + + description : typing.Optional[str] + Agent description for developers to quickly read and understand. + + id : typing.Optional[str] + The unique identifier for the agent. + + system_instruction : typing.Optional[str] + System instruction for the agent. + + tools : typing.Optional[typing.Sequence[AgentTool]] + The tools available to the agent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Agent + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_agents.create() + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + base_agent=base_agent, + base_environment=base_environment, + description=description, + id=id, + system_instruction=system_instruction, + tools=tools, + request_options=request_options, + ) + return _response.data + + async def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Agent: + """ + Gets a specific Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Agent + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_agents.get( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(id, request_options=request_options) + return _response.data + + async def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Empty: + """ + Deletes an Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Empty + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_agents.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(id, request_options=request_options) + return _response.data diff --git a/google/genai/_fern_interactions/fern_agents/raw_client.py b/google/genai/_fern_interactions/fern_agents/raw_client.py new file mode 100644 index 000000000..3153afb40 --- /dev/null +++ b/google/genai/_fern_interactions/fern_agents/raw_client.py @@ -0,0 +1,466 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.jsonable_encoder import encode_path_param +from ..core.parse_error import ParsingError +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..types.agent import Agent +from ..types.agent_base_environment import AgentBaseEnvironment +from ..types.agent_tool import AgentTool +from ..types.empty import Empty +from ..types.list_agents_response import ListAgentsResponse +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawFernAgentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + parent: typing.Optional[str] = None, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ListAgentsResponse]: + """ + Lists all Agents. + + Parameters + ---------- + parent : typing.Optional[str] + + page_size : typing.Optional[int] + + page_token : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ListAgentsResponse] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents", + method="GET", + params={ + "parent": parent, + "pageSize": page_size, + "pageToken": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListAgentsResponse, + parse_obj_as( + type_=ListAgentsResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def create( + self, + *, + base_agent: typing.Optional[str] = OMIT, + base_environment: typing.Optional[AgentBaseEnvironment] = OMIT, + description: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + system_instruction: typing.Optional[str] = OMIT, + tools: typing.Optional[typing.Sequence[AgentTool]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Agent]: + """ + Creates a new Agent (Typed version for SDK). + + Parameters + ---------- + base_agent : typing.Optional[str] + The base agent to extend. + + base_environment : typing.Optional[AgentBaseEnvironment] + The environment configuration for the agent. + + description : typing.Optional[str] + Agent description for developers to quickly read and understand. + + id : typing.Optional[str] + The unique identifier for the agent. + + system_instruction : typing.Optional[str] + System instruction for the agent. + + tools : typing.Optional[typing.Sequence[AgentTool]] + The tools available to the agent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Agent] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents", + method="POST", + json={ + "base_agent": base_agent, + "base_environment": convert_and_respect_annotation_metadata( + object_=base_environment, annotation=AgentBaseEnvironment, direction="write" + ), + "description": description, + "id": id, + "system_instruction": system_instruction, + "tools": convert_and_respect_annotation_metadata( + object_=tools, annotation=typing.Sequence[AgentTool], direction="write" + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Agent, + parse_obj_as( + type_=Agent, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Agent]: + """ + Gets a specific Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Agent] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents/{encode_path_param(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Agent, + parse_obj_as( + type_=Agent, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Empty]: + """ + Deletes an Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Empty] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Empty, + parse_obj_as( + type_=Empty, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawFernAgentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + parent: typing.Optional[str] = None, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ListAgentsResponse]: + """ + Lists all Agents. + + Parameters + ---------- + parent : typing.Optional[str] + + page_size : typing.Optional[int] + + page_token : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ListAgentsResponse] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents", + method="GET", + params={ + "parent": parent, + "pageSize": page_size, + "pageToken": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListAgentsResponse, + parse_obj_as( + type_=ListAgentsResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create( + self, + *, + base_agent: typing.Optional[str] = OMIT, + base_environment: typing.Optional[AgentBaseEnvironment] = OMIT, + description: typing.Optional[str] = OMIT, + id: typing.Optional[str] = OMIT, + system_instruction: typing.Optional[str] = OMIT, + tools: typing.Optional[typing.Sequence[AgentTool]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Agent]: + """ + Creates a new Agent (Typed version for SDK). + + Parameters + ---------- + base_agent : typing.Optional[str] + The base agent to extend. + + base_environment : typing.Optional[AgentBaseEnvironment] + The environment configuration for the agent. + + description : typing.Optional[str] + Agent description for developers to quickly read and understand. + + id : typing.Optional[str] + The unique identifier for the agent. + + system_instruction : typing.Optional[str] + System instruction for the agent. + + tools : typing.Optional[typing.Sequence[AgentTool]] + The tools available to the agent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Agent] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents", + method="POST", + json={ + "base_agent": base_agent, + "base_environment": convert_and_respect_annotation_metadata( + object_=base_environment, annotation=AgentBaseEnvironment, direction="write" + ), + "description": description, + "id": id, + "system_instruction": system_instruction, + "tools": convert_and_respect_annotation_metadata( + object_=tools, annotation=typing.Sequence[AgentTool], direction="write" + ), + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Agent, + parse_obj_as( + type_=Agent, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Agent]: + """ + Gets a specific Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Agent] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents/{encode_path_param(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Agent, + parse_obj_as( + type_=Agent, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Empty]: + """ + Deletes an Agent. + + Parameters + ---------- + id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Empty] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/agents/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Empty, + parse_obj_as( + type_=Empty, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/google/genai/_fern_interactions/fern_interactions/__init__.py b/google/genai/_fern_interactions/fern_interactions/__init__.py new file mode 100644 index 000000000..9c857fe43 --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/__init__.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CreateFernInteractionsRequest, CreateFernInteractionsStreamRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateFernInteractionsRequest": ".types", + "CreateFernInteractionsStreamRequest": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateFernInteractionsRequest", "CreateFernInteractionsStreamRequest"] diff --git a/google/genai/_fern_interactions/fern_interactions/client.py b/google/genai/_fern_interactions/fern_interactions/client.py new file mode 100644 index 000000000..975e84b63 --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/client.py @@ -0,0 +1,612 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.interaction import Interaction +from ..types.interaction_sse_event import InteractionSseEvent +from .raw_client import AsyncRawFernInteractionsClient, RawFernInteractionsClient +from .types.create_fern_interactions_request import CreateFernInteractionsRequest +from .types.create_fern_interactions_stream_request import CreateFernInteractionsStreamRequest + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class FernInteractionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawFernInteractionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawFernInteractionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawFernInteractionsClient + """ + return self._raw_client + + def create_stream( + self, *, request: CreateFernInteractionsStreamRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Iterator[InteractionSseEvent]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsStreamRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[InteractionSseEvent] + + + Examples + -------- + from google.genai._fern_interactions import ( + CreateModelInteractionParams, + GeminiNextGenAPIClient, + TextContent, + ) + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + response = client.fern_interactions.create_stream( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent( + text="text", + type="text", + ), + ), + ) + for chunk in response: + yield chunk + """ + with self._raw_client.create_stream(request=request, request_options=request_options) as r: + yield from r.data + + def create( + self, *, request: CreateFernInteractionsRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Interaction: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + + + Examples + -------- + from google.genai._fern_interactions import ( + CreateModelInteractionParams, + GeminiNextGenAPIClient, + TextContent, + ) + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_interactions.create( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent( + text="text", + type="text", + ), + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def get_stream( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[InteractionSseEvent]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[InteractionSseEvent] + + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + response = client.fern_interactions.get_stream( + id="id", + ) + for chunk in response: + yield chunk + """ + with self._raw_client.get_stream( + id, stream=stream, last_event_id=last_event_id, include_input=include_input, request_options=request_options + ) as r: + yield from r.data + + def get( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Interaction: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_interactions.get( + id="id", + ) + """ + _response = self._raw_client.get( + id, stream=stream, last_event_id=last_event_id, include_input=include_input, request_options=request_options + ) + return _response.data + + def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Dict[str, typing.Any]: + """ + Deletes the interaction by id. + + Parameters + ---------- + id : str + The unique identifier of the interaction to delete. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Dict[str, typing.Any] + Successful deletion of the interaction. + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_interactions.delete( + id="id", + ) + """ + _response = self._raw_client.delete(id, request_options=request_options) + return _response.data + + def cancel(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Interaction: + """ + Cancels an interaction by id. This only applies to background interactions that are still running. + + Parameters + ---------- + id : str + The unique identifier of the interaction to cancel. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + Successful cancellation of the interaction. + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_interactions.cancel( + id="id", + ) + """ + _response = self._raw_client.cancel(id, request_options=request_options) + return _response.data + + +class AsyncFernInteractionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawFernInteractionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawFernInteractionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawFernInteractionsClient + """ + return self._raw_client + + async def create_stream( + self, *, request: CreateFernInteractionsStreamRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.AsyncIterator[InteractionSseEvent]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsStreamRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[InteractionSseEvent] + + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import ( + AsyncGeminiNextGenAPIClient, + CreateModelInteractionParams, + TextContent, + ) + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + response = await client.fern_interactions.create_stream( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent( + text="text", + type="text", + ), + ), + ) + async for chunk in response: + yield chunk + + + asyncio.run(main()) + """ + async with self._raw_client.create_stream(request=request, request_options=request_options) as r: + async for _chunk in r.data: + yield _chunk + + async def create( + self, *, request: CreateFernInteractionsRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Interaction: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import ( + AsyncGeminiNextGenAPIClient, + CreateModelInteractionParams, + TextContent, + ) + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_interactions.create( + request=CreateModelInteractionParams( + model="gemini-2.5-computer-use-preview-10-2025", + input=TextContent( + text="text", + type="text", + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def get_stream( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[InteractionSseEvent]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[InteractionSseEvent] + + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + response = await client.fern_interactions.get_stream( + id="id", + ) + async for chunk in response: + yield chunk + + + asyncio.run(main()) + """ + async with self._raw_client.get_stream( + id, stream=stream, last_event_id=last_event_id, include_input=include_input, request_options=request_options + ) as r: + async for _chunk in r.data: + yield _chunk + + async def get( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Interaction: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_interactions.get( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + id, stream=stream, last_event_id=last_event_id, include_input=include_input, request_options=request_options + ) + return _response.data + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Dict[str, typing.Any]: + """ + Deletes the interaction by id. + + Parameters + ---------- + id : str + The unique identifier of the interaction to delete. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Dict[str, typing.Any] + Successful deletion of the interaction. + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_interactions.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(id, request_options=request_options) + return _response.data + + async def cancel(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Interaction: + """ + Cancels an interaction by id. This only applies to background interactions that are still running. + + Parameters + ---------- + id : str + The unique identifier of the interaction to cancel. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Interaction + Successful cancellation of the interaction. + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_interactions.cancel( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.cancel(id, request_options=request_options) + return _response.data diff --git a/google/genai/_fern_interactions/fern_interactions/raw_client.py b/google/genai/_fern_interactions/fern_interactions/raw_client.py new file mode 100644 index 000000000..bd3f4538c --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/raw_client.py @@ -0,0 +1,760 @@ +# This file was auto-generated by Fern from our API Definition. + +import contextlib +import typing +from json.decoder import JSONDecodeError +from logging import error, warning + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.http_sse._api import EventSource +from ..core.jsonable_encoder import encode_path_param +from ..core.parse_error import ParsingError +from ..core.pydantic_utilities import parse_obj_as, parse_sse_obj +from ..core.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..types.interaction import Interaction +from ..types.interaction_sse_event import InteractionSseEvent +from .types.create_fern_interactions_request import CreateFernInteractionsRequest +from .types.create_fern_interactions_stream_request import CreateFernInteractionsStreamRequest +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawFernInteractionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + @contextlib.contextmanager + def create_stream( + self, *, request: CreateFernInteractionsStreamRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Iterator[HttpResponse[typing.Iterator[InteractionSseEvent]]]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsStreamRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[HttpResponse[typing.Iterator[InteractionSseEvent]]] + + """ + with self._client_wrapper.httpx_client.stream( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateFernInteractionsStreamRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) as _response: + + def _stream() -> HttpResponse[typing.Iterator[InteractionSseEvent]]: + try: + if 200 <= _response.status_code < 300: + + def _iter(): + _event_source = EventSource(_response) + for _sse in _event_source.iter_sse(): + if _sse.data == "[DONE]": + return + try: + yield typing.cast( + InteractionSseEvent, + parse_sse_obj( + sse=_sse, + type_=InteractionSseEvent, # type: ignore + ), + ) + except JSONDecodeError as e: + warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}") + except (TypeError, ValueError, KeyError, AttributeError) as e: + warning( + f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + except Exception as e: + error( + f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + return + + return HttpResponse(response=_response, data=_iter()) + _response.read() + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.json(), + cause=e, + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + yield _stream() + + def create( + self, *, request: CreateFernInteractionsRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Interaction]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Interaction] + + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateFernInteractionsRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + @contextlib.contextmanager + def get_stream( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[InteractionSseEvent]]]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[HttpResponse[typing.Iterator[InteractionSseEvent]]] + + """ + with self._client_wrapper.httpx_client.stream( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="GET", + params={ + "stream": stream, + "last_event_id": last_event_id, + "include_input": include_input, + }, + request_options=request_options, + ) as _response: + + def _stream() -> HttpResponse[typing.Iterator[InteractionSseEvent]]: + try: + if 200 <= _response.status_code < 300: + + def _iter(): + _event_source = EventSource(_response) + for _sse in _event_source.iter_sse(): + if _sse.data == "[DONE]": + return + try: + yield typing.cast( + InteractionSseEvent, + parse_sse_obj( + sse=_sse, + type_=InteractionSseEvent, # type: ignore + ), + ) + except JSONDecodeError as e: + warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}") + except (TypeError, ValueError, KeyError, AttributeError) as e: + warning( + f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + except Exception as e: + error( + f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + return + + return HttpResponse(response=_response, data=_iter()) + _response.read() + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.json(), + cause=e, + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + yield _stream() + + def get( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Interaction]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Interaction] + + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="GET", + params={ + "stream": stream, + "last_event_id": last_event_id, + "include_input": include_input, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Dict[str, typing.Any]]: + """ + Deletes the interaction by id. + + Parameters + ---------- + id : str + The unique identifier of the interaction to delete. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Dict[str, typing.Any]] + Successful deletion of the interaction. + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Dict[str, typing.Any], + parse_obj_as( + type_=typing.Dict[str, typing.Any], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def cancel(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Interaction]: + """ + Cancels an interaction by id. This only applies to background interactions that are still running. + + Parameters + ---------- + id : str + The unique identifier of the interaction to cancel. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Interaction] + Successful cancellation of the interaction. + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}/cancel", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawFernInteractionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + @contextlib.asynccontextmanager + async def create_stream( + self, *, request: CreateFernInteractionsStreamRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsStreamRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]] + + """ + async with self._client_wrapper.httpx_client.stream( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateFernInteractionsStreamRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) as _response: + + async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]: + try: + if 200 <= _response.status_code < 300: + + async def _iter(): + _event_source = EventSource(_response) + async for _sse in _event_source.aiter_sse(): + if _sse.data == "[DONE]": + return + try: + yield typing.cast( + InteractionSseEvent, + parse_sse_obj( + sse=_sse, + type_=InteractionSseEvent, # type: ignore + ), + ) + except JSONDecodeError as e: + warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}") + except (TypeError, ValueError, KeyError, AttributeError) as e: + warning( + f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + except Exception as e: + error( + f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + return + + return AsyncHttpResponse(response=_response, data=_iter()) + await _response.aread() + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.json(), + cause=e, + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + yield await _stream() + + async def create( + self, *, request: CreateFernInteractionsRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Interaction]: + """ + Creates a new interaction. + + Parameters + ---------- + request : CreateFernInteractionsRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Interaction] + + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateFernInteractionsRequest, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + @contextlib.asynccontextmanager + async def get_stream( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]] + + """ + async with self._client_wrapper.httpx_client.stream( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="GET", + params={ + "stream": stream, + "last_event_id": last_event_id, + "include_input": include_input, + }, + request_options=request_options, + ) as _response: + + async def _stream() -> AsyncHttpResponse[typing.AsyncIterator[InteractionSseEvent]]: + try: + if 200 <= _response.status_code < 300: + + async def _iter(): + _event_source = EventSource(_response) + async for _sse in _event_source.aiter_sse(): + if _sse.data == "[DONE]": + return + try: + yield typing.cast( + InteractionSseEvent, + parse_sse_obj( + sse=_sse, + type_=InteractionSseEvent, # type: ignore + ), + ) + except JSONDecodeError as e: + warning(f"Skipping SSE event with invalid JSON: {e}, sse: {_sse!r}") + except (TypeError, ValueError, KeyError, AttributeError) as e: + warning( + f"Skipping SSE event due to model construction error: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + except Exception as e: + error( + f"Unexpected error processing SSE event: {type(e).__name__}: {e}, sse: {_sse!r}" + ) + return + + return AsyncHttpResponse(response=_response, data=_iter()) + await _response.aread() + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.text + ) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, + headers=dict(_response.headers), + body=_response.json(), + cause=e, + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + yield await _stream() + + async def get( + self, + id: str, + *, + stream: typing.Optional[bool] = None, + last_event_id: typing.Optional[str] = None, + include_input: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Interaction]: + """ + Retrieves the full details of a single interaction based on its `Interaction.id`. + + Parameters + ---------- + id : str + The unique identifier of the interaction to retrieve. + + stream : typing.Optional[bool] + If set to true, the generated content will be streamed incrementally. + + last_event_id : typing.Optional[str] + Optional. If set, resumes the interaction stream from the next chunk after the event marked by the event id. Can only be used if `stream` is true. + + include_input : typing.Optional[bool] + If set to true, includes the input in the response. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Interaction] + + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="GET", + params={ + "stream": stream, + "last_event_id": last_event_id, + "include_input": include_input, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Dict[str, typing.Any]]: + """ + Deletes the interaction by id. + + Parameters + ---------- + id : str + The unique identifier of the interaction to delete. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Dict[str, typing.Any]] + Successful deletion of the interaction. + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Dict[str, typing.Any], + parse_obj_as( + type_=typing.Dict[str, typing.Any], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def cancel( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Interaction]: + """ + Cancels an interaction by id. This only applies to background interactions that are still running. + + Parameters + ---------- + id : str + The unique identifier of the interaction to cancel. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Interaction] + Successful cancellation of the interaction. + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/interactions/{encode_path_param(id)}/cancel", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Interaction, + parse_obj_as( + type_=Interaction, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/google/genai/_fern_interactions/fern_interactions/types/__init__.py b/google/genai/_fern_interactions/fern_interactions/types/__init__.py new file mode 100644 index 000000000..99ec08326 --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/types/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_fern_interactions_request import CreateFernInteractionsRequest + from .create_fern_interactions_stream_request import CreateFernInteractionsStreamRequest +_dynamic_imports: typing.Dict[str, str] = { + "CreateFernInteractionsRequest": ".create_fern_interactions_request", + "CreateFernInteractionsStreamRequest": ".create_fern_interactions_stream_request", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateFernInteractionsRequest", "CreateFernInteractionsStreamRequest"] diff --git a/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_request.py b/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_request.py new file mode 100644 index 000000000..07e8096b9 --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.create_agent_interaction_params import CreateAgentInteractionParams +from ...types.create_model_interaction_params import CreateModelInteractionParams + +CreateFernInteractionsRequest = typing.Union[CreateModelInteractionParams, CreateAgentInteractionParams] diff --git a/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_stream_request.py b/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_stream_request.py new file mode 100644 index 000000000..e35e44f76 --- /dev/null +++ b/google/genai/_fern_interactions/fern_interactions/types/create_fern_interactions_stream_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.create_agent_interaction_params import CreateAgentInteractionParams +from ...types.create_model_interaction_params import CreateModelInteractionParams + +CreateFernInteractionsStreamRequest = typing.Union[CreateModelInteractionParams, CreateAgentInteractionParams] diff --git a/google/genai/_fern_interactions/fern_webhooks/__init__.py b/google/genai/_fern_interactions/fern_webhooks/__init__.py new file mode 100644 index 000000000..9a0b6f081 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/__init__.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + RotateSigningSecretRequestRevocationBehavior, + WebhookUpdateState, + WebhookUpdateSubscribedEventsItem, + WebhookUpdateSubscribedEventsItemFive, + WebhookUpdateSubscribedEventsItemFour, + WebhookUpdateSubscribedEventsItemOne, + WebhookUpdateSubscribedEventsItemSeven, + WebhookUpdateSubscribedEventsItemSix, + WebhookUpdateSubscribedEventsItemThree, + WebhookUpdateSubscribedEventsItemTwo, + ) +_dynamic_imports: typing.Dict[str, str] = { + "RotateSigningSecretRequestRevocationBehavior": ".types", + "WebhookUpdateState": ".types", + "WebhookUpdateSubscribedEventsItem": ".types", + "WebhookUpdateSubscribedEventsItemFive": ".types", + "WebhookUpdateSubscribedEventsItemFour": ".types", + "WebhookUpdateSubscribedEventsItemOne": ".types", + "WebhookUpdateSubscribedEventsItemSeven": ".types", + "WebhookUpdateSubscribedEventsItemSix": ".types", + "WebhookUpdateSubscribedEventsItemThree": ".types", + "WebhookUpdateSubscribedEventsItemTwo": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "RotateSigningSecretRequestRevocationBehavior", + "WebhookUpdateState", + "WebhookUpdateSubscribedEventsItem", + "WebhookUpdateSubscribedEventsItemFive", + "WebhookUpdateSubscribedEventsItemFour", + "WebhookUpdateSubscribedEventsItemOne", + "WebhookUpdateSubscribedEventsItemSeven", + "WebhookUpdateSubscribedEventsItemSix", + "WebhookUpdateSubscribedEventsItemThree", + "WebhookUpdateSubscribedEventsItemTwo", +] diff --git a/google/genai/_fern_interactions/fern_webhooks/client.py b/google/genai/_fern_interactions/fern_webhooks/client.py new file mode 100644 index 000000000..a2138089e --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/client.py @@ -0,0 +1,746 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.empty import Empty +from ..types.list_webhooks_response import ListWebhooksResponse +from ..types.ping_webhook_request import PingWebhookRequest +from ..types.ping_webhook_response import PingWebhookResponse +from ..types.rotate_signing_secret_response import RotateSigningSecretResponse +from ..types.webhook_read import WebhookRead +from ..types.webhook_subscribed_events_item import WebhookSubscribedEventsItem +from .raw_client import AsyncRawFernWebhooksClient, RawFernWebhooksClient +from .types.rotate_signing_secret_request_revocation_behavior import RotateSigningSecretRequestRevocationBehavior +from .types.webhook_update_state import WebhookUpdateState +from .types.webhook_update_subscribed_events_item import WebhookUpdateSubscribedEventsItem + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class FernWebhooksClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawFernWebhooksClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawFernWebhooksClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawFernWebhooksClient + """ + return self._raw_client + + def list( + self, + *, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ListWebhooksResponse: + """ + Lists all Webhooks. + + Parameters + ---------- + page_size : typing.Optional[int] + Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + + page_token : typing.Optional[str] + Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListWebhooksResponse + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.list() + """ + _response = self._raw_client.list(page_size=page_size, page_token=page_token, request_options=request_options) + return _response.data + + def create( + self, + *, + subscribed_events: typing.Sequence[WebhookSubscribedEventsItem], + uri: str, + name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> WebhookRead: + """ + Creates a new Webhook. + + Parameters + ---------- + subscribed_events : typing.Sequence[WebhookSubscribedEventsItem] + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : str + Required. The URI to which webhook events will be sent. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.create( + subscribed_events=["batch.succeeded"], + uri="uri", + ) + """ + _response = self._raw_client.create( + subscribed_events=subscribed_events, uri=uri, name=name, request_options=request_options + ) + return _response.data + + def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> WebhookRead: + """ + Gets a specific Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.get( + id="id", + ) + """ + _response = self._raw_client.get(id, request_options=request_options) + return _response.data + + def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Empty: + """ + Deletes a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to delete. + Format: `{webhook_id}` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Empty + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.delete( + id="id", + ) + """ + _response = self._raw_client.delete(id, request_options=request_options) + return _response.data + + def update( + self, + id: str, + *, + update_mask: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + state: typing.Optional[WebhookUpdateState] = OMIT, + subscribed_events: typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] = OMIT, + uri: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> WebhookRead: + """ + Updates an existing Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to update. + + update_mask : typing.Optional[str] + Optional. The list of fields to update. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + state : typing.Optional[WebhookUpdateState] + Optional. The state of the webhook. + + subscribed_events : typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] + Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : typing.Optional[str] + Optional. The URI to which webhook events will be sent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.update( + id="id", + ) + """ + _response = self._raw_client.update( + id, + update_mask=update_mask, + name=name, + state=state, + subscribed_events=subscribed_events, + uri=uri, + request_options=request_options, + ) + return _response.data + + def rotate_signing_secret( + self, + id: str, + *, + revocation_behavior: typing.Optional[RotateSigningSecretRequestRevocationBehavior] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> RotateSigningSecretResponse: + """ + Generates a new signing secret for a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + + revocation_behavior : typing.Optional[RotateSigningSecretRequestRevocationBehavior] + Optional. The revocation behavior for previous signing secrets. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + RotateSigningSecretResponse + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.rotate_signing_secret( + id="id", + ) + """ + _response = self._raw_client.rotate_signing_secret( + id, revocation_behavior=revocation_behavior, request_options=request_options + ) + return _response.data + + def ping( + self, id: str, *, request: PingWebhookRequest, request_options: typing.Optional[RequestOptions] = None + ) -> PingWebhookResponse: + """ + Sends a ping event to a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to ping. + Format: `{webhook_id}` + + request : PingWebhookRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PingWebhookResponse + Successful operation + + Examples + -------- + from google.genai._fern_interactions import GeminiNextGenAPIClient + + client = GeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + client.fern_webhooks.ping( + id="id", + request={"key": "value"}, + ) + """ + _response = self._raw_client.ping(id, request=request, request_options=request_options) + return _response.data + + +class AsyncFernWebhooksClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawFernWebhooksClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawFernWebhooksClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawFernWebhooksClient + """ + return self._raw_client + + async def list( + self, + *, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ListWebhooksResponse: + """ + Lists all Webhooks. + + Parameters + ---------- + page_size : typing.Optional[int] + Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + + page_token : typing.Optional[str] + Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListWebhooksResponse + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + page_size=page_size, page_token=page_token, request_options=request_options + ) + return _response.data + + async def create( + self, + *, + subscribed_events: typing.Sequence[WebhookSubscribedEventsItem], + uri: str, + name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> WebhookRead: + """ + Creates a new Webhook. + + Parameters + ---------- + subscribed_events : typing.Sequence[WebhookSubscribedEventsItem] + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : str + Required. The URI to which webhook events will be sent. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.create( + subscribed_events=["batch.succeeded"], + uri="uri", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + subscribed_events=subscribed_events, uri=uri, name=name, request_options=request_options + ) + return _response.data + + async def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> WebhookRead: + """ + Gets a specific Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.get( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(id, request_options=request_options) + return _response.data + + async def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Empty: + """ + Deletes a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to delete. + Format: `{webhook_id}` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Empty + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.delete( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(id, request_options=request_options) + return _response.data + + async def update( + self, + id: str, + *, + update_mask: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + state: typing.Optional[WebhookUpdateState] = OMIT, + subscribed_events: typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] = OMIT, + uri: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> WebhookRead: + """ + Updates an existing Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to update. + + update_mask : typing.Optional[str] + Optional. The list of fields to update. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + state : typing.Optional[WebhookUpdateState] + Optional. The state of the webhook. + + subscribed_events : typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] + Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : typing.Optional[str] + Optional. The URI to which webhook events will be sent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WebhookRead + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.update( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + id, + update_mask=update_mask, + name=name, + state=state, + subscribed_events=subscribed_events, + uri=uri, + request_options=request_options, + ) + return _response.data + + async def rotate_signing_secret( + self, + id: str, + *, + revocation_behavior: typing.Optional[RotateSigningSecretRequestRevocationBehavior] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> RotateSigningSecretResponse: + """ + Generates a new signing secret for a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + + revocation_behavior : typing.Optional[RotateSigningSecretRequestRevocationBehavior] + Optional. The revocation behavior for previous signing secrets. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + RotateSigningSecretResponse + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.rotate_signing_secret( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.rotate_signing_secret( + id, revocation_behavior=revocation_behavior, request_options=request_options + ) + return _response.data + + async def ping( + self, id: str, *, request: PingWebhookRequest, request_options: typing.Optional[RequestOptions] = None + ) -> PingWebhookResponse: + """ + Sends a ping event to a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to ping. + Format: `{webhook_id}` + + request : PingWebhookRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PingWebhookResponse + Successful operation + + Examples + -------- + import asyncio + + from google.genai._fern_interactions import AsyncGeminiNextGenAPIClient + + client = AsyncGeminiNextGenAPIClient( + "v1beta", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.fern_webhooks.ping( + id="id", + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.ping(id, request=request, request_options=request_options) + return _response.data diff --git a/google/genai/_fern_interactions/fern_webhooks/raw_client.py b/google/genai/_fern_interactions/fern_webhooks/raw_client.py new file mode 100644 index 000000000..128406a00 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/raw_client.py @@ -0,0 +1,865 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.jsonable_encoder import encode_path_param +from ..core.parse_error import ParsingError +from ..core.pydantic_utilities import parse_obj_as +from ..core.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..types.empty import Empty +from ..types.list_webhooks_response import ListWebhooksResponse +from ..types.ping_webhook_request import PingWebhookRequest +from ..types.ping_webhook_response import PingWebhookResponse +from ..types.rotate_signing_secret_response import RotateSigningSecretResponse +from ..types.webhook_read import WebhookRead +from ..types.webhook_subscribed_events_item import WebhookSubscribedEventsItem +from .types.rotate_signing_secret_request_revocation_behavior import RotateSigningSecretRequestRevocationBehavior +from .types.webhook_update_state import WebhookUpdateState +from .types.webhook_update_subscribed_events_item import WebhookUpdateSubscribedEventsItem +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawFernWebhooksClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ListWebhooksResponse]: + """ + Lists all Webhooks. + + Parameters + ---------- + page_size : typing.Optional[int] + Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + + page_token : typing.Optional[str] + Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ListWebhooksResponse] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks", + method="GET", + params={ + "page_size": page_size, + "page_token": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListWebhooksResponse, + parse_obj_as( + type_=ListWebhooksResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def create( + self, + *, + subscribed_events: typing.Sequence[WebhookSubscribedEventsItem], + uri: str, + name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[WebhookRead]: + """ + Creates a new Webhook. + + Parameters + ---------- + subscribed_events : typing.Sequence[WebhookSubscribedEventsItem] + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : str + Required. The URI to which webhook events will be sent. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[WebhookRead] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks", + method="POST", + json={ + "name": name, + "subscribed_events": convert_and_respect_annotation_metadata( + object_=subscribed_events, + annotation=typing.Sequence[WebhookSubscribedEventsItem], + direction="write", + ), + "uri": uri, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[WebhookRead]: + """ + Gets a specific Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[WebhookRead] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Empty]: + """ + Deletes a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to delete. + Format: `{webhook_id}` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Empty] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Empty, + parse_obj_as( + type_=Empty, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def update( + self, + id: str, + *, + update_mask: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + state: typing.Optional[WebhookUpdateState] = OMIT, + subscribed_events: typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] = OMIT, + uri: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[WebhookRead]: + """ + Updates an existing Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to update. + + update_mask : typing.Optional[str] + Optional. The list of fields to update. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + state : typing.Optional[WebhookUpdateState] + Optional. The state of the webhook. + + subscribed_events : typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] + Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : typing.Optional[str] + Optional. The URI to which webhook events will be sent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[WebhookRead] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="PATCH", + params={ + "update_mask": update_mask, + }, + json={ + "name": name, + "state": state, + "subscribed_events": convert_and_respect_annotation_metadata( + object_=subscribed_events, + annotation=typing.Sequence[WebhookUpdateSubscribedEventsItem], + direction="write", + ), + "uri": uri, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def rotate_signing_secret( + self, + id: str, + *, + revocation_behavior: typing.Optional[RotateSigningSecretRequestRevocationBehavior] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[RotateSigningSecretResponse]: + """ + Generates a new signing secret for a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + + revocation_behavior : typing.Optional[RotateSigningSecretRequestRevocationBehavior] + Optional. The revocation behavior for previous signing secrets. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[RotateSigningSecretResponse] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}:rotateSigningSecret", + method="POST", + json={ + "revocation_behavior": revocation_behavior, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + RotateSigningSecretResponse, + parse_obj_as( + type_=RotateSigningSecretResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def ping( + self, id: str, *, request: PingWebhookRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[PingWebhookResponse]: + """ + Sends a ping event to a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to ping. + Format: `{webhook_id}` + + request : PingWebhookRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PingWebhookResponse] + Successful operation + """ + _response = self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}:ping", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PingWebhookResponse, + parse_obj_as( + type_=PingWebhookResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawFernWebhooksClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page_size: typing.Optional[int] = None, + page_token: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ListWebhooksResponse]: + """ + Lists all Webhooks. + + Parameters + ---------- + page_size : typing.Optional[int] + Optional. The maximum number of webhooks to return. The service may return fewer than + this value. If unspecified, at most 50 webhooks will be returned. + The maximum value is 1000. + + page_token : typing.Optional[str] + Optional. A page token, received from a previous `ListWebhooks` call. + Provide this to retrieve the subsequent page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ListWebhooksResponse] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks", + method="GET", + params={ + "page_size": page_size, + "page_token": page_token, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListWebhooksResponse, + parse_obj_as( + type_=ListWebhooksResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create( + self, + *, + subscribed_events: typing.Sequence[WebhookSubscribedEventsItem], + uri: str, + name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[WebhookRead]: + """ + Creates a new Webhook. + + Parameters + ---------- + subscribed_events : typing.Sequence[WebhookSubscribedEventsItem] + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : str + Required. The URI to which webhook events will be sent. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[WebhookRead] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks", + method="POST", + json={ + "name": name, + "subscribed_events": convert_and_respect_annotation_metadata( + object_=subscribed_events, + annotation=typing.Sequence[WebhookSubscribedEventsItem], + direction="write", + ), + "uri": uri, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[WebhookRead]: + """ + Gets a specific Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[WebhookRead] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Empty]: + """ + Deletes a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to delete. + Format: `{webhook_id}` + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Empty] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Empty, + parse_obj_as( + type_=Empty, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def update( + self, + id: str, + *, + update_mask: typing.Optional[str] = None, + name: typing.Optional[str] = OMIT, + state: typing.Optional[WebhookUpdateState] = OMIT, + subscribed_events: typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] = OMIT, + uri: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[WebhookRead]: + """ + Updates an existing Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to update. + + update_mask : typing.Optional[str] + Optional. The list of fields to update. + + name : typing.Optional[str] + Optional. The user-provided name of the webhook. + + state : typing.Optional[WebhookUpdateState] + Optional. The state of the webhook. + + subscribed_events : typing.Optional[typing.Sequence[WebhookUpdateSubscribedEventsItem]] + Optional. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + + uri : typing.Optional[str] + Optional. The URI to which webhook events will be sent. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[WebhookRead] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}", + method="PATCH", + params={ + "update_mask": update_mask, + }, + json={ + "name": name, + "state": state, + "subscribed_events": convert_and_respect_annotation_metadata( + object_=subscribed_events, + annotation=typing.Sequence[WebhookUpdateSubscribedEventsItem], + direction="write", + ), + "uri": uri, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WebhookRead, + parse_obj_as( + type_=WebhookRead, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def rotate_signing_secret( + self, + id: str, + *, + revocation_behavior: typing.Optional[RotateSigningSecretRequestRevocationBehavior] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[RotateSigningSecretResponse]: + """ + Generates a new signing secret for a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook for which to generate a signing secret. + Format: `{webhook_id}` + + revocation_behavior : typing.Optional[RotateSigningSecretRequestRevocationBehavior] + Optional. The revocation behavior for previous signing secrets. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[RotateSigningSecretResponse] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}:rotateSigningSecret", + method="POST", + json={ + "revocation_behavior": revocation_behavior, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + RotateSigningSecretResponse, + parse_obj_as( + type_=RotateSigningSecretResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def ping( + self, id: str, *, request: PingWebhookRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PingWebhookResponse]: + """ + Sends a ping event to a Webhook. + + Parameters + ---------- + id : str + Required. The ID of the webhook to ping. + Format: `{webhook_id}` + + request : PingWebhookRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PingWebhookResponse] + Successful operation + """ + _response = await self._client_wrapper.httpx_client.request( + f"{encode_path_param(self._client_wrapper._api_version)}/webhooks/{encode_path_param(id)}:ping", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PingWebhookResponse, + parse_obj_as( + type_=PingWebhookResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/google/genai/_fern_interactions/fern_webhooks/types/__init__.py b/google/genai/_fern_interactions/fern_webhooks/types/__init__.py new file mode 100644 index 000000000..860535a96 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/__init__.py @@ -0,0 +1,65 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .rotate_signing_secret_request_revocation_behavior import RotateSigningSecretRequestRevocationBehavior + from .webhook_update_state import WebhookUpdateState + from .webhook_update_subscribed_events_item import WebhookUpdateSubscribedEventsItem + from .webhook_update_subscribed_events_item_five import WebhookUpdateSubscribedEventsItemFive + from .webhook_update_subscribed_events_item_four import WebhookUpdateSubscribedEventsItemFour + from .webhook_update_subscribed_events_item_one import WebhookUpdateSubscribedEventsItemOne + from .webhook_update_subscribed_events_item_seven import WebhookUpdateSubscribedEventsItemSeven + from .webhook_update_subscribed_events_item_six import WebhookUpdateSubscribedEventsItemSix + from .webhook_update_subscribed_events_item_three import WebhookUpdateSubscribedEventsItemThree + from .webhook_update_subscribed_events_item_two import WebhookUpdateSubscribedEventsItemTwo +_dynamic_imports: typing.Dict[str, str] = { + "RotateSigningSecretRequestRevocationBehavior": ".rotate_signing_secret_request_revocation_behavior", + "WebhookUpdateState": ".webhook_update_state", + "WebhookUpdateSubscribedEventsItem": ".webhook_update_subscribed_events_item", + "WebhookUpdateSubscribedEventsItemFive": ".webhook_update_subscribed_events_item_five", + "WebhookUpdateSubscribedEventsItemFour": ".webhook_update_subscribed_events_item_four", + "WebhookUpdateSubscribedEventsItemOne": ".webhook_update_subscribed_events_item_one", + "WebhookUpdateSubscribedEventsItemSeven": ".webhook_update_subscribed_events_item_seven", + "WebhookUpdateSubscribedEventsItemSix": ".webhook_update_subscribed_events_item_six", + "WebhookUpdateSubscribedEventsItemThree": ".webhook_update_subscribed_events_item_three", + "WebhookUpdateSubscribedEventsItemTwo": ".webhook_update_subscribed_events_item_two", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "RotateSigningSecretRequestRevocationBehavior", + "WebhookUpdateState", + "WebhookUpdateSubscribedEventsItem", + "WebhookUpdateSubscribedEventsItemFive", + "WebhookUpdateSubscribedEventsItemFour", + "WebhookUpdateSubscribedEventsItemOne", + "WebhookUpdateSubscribedEventsItemSeven", + "WebhookUpdateSubscribedEventsItemSix", + "WebhookUpdateSubscribedEventsItemThree", + "WebhookUpdateSubscribedEventsItemTwo", +] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/rotate_signing_secret_request_revocation_behavior.py b/google/genai/_fern_interactions/fern_webhooks/types/rotate_signing_secret_request_revocation_behavior.py new file mode 100644 index 000000000..9e54fe002 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/rotate_signing_secret_request_revocation_behavior.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RotateSigningSecretRequestRevocationBehavior = typing.Union[ + typing.Literal["revoke_previous_secrets_after_h24", "revoke_previous_secrets_immediately"], typing.Any +] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_state.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_state.py new file mode 100644 index 000000000..838c614d6 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateState = typing.Union[ + typing.Literal["enabled", "disabled", "disabled_due_to_failed_deliveries"], typing.Any +] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item.py new file mode 100644 index 000000000..89cbf38ba --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .webhook_update_subscribed_events_item_five import WebhookUpdateSubscribedEventsItemFive +from .webhook_update_subscribed_events_item_four import WebhookUpdateSubscribedEventsItemFour +from .webhook_update_subscribed_events_item_one import WebhookUpdateSubscribedEventsItemOne +from .webhook_update_subscribed_events_item_seven import WebhookUpdateSubscribedEventsItemSeven +from .webhook_update_subscribed_events_item_six import WebhookUpdateSubscribedEventsItemSix +from .webhook_update_subscribed_events_item_three import WebhookUpdateSubscribedEventsItemThree +from .webhook_update_subscribed_events_item_two import WebhookUpdateSubscribedEventsItemTwo + +WebhookUpdateSubscribedEventsItem = typing.Union[ + str, + WebhookUpdateSubscribedEventsItemOne, + WebhookUpdateSubscribedEventsItemTwo, + WebhookUpdateSubscribedEventsItemThree, + WebhookUpdateSubscribedEventsItemFour, + WebhookUpdateSubscribedEventsItemFive, + WebhookUpdateSubscribedEventsItemSix, + WebhookUpdateSubscribedEventsItemSeven, +] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_five.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_five.py new file mode 100644 index 000000000..7b22fc101 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_five.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemFive = typing.Union[typing.Literal["interaction.completed"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_four.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_four.py new file mode 100644 index 000000000..9ae5250e2 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_four.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemFour = typing.Union[typing.Literal["interaction.requires_action"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_one.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_one.py new file mode 100644 index 000000000..5ea3eb945 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemOne = typing.Union[typing.Literal["batch.succeeded"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_seven.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_seven.py new file mode 100644 index 000000000..0b902bd8f --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_seven.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemSeven = typing.Union[typing.Literal["video.generated"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_six.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_six.py new file mode 100644 index 000000000..d7941dd5e --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_six.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemSix = typing.Union[typing.Literal["interaction.failed"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_three.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_three.py new file mode 100644 index 000000000..72ef81917 --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_three.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemThree = typing.Union[typing.Literal["batch.failed"], typing.Any] diff --git a/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_two.py b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_two.py new file mode 100644 index 000000000..6d152aa4b --- /dev/null +++ b/google/genai/_fern_interactions/fern_webhooks/types/webhook_update_subscribed_events_item_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookUpdateSubscribedEventsItemTwo = typing.Union[typing.Literal["batch.expired"], typing.Any] diff --git a/google/genai/_fern_interactions/types/__init__.py b/google/genai/_fern_interactions/types/__init__.py new file mode 100644 index 000000000..3e369371b --- /dev/null +++ b/google/genai/_fern_interactions/types/__init__.py @@ -0,0 +1,938 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .agent import Agent + from .agent_base_environment import AgentBaseEnvironment + from .agent_egress_allowlist import AgentEgressAllowlist + from .agent_egress_rule import AgentEgressRule + from .agent_option import AgentOption + from .agent_option_one import AgentOptionOne + from .agent_option_three import AgentOptionThree + from .agent_option_two import AgentOptionTwo + from .agent_tool import AgentTool + from .allowed_tools import AllowedTools + from .allowlist_entry import AllowlistEntry + from .annotation import Annotation + from .arguments_delta import ArgumentsDelta + from .arguments_delta_type import ArgumentsDeltaType + from .audio_content import AudioContent + from .audio_content_mime_type import AudioContentMimeType + from .audio_content_type import AudioContentType + from .audio_delta import AudioDelta + from .audio_delta_mime_type import AudioDeltaMimeType + from .audio_delta_type import AudioDeltaType + from .audio_response_format import AudioResponseFormat + from .audio_response_format_delivery import AudioResponseFormatDelivery + from .audio_response_format_mime_type import AudioResponseFormatMimeType + from .audio_response_format_type import AudioResponseFormatType + from .code_execution import CodeExecution + from .code_execution_call_arguments import CodeExecutionCallArguments + from .code_execution_call_arguments_language import CodeExecutionCallArgumentsLanguage + from .code_execution_call_delta import CodeExecutionCallDelta + from .code_execution_call_delta_type import CodeExecutionCallDeltaType + from .code_execution_call_step import CodeExecutionCallStep + from .code_execution_call_step_arguments import CodeExecutionCallStepArguments + from .code_execution_call_step_arguments_language import CodeExecutionCallStepArgumentsLanguage + from .code_execution_call_step_type import CodeExecutionCallStepType + from .code_execution_result_delta import CodeExecutionResultDelta + from .code_execution_result_delta_type import CodeExecutionResultDeltaType + from .code_execution_result_step import CodeExecutionResultStep + from .code_execution_result_step_type import CodeExecutionResultStepType + from .code_execution_type import CodeExecutionType + from .computer_use import ComputerUse + from .computer_use_environment import ComputerUseEnvironment + from .computer_use_type import ComputerUseType + from .content import Content + from .content_delta import ContentDelta + from .content_delta_data import ContentDeltaData + from .content_delta_event_type import ContentDeltaEventType + from .content_start import ContentStart + from .content_start_event_type import ContentStartEventType + from .content_stop import ContentStop + from .content_stop_event_type import ContentStopEventType + from .create_agent_interaction_params import CreateAgentInteractionParams + from .create_agent_interaction_params_agent_config import CreateAgentInteractionParamsAgentConfig + from .create_agent_interaction_params_environment import CreateAgentInteractionParamsEnvironment + from .create_agent_interaction_params_response_format import CreateAgentInteractionParamsResponseFormat + from .create_model_interaction_params import CreateModelInteractionParams + from .create_model_interaction_params_environment import CreateModelInteractionParamsEnvironment + from .create_model_interaction_params_response_format import CreateModelInteractionParamsResponseFormat + from .deep_research_agent_config import DeepResearchAgentConfig + from .deep_research_agent_config_type import DeepResearchAgentConfigType + from .deep_research_agent_config_visualization import DeepResearchAgentConfigVisualization + from .document_content import DocumentContent + from .document_content_mime_type import DocumentContentMimeType + from .document_content_type import DocumentContentType + from .document_delta import DocumentDelta + from .document_delta_mime_type import DocumentDeltaMimeType + from .document_delta_type import DocumentDeltaType + from .dynamic_agent_config import DynamicAgentConfig + from .dynamic_agent_config_type import DynamicAgentConfigType + from .empty import Empty + from .environment_config import EnvironmentConfig + from .environment_config_network import EnvironmentConfigNetwork + from .environment_config_network_one import EnvironmentConfigNetworkOne + from .environment_config_type import EnvironmentConfigType + from .environment_network_egress_allowlist import EnvironmentNetworkEgressAllowlist + from .environment_network_egress_allowlist_allowlist import EnvironmentNetworkEgressAllowlistAllowlist + from .environment_network_egress_allowlist_one import EnvironmentNetworkEgressAllowlistOne + from .environment_source import EnvironmentSource + from .environment_source_type import EnvironmentSourceType + from .error import Error + from .error_event import ErrorEvent + from .error_event_event_type import ErrorEventEventType + from .file_citation import FileCitation + from .file_citation_type import FileCitationType + from .file_search import FileSearch + from .file_search_call_delta import FileSearchCallDelta + from .file_search_call_delta_type import FileSearchCallDeltaType + from .file_search_call_step import FileSearchCallStep + from .file_search_call_step_type import FileSearchCallStepType + from .file_search_result import FileSearchResult + from .file_search_result_delta import FileSearchResultDelta + from .file_search_result_delta_type import FileSearchResultDeltaType + from .file_search_result_step import FileSearchResultStep + from .file_search_result_step_type import FileSearchResultStepType + from .file_search_type import FileSearchType + from .function import Function + from .function_call_delta import FunctionCallDelta + from .function_call_delta_type import FunctionCallDeltaType + from .function_call_step import FunctionCallStep + from .function_call_step_type import FunctionCallStepType + from .function_result_delta import FunctionResultDelta + from .function_result_delta_result import FunctionResultDeltaResult + from .function_result_delta_type import FunctionResultDeltaType + from .function_result_step import FunctionResultStep + from .function_result_step_result import FunctionResultStepResult + from .function_result_step_type import FunctionResultStepType + from .function_result_subcontent import FunctionResultSubcontent + from .function_type import FunctionType + from .generation_config import GenerationConfig + from .generation_config_tool_choice import GenerationConfigToolChoice + from .google_maps import GoogleMaps + from .google_maps_call_arguments import GoogleMapsCallArguments + from .google_maps_call_delta import GoogleMapsCallDelta + from .google_maps_call_delta_type import GoogleMapsCallDeltaType + from .google_maps_call_step import GoogleMapsCallStep + from .google_maps_call_step_arguments import GoogleMapsCallStepArguments + from .google_maps_call_step_type import GoogleMapsCallStepType + from .google_maps_result import GoogleMapsResult + from .google_maps_result_delta import GoogleMapsResultDelta + from .google_maps_result_delta_type import GoogleMapsResultDeltaType + from .google_maps_result_item import GoogleMapsResultItem + from .google_maps_result_places import GoogleMapsResultPlaces + from .google_maps_result_step import GoogleMapsResultStep + from .google_maps_result_step_type import GoogleMapsResultStepType + from .google_maps_type import GoogleMapsType + from .google_search import GoogleSearch + from .google_search_call_arguments import GoogleSearchCallArguments + from .google_search_call_delta import GoogleSearchCallDelta + from .google_search_call_delta_type import GoogleSearchCallDeltaType + from .google_search_call_step import GoogleSearchCallStep + from .google_search_call_step_arguments import GoogleSearchCallStepArguments + from .google_search_call_step_search_type import GoogleSearchCallStepSearchType + from .google_search_call_step_type import GoogleSearchCallStepType + from .google_search_result import GoogleSearchResult + from .google_search_result_delta import GoogleSearchResultDelta + from .google_search_result_delta_type import GoogleSearchResultDeltaType + from .google_search_result_item import GoogleSearchResultItem + from .google_search_result_step import GoogleSearchResultStep + from .google_search_result_step_type import GoogleSearchResultStepType + from .google_search_search_types_item import GoogleSearchSearchTypesItem + from .google_search_type import GoogleSearchType + from .grounding_tool_count import GroundingToolCount + from .grounding_tool_count_type import GroundingToolCountType + from .image_config import ImageConfig + from .image_config_aspect_ratio import ImageConfigAspectRatio + from .image_config_image_size import ImageConfigImageSize + from .image_content import ImageContent + from .image_content_mime_type import ImageContentMimeType + from .image_content_type import ImageContentType + from .image_delta import ImageDelta + from .image_delta_mime_type import ImageDeltaMimeType + from .image_delta_type import ImageDeltaType + from .image_response_format import ImageResponseFormat + from .image_response_format_aspect_ratio import ImageResponseFormatAspectRatio + from .image_response_format_delivery import ImageResponseFormatDelivery + from .image_response_format_image_size import ImageResponseFormatImageSize + from .image_response_format_mime_type import ImageResponseFormatMimeType + from .image_response_format_type import ImageResponseFormatType + from .interaction import Interaction + from .interaction_agent_config import InteractionAgentConfig + from .interaction_completed_event import InteractionCompletedEvent + from .interaction_completed_event_event_type import InteractionCompletedEventEventType + from .interaction_created_event import InteractionCreatedEvent + from .interaction_created_event_event_type import InteractionCreatedEventEventType + from .interaction_environment import InteractionEnvironment + from .interaction_response_format import InteractionResponseFormat + from .interaction_sse_event import InteractionSseEvent + from .interaction_status import InteractionStatus + from .interaction_status_update import InteractionStatusUpdate + from .interaction_status_update_event_type import InteractionStatusUpdateEventType + from .interaction_status_update_status import InteractionStatusUpdateStatus + from .interactions_input import InteractionsInput + from .list_agents_response import ListAgentsResponse + from .list_webhooks_response import ListWebhooksResponse + from .mcp_server import McpServer + from .mcp_server_tool_call_delta import McpServerToolCallDelta + from .mcp_server_tool_call_delta_type import McpServerToolCallDeltaType + from .mcp_server_tool_call_step import McpServerToolCallStep + from .mcp_server_tool_call_step_type import McpServerToolCallStepType + from .mcp_server_tool_result_delta import McpServerToolResultDelta + from .mcp_server_tool_result_delta_result import McpServerToolResultDeltaResult + from .mcp_server_tool_result_delta_type import McpServerToolResultDeltaType + from .mcp_server_tool_result_step import McpServerToolResultStep + from .mcp_server_tool_result_step_result import McpServerToolResultStepResult + from .mcp_server_tool_result_step_type import McpServerToolResultStepType + from .mcp_server_type import McpServerType + from .media_resolution import MediaResolution + from .modality_tokens import ModalityTokens + from .model_option import ModelOption + from .model_option_eight import ModelOptionEight + from .model_option_eighteen import ModelOptionEighteen + from .model_option_eleven import ModelOptionEleven + from .model_option_fifteen import ModelOptionFifteen + from .model_option_five import ModelOptionFive + from .model_option_four import ModelOptionFour + from .model_option_fourteen import ModelOptionFourteen + from .model_option_nine import ModelOptionNine + from .model_option_nineteen import ModelOptionNineteen + from .model_option_one import ModelOptionOne + from .model_option_seven import ModelOptionSeven + from .model_option_seventeen import ModelOptionSeventeen + from .model_option_six import ModelOptionSix + from .model_option_sixteen import ModelOptionSixteen + from .model_option_ten import ModelOptionTen + from .model_option_thirteen import ModelOptionThirteen + from .model_option_three import ModelOptionThree + from .model_option_twelve import ModelOptionTwelve + from .model_option_twenty import ModelOptionTwenty + from .model_option_two import ModelOptionTwo + from .model_output_step import ModelOutputStep + from .model_output_step_type import ModelOutputStepType + from .ping_webhook_request import PingWebhookRequest + from .ping_webhook_response import PingWebhookResponse + from .place_citation import PlaceCitation + from .place_citation_type import PlaceCitationType + from .places import Places + from .response_format import ResponseFormat + from .response_format_list import ResponseFormatList + from .response_modality import ResponseModality + from .retrieval import Retrieval + from .retrieval_retrieval_types_item import RetrievalRetrievalTypesItem + from .retrieval_type import RetrievalType + from .review_snippet import ReviewSnippet + from .rotate_signing_secret_response import RotateSigningSecretResponse + from .service_tier import ServiceTier + from .signing_secret import SigningSecret + from .signing_secret_read import SigningSecretRead + from .source import Source + from .source_type import SourceType + from .speech_config import SpeechConfig + from .step import Step + from .step_delta import StepDelta + from .step_delta_data import StepDeltaData + from .step_delta_event_type import StepDeltaEventType + from .step_start import StepStart + from .step_start_event_type import StepStartEventType + from .step_stop import StepStop + from .step_stop_event_type import StepStopEventType + from .text_annotation_delta import TextAnnotationDelta + from .text_annotation_delta_type import TextAnnotationDeltaType + from .text_content import TextContent + from .text_content_type import TextContentType + from .text_delta import TextDelta + from .text_delta_type import TextDeltaType + from .text_response_format import TextResponseFormat + from .text_response_format_mime_type import TextResponseFormatMimeType + from .text_response_format_type import TextResponseFormatType + from .thinking_level import ThinkingLevel + from .thinking_summaries import ThinkingSummaries + from .thought_content import ThoughtContent + from .thought_content_type import ThoughtContentType + from .thought_signature_delta import ThoughtSignatureDelta + from .thought_signature_delta_type import ThoughtSignatureDeltaType + from .thought_step import ThoughtStep + from .thought_step_type import ThoughtStepType + from .thought_summary_content import ThoughtSummaryContent + from .thought_summary_delta import ThoughtSummaryDelta + from .thought_summary_delta_type import ThoughtSummaryDeltaType + from .tool import Tool + from .tool_choice_config import ToolChoiceConfig + from .tool_choice_type import ToolChoiceType + from .turn import Turn + from .turn_content import TurnContent + from .url_citation import UrlCitation + from .url_citation_type import UrlCitationType + from .url_context import UrlContext + from .url_context_call_arguments import UrlContextCallArguments + from .url_context_call_delta import UrlContextCallDelta + from .url_context_call_delta_type import UrlContextCallDeltaType + from .url_context_call_step import UrlContextCallStep + from .url_context_call_step_arguments import UrlContextCallStepArguments + from .url_context_call_step_type import UrlContextCallStepType + from .url_context_result import UrlContextResult + from .url_context_result_delta import UrlContextResultDelta + from .url_context_result_delta_type import UrlContextResultDeltaType + from .url_context_result_item import UrlContextResultItem + from .url_context_result_item_status import UrlContextResultItemStatus + from .url_context_result_status import UrlContextResultStatus + from .url_context_result_step import UrlContextResultStep + from .url_context_result_step_type import UrlContextResultStepType + from .url_context_type import UrlContextType + from .usage import Usage + from .user_input_step import UserInputStep + from .user_input_step_type import UserInputStepType + from .vertex_ai_search_config import VertexAiSearchConfig + from .video_content import VideoContent + from .video_content_mime_type import VideoContentMimeType + from .video_content_type import VideoContentType + from .video_delta import VideoDelta + from .video_delta_mime_type import VideoDeltaMimeType + from .video_delta_type import VideoDeltaType + from .webhook import Webhook + from .webhook_config import WebhookConfig + from .webhook_read import WebhookRead + from .webhook_state import WebhookState + from .webhook_subscribed_events_item import WebhookSubscribedEventsItem + from .webhook_subscribed_events_item_five import WebhookSubscribedEventsItemFive + from .webhook_subscribed_events_item_four import WebhookSubscribedEventsItemFour + from .webhook_subscribed_events_item_one import WebhookSubscribedEventsItemOne + from .webhook_subscribed_events_item_seven import WebhookSubscribedEventsItemSeven + from .webhook_subscribed_events_item_six import WebhookSubscribedEventsItemSix + from .webhook_subscribed_events_item_three import WebhookSubscribedEventsItemThree + from .webhook_subscribed_events_item_two import WebhookSubscribedEventsItemTwo +_dynamic_imports: typing.Dict[str, str] = { + "Agent": ".agent", + "AgentBaseEnvironment": ".agent_base_environment", + "AgentEgressAllowlist": ".agent_egress_allowlist", + "AgentEgressRule": ".agent_egress_rule", + "AgentOption": ".agent_option", + "AgentOptionOne": ".agent_option_one", + "AgentOptionThree": ".agent_option_three", + "AgentOptionTwo": ".agent_option_two", + "AgentTool": ".agent_tool", + "AllowedTools": ".allowed_tools", + "AllowlistEntry": ".allowlist_entry", + "Annotation": ".annotation", + "ArgumentsDelta": ".arguments_delta", + "ArgumentsDeltaType": ".arguments_delta_type", + "AudioContent": ".audio_content", + "AudioContentMimeType": ".audio_content_mime_type", + "AudioContentType": ".audio_content_type", + "AudioDelta": ".audio_delta", + "AudioDeltaMimeType": ".audio_delta_mime_type", + "AudioDeltaType": ".audio_delta_type", + "AudioResponseFormat": ".audio_response_format", + "AudioResponseFormatDelivery": ".audio_response_format_delivery", + "AudioResponseFormatMimeType": ".audio_response_format_mime_type", + "AudioResponseFormatType": ".audio_response_format_type", + "CodeExecution": ".code_execution", + "CodeExecutionCallArguments": ".code_execution_call_arguments", + "CodeExecutionCallArgumentsLanguage": ".code_execution_call_arguments_language", + "CodeExecutionCallDelta": ".code_execution_call_delta", + "CodeExecutionCallDeltaType": ".code_execution_call_delta_type", + "CodeExecutionCallStep": ".code_execution_call_step", + "CodeExecutionCallStepArguments": ".code_execution_call_step_arguments", + "CodeExecutionCallStepArgumentsLanguage": ".code_execution_call_step_arguments_language", + "CodeExecutionCallStepType": ".code_execution_call_step_type", + "CodeExecutionResultDelta": ".code_execution_result_delta", + "CodeExecutionResultDeltaType": ".code_execution_result_delta_type", + "CodeExecutionResultStep": ".code_execution_result_step", + "CodeExecutionResultStepType": ".code_execution_result_step_type", + "CodeExecutionType": ".code_execution_type", + "ComputerUse": ".computer_use", + "ComputerUseEnvironment": ".computer_use_environment", + "ComputerUseType": ".computer_use_type", + "Content": ".content", + "ContentDelta": ".content_delta", + "ContentDeltaData": ".content_delta_data", + "ContentDeltaEventType": ".content_delta_event_type", + "ContentStart": ".content_start", + "ContentStartEventType": ".content_start_event_type", + "ContentStop": ".content_stop", + "ContentStopEventType": ".content_stop_event_type", + "CreateAgentInteractionParams": ".create_agent_interaction_params", + "CreateAgentInteractionParamsAgentConfig": ".create_agent_interaction_params_agent_config", + "CreateAgentInteractionParamsEnvironment": ".create_agent_interaction_params_environment", + "CreateAgentInteractionParamsResponseFormat": ".create_agent_interaction_params_response_format", + "CreateModelInteractionParams": ".create_model_interaction_params", + "CreateModelInteractionParamsEnvironment": ".create_model_interaction_params_environment", + "CreateModelInteractionParamsResponseFormat": ".create_model_interaction_params_response_format", + "DeepResearchAgentConfig": ".deep_research_agent_config", + "DeepResearchAgentConfigType": ".deep_research_agent_config_type", + "DeepResearchAgentConfigVisualization": ".deep_research_agent_config_visualization", + "DocumentContent": ".document_content", + "DocumentContentMimeType": ".document_content_mime_type", + "DocumentContentType": ".document_content_type", + "DocumentDelta": ".document_delta", + "DocumentDeltaMimeType": ".document_delta_mime_type", + "DocumentDeltaType": ".document_delta_type", + "DynamicAgentConfig": ".dynamic_agent_config", + "DynamicAgentConfigType": ".dynamic_agent_config_type", + "Empty": ".empty", + "EnvironmentConfig": ".environment_config", + "EnvironmentConfigNetwork": ".environment_config_network", + "EnvironmentConfigNetworkOne": ".environment_config_network_one", + "EnvironmentConfigType": ".environment_config_type", + "EnvironmentNetworkEgressAllowlist": ".environment_network_egress_allowlist", + "EnvironmentNetworkEgressAllowlistAllowlist": ".environment_network_egress_allowlist_allowlist", + "EnvironmentNetworkEgressAllowlistOne": ".environment_network_egress_allowlist_one", + "EnvironmentSource": ".environment_source", + "EnvironmentSourceType": ".environment_source_type", + "Error": ".error", + "ErrorEvent": ".error_event", + "ErrorEventEventType": ".error_event_event_type", + "FileCitation": ".file_citation", + "FileCitationType": ".file_citation_type", + "FileSearch": ".file_search", + "FileSearchCallDelta": ".file_search_call_delta", + "FileSearchCallDeltaType": ".file_search_call_delta_type", + "FileSearchCallStep": ".file_search_call_step", + "FileSearchCallStepType": ".file_search_call_step_type", + "FileSearchResult": ".file_search_result", + "FileSearchResultDelta": ".file_search_result_delta", + "FileSearchResultDeltaType": ".file_search_result_delta_type", + "FileSearchResultStep": ".file_search_result_step", + "FileSearchResultStepType": ".file_search_result_step_type", + "FileSearchType": ".file_search_type", + "Function": ".function", + "FunctionCallDelta": ".function_call_delta", + "FunctionCallDeltaType": ".function_call_delta_type", + "FunctionCallStep": ".function_call_step", + "FunctionCallStepType": ".function_call_step_type", + "FunctionResultDelta": ".function_result_delta", + "FunctionResultDeltaResult": ".function_result_delta_result", + "FunctionResultDeltaType": ".function_result_delta_type", + "FunctionResultStep": ".function_result_step", + "FunctionResultStepResult": ".function_result_step_result", + "FunctionResultStepType": ".function_result_step_type", + "FunctionResultSubcontent": ".function_result_subcontent", + "FunctionType": ".function_type", + "GenerationConfig": ".generation_config", + "GenerationConfigToolChoice": ".generation_config_tool_choice", + "GoogleMaps": ".google_maps", + "GoogleMapsCallArguments": ".google_maps_call_arguments", + "GoogleMapsCallDelta": ".google_maps_call_delta", + "GoogleMapsCallDeltaType": ".google_maps_call_delta_type", + "GoogleMapsCallStep": ".google_maps_call_step", + "GoogleMapsCallStepArguments": ".google_maps_call_step_arguments", + "GoogleMapsCallStepType": ".google_maps_call_step_type", + "GoogleMapsResult": ".google_maps_result", + "GoogleMapsResultDelta": ".google_maps_result_delta", + "GoogleMapsResultDeltaType": ".google_maps_result_delta_type", + "GoogleMapsResultItem": ".google_maps_result_item", + "GoogleMapsResultPlaces": ".google_maps_result_places", + "GoogleMapsResultStep": ".google_maps_result_step", + "GoogleMapsResultStepType": ".google_maps_result_step_type", + "GoogleMapsType": ".google_maps_type", + "GoogleSearch": ".google_search", + "GoogleSearchCallArguments": ".google_search_call_arguments", + "GoogleSearchCallDelta": ".google_search_call_delta", + "GoogleSearchCallDeltaType": ".google_search_call_delta_type", + "GoogleSearchCallStep": ".google_search_call_step", + "GoogleSearchCallStepArguments": ".google_search_call_step_arguments", + "GoogleSearchCallStepSearchType": ".google_search_call_step_search_type", + "GoogleSearchCallStepType": ".google_search_call_step_type", + "GoogleSearchResult": ".google_search_result", + "GoogleSearchResultDelta": ".google_search_result_delta", + "GoogleSearchResultDeltaType": ".google_search_result_delta_type", + "GoogleSearchResultItem": ".google_search_result_item", + "GoogleSearchResultStep": ".google_search_result_step", + "GoogleSearchResultStepType": ".google_search_result_step_type", + "GoogleSearchSearchTypesItem": ".google_search_search_types_item", + "GoogleSearchType": ".google_search_type", + "GroundingToolCount": ".grounding_tool_count", + "GroundingToolCountType": ".grounding_tool_count_type", + "ImageConfig": ".image_config", + "ImageConfigAspectRatio": ".image_config_aspect_ratio", + "ImageConfigImageSize": ".image_config_image_size", + "ImageContent": ".image_content", + "ImageContentMimeType": ".image_content_mime_type", + "ImageContentType": ".image_content_type", + "ImageDelta": ".image_delta", + "ImageDeltaMimeType": ".image_delta_mime_type", + "ImageDeltaType": ".image_delta_type", + "ImageResponseFormat": ".image_response_format", + "ImageResponseFormatAspectRatio": ".image_response_format_aspect_ratio", + "ImageResponseFormatDelivery": ".image_response_format_delivery", + "ImageResponseFormatImageSize": ".image_response_format_image_size", + "ImageResponseFormatMimeType": ".image_response_format_mime_type", + "ImageResponseFormatType": ".image_response_format_type", + "Interaction": ".interaction", + "InteractionAgentConfig": ".interaction_agent_config", + "InteractionCompletedEvent": ".interaction_completed_event", + "InteractionCompletedEventEventType": ".interaction_completed_event_event_type", + "InteractionCreatedEvent": ".interaction_created_event", + "InteractionCreatedEventEventType": ".interaction_created_event_event_type", + "InteractionEnvironment": ".interaction_environment", + "InteractionResponseFormat": ".interaction_response_format", + "InteractionSseEvent": ".interaction_sse_event", + "InteractionStatus": ".interaction_status", + "InteractionStatusUpdate": ".interaction_status_update", + "InteractionStatusUpdateEventType": ".interaction_status_update_event_type", + "InteractionStatusUpdateStatus": ".interaction_status_update_status", + "InteractionsInput": ".interactions_input", + "ListAgentsResponse": ".list_agents_response", + "ListWebhooksResponse": ".list_webhooks_response", + "McpServer": ".mcp_server", + "McpServerToolCallDelta": ".mcp_server_tool_call_delta", + "McpServerToolCallDeltaType": ".mcp_server_tool_call_delta_type", + "McpServerToolCallStep": ".mcp_server_tool_call_step", + "McpServerToolCallStepType": ".mcp_server_tool_call_step_type", + "McpServerToolResultDelta": ".mcp_server_tool_result_delta", + "McpServerToolResultDeltaResult": ".mcp_server_tool_result_delta_result", + "McpServerToolResultDeltaType": ".mcp_server_tool_result_delta_type", + "McpServerToolResultStep": ".mcp_server_tool_result_step", + "McpServerToolResultStepResult": ".mcp_server_tool_result_step_result", + "McpServerToolResultStepType": ".mcp_server_tool_result_step_type", + "McpServerType": ".mcp_server_type", + "MediaResolution": ".media_resolution", + "ModalityTokens": ".modality_tokens", + "ModelOption": ".model_option", + "ModelOptionEight": ".model_option_eight", + "ModelOptionEighteen": ".model_option_eighteen", + "ModelOptionEleven": ".model_option_eleven", + "ModelOptionFifteen": ".model_option_fifteen", + "ModelOptionFive": ".model_option_five", + "ModelOptionFour": ".model_option_four", + "ModelOptionFourteen": ".model_option_fourteen", + "ModelOptionNine": ".model_option_nine", + "ModelOptionNineteen": ".model_option_nineteen", + "ModelOptionOne": ".model_option_one", + "ModelOptionSeven": ".model_option_seven", + "ModelOptionSeventeen": ".model_option_seventeen", + "ModelOptionSix": ".model_option_six", + "ModelOptionSixteen": ".model_option_sixteen", + "ModelOptionTen": ".model_option_ten", + "ModelOptionThirteen": ".model_option_thirteen", + "ModelOptionThree": ".model_option_three", + "ModelOptionTwelve": ".model_option_twelve", + "ModelOptionTwenty": ".model_option_twenty", + "ModelOptionTwo": ".model_option_two", + "ModelOutputStep": ".model_output_step", + "ModelOutputStepType": ".model_output_step_type", + "PingWebhookRequest": ".ping_webhook_request", + "PingWebhookResponse": ".ping_webhook_response", + "PlaceCitation": ".place_citation", + "PlaceCitationType": ".place_citation_type", + "Places": ".places", + "ResponseFormat": ".response_format", + "ResponseFormatList": ".response_format_list", + "ResponseModality": ".response_modality", + "Retrieval": ".retrieval", + "RetrievalRetrievalTypesItem": ".retrieval_retrieval_types_item", + "RetrievalType": ".retrieval_type", + "ReviewSnippet": ".review_snippet", + "RotateSigningSecretResponse": ".rotate_signing_secret_response", + "ServiceTier": ".service_tier", + "SigningSecret": ".signing_secret", + "SigningSecretRead": ".signing_secret_read", + "Source": ".source", + "SourceType": ".source_type", + "SpeechConfig": ".speech_config", + "Step": ".step", + "StepDelta": ".step_delta", + "StepDeltaData": ".step_delta_data", + "StepDeltaEventType": ".step_delta_event_type", + "StepStart": ".step_start", + "StepStartEventType": ".step_start_event_type", + "StepStop": ".step_stop", + "StepStopEventType": ".step_stop_event_type", + "TextAnnotationDelta": ".text_annotation_delta", + "TextAnnotationDeltaType": ".text_annotation_delta_type", + "TextContent": ".text_content", + "TextContentType": ".text_content_type", + "TextDelta": ".text_delta", + "TextDeltaType": ".text_delta_type", + "TextResponseFormat": ".text_response_format", + "TextResponseFormatMimeType": ".text_response_format_mime_type", + "TextResponseFormatType": ".text_response_format_type", + "ThinkingLevel": ".thinking_level", + "ThinkingSummaries": ".thinking_summaries", + "ThoughtContent": ".thought_content", + "ThoughtContentType": ".thought_content_type", + "ThoughtSignatureDelta": ".thought_signature_delta", + "ThoughtSignatureDeltaType": ".thought_signature_delta_type", + "ThoughtStep": ".thought_step", + "ThoughtStepType": ".thought_step_type", + "ThoughtSummaryContent": ".thought_summary_content", + "ThoughtSummaryDelta": ".thought_summary_delta", + "ThoughtSummaryDeltaType": ".thought_summary_delta_type", + "Tool": ".tool", + "ToolChoiceConfig": ".tool_choice_config", + "ToolChoiceType": ".tool_choice_type", + "Turn": ".turn", + "TurnContent": ".turn_content", + "UrlCitation": ".url_citation", + "UrlCitationType": ".url_citation_type", + "UrlContext": ".url_context", + "UrlContextCallArguments": ".url_context_call_arguments", + "UrlContextCallDelta": ".url_context_call_delta", + "UrlContextCallDeltaType": ".url_context_call_delta_type", + "UrlContextCallStep": ".url_context_call_step", + "UrlContextCallStepArguments": ".url_context_call_step_arguments", + "UrlContextCallStepType": ".url_context_call_step_type", + "UrlContextResult": ".url_context_result", + "UrlContextResultDelta": ".url_context_result_delta", + "UrlContextResultDeltaType": ".url_context_result_delta_type", + "UrlContextResultItem": ".url_context_result_item", + "UrlContextResultItemStatus": ".url_context_result_item_status", + "UrlContextResultStatus": ".url_context_result_status", + "UrlContextResultStep": ".url_context_result_step", + "UrlContextResultStepType": ".url_context_result_step_type", + "UrlContextType": ".url_context_type", + "Usage": ".usage", + "UserInputStep": ".user_input_step", + "UserInputStepType": ".user_input_step_type", + "VertexAiSearchConfig": ".vertex_ai_search_config", + "VideoContent": ".video_content", + "VideoContentMimeType": ".video_content_mime_type", + "VideoContentType": ".video_content_type", + "VideoDelta": ".video_delta", + "VideoDeltaMimeType": ".video_delta_mime_type", + "VideoDeltaType": ".video_delta_type", + "Webhook": ".webhook", + "WebhookConfig": ".webhook_config", + "WebhookRead": ".webhook_read", + "WebhookState": ".webhook_state", + "WebhookSubscribedEventsItem": ".webhook_subscribed_events_item", + "WebhookSubscribedEventsItemFive": ".webhook_subscribed_events_item_five", + "WebhookSubscribedEventsItemFour": ".webhook_subscribed_events_item_four", + "WebhookSubscribedEventsItemOne": ".webhook_subscribed_events_item_one", + "WebhookSubscribedEventsItemSeven": ".webhook_subscribed_events_item_seven", + "WebhookSubscribedEventsItemSix": ".webhook_subscribed_events_item_six", + "WebhookSubscribedEventsItemThree": ".webhook_subscribed_events_item_three", + "WebhookSubscribedEventsItemTwo": ".webhook_subscribed_events_item_two", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Agent", + "AgentBaseEnvironment", + "AgentEgressAllowlist", + "AgentEgressRule", + "AgentOption", + "AgentOptionOne", + "AgentOptionThree", + "AgentOptionTwo", + "AgentTool", + "AllowedTools", + "AllowlistEntry", + "Annotation", + "ArgumentsDelta", + "ArgumentsDeltaType", + "AudioContent", + "AudioContentMimeType", + "AudioContentType", + "AudioDelta", + "AudioDeltaMimeType", + "AudioDeltaType", + "AudioResponseFormat", + "AudioResponseFormatDelivery", + "AudioResponseFormatMimeType", + "AudioResponseFormatType", + "CodeExecution", + "CodeExecutionCallArguments", + "CodeExecutionCallArgumentsLanguage", + "CodeExecutionCallDelta", + "CodeExecutionCallDeltaType", + "CodeExecutionCallStep", + "CodeExecutionCallStepArguments", + "CodeExecutionCallStepArgumentsLanguage", + "CodeExecutionCallStepType", + "CodeExecutionResultDelta", + "CodeExecutionResultDeltaType", + "CodeExecutionResultStep", + "CodeExecutionResultStepType", + "CodeExecutionType", + "ComputerUse", + "ComputerUseEnvironment", + "ComputerUseType", + "Content", + "ContentDelta", + "ContentDeltaData", + "ContentDeltaEventType", + "ContentStart", + "ContentStartEventType", + "ContentStop", + "ContentStopEventType", + "CreateAgentInteractionParams", + "CreateAgentInteractionParamsAgentConfig", + "CreateAgentInteractionParamsEnvironment", + "CreateAgentInteractionParamsResponseFormat", + "CreateModelInteractionParams", + "CreateModelInteractionParamsEnvironment", + "CreateModelInteractionParamsResponseFormat", + "DeepResearchAgentConfig", + "DeepResearchAgentConfigType", + "DeepResearchAgentConfigVisualization", + "DocumentContent", + "DocumentContentMimeType", + "DocumentContentType", + "DocumentDelta", + "DocumentDeltaMimeType", + "DocumentDeltaType", + "DynamicAgentConfig", + "DynamicAgentConfigType", + "Empty", + "EnvironmentConfig", + "EnvironmentConfigNetwork", + "EnvironmentConfigNetworkOne", + "EnvironmentConfigType", + "EnvironmentNetworkEgressAllowlist", + "EnvironmentNetworkEgressAllowlistAllowlist", + "EnvironmentNetworkEgressAllowlistOne", + "EnvironmentSource", + "EnvironmentSourceType", + "Error", + "ErrorEvent", + "ErrorEventEventType", + "FileCitation", + "FileCitationType", + "FileSearch", + "FileSearchCallDelta", + "FileSearchCallDeltaType", + "FileSearchCallStep", + "FileSearchCallStepType", + "FileSearchResult", + "FileSearchResultDelta", + "FileSearchResultDeltaType", + "FileSearchResultStep", + "FileSearchResultStepType", + "FileSearchType", + "Function", + "FunctionCallDelta", + "FunctionCallDeltaType", + "FunctionCallStep", + "FunctionCallStepType", + "FunctionResultDelta", + "FunctionResultDeltaResult", + "FunctionResultDeltaType", + "FunctionResultStep", + "FunctionResultStepResult", + "FunctionResultStepType", + "FunctionResultSubcontent", + "FunctionType", + "GenerationConfig", + "GenerationConfigToolChoice", + "GoogleMaps", + "GoogleMapsCallArguments", + "GoogleMapsCallDelta", + "GoogleMapsCallDeltaType", + "GoogleMapsCallStep", + "GoogleMapsCallStepArguments", + "GoogleMapsCallStepType", + "GoogleMapsResult", + "GoogleMapsResultDelta", + "GoogleMapsResultDeltaType", + "GoogleMapsResultItem", + "GoogleMapsResultPlaces", + "GoogleMapsResultStep", + "GoogleMapsResultStepType", + "GoogleMapsType", + "GoogleSearch", + "GoogleSearchCallArguments", + "GoogleSearchCallDelta", + "GoogleSearchCallDeltaType", + "GoogleSearchCallStep", + "GoogleSearchCallStepArguments", + "GoogleSearchCallStepSearchType", + "GoogleSearchCallStepType", + "GoogleSearchResult", + "GoogleSearchResultDelta", + "GoogleSearchResultDeltaType", + "GoogleSearchResultItem", + "GoogleSearchResultStep", + "GoogleSearchResultStepType", + "GoogleSearchSearchTypesItem", + "GoogleSearchType", + "GroundingToolCount", + "GroundingToolCountType", + "ImageConfig", + "ImageConfigAspectRatio", + "ImageConfigImageSize", + "ImageContent", + "ImageContentMimeType", + "ImageContentType", + "ImageDelta", + "ImageDeltaMimeType", + "ImageDeltaType", + "ImageResponseFormat", + "ImageResponseFormatAspectRatio", + "ImageResponseFormatDelivery", + "ImageResponseFormatImageSize", + "ImageResponseFormatMimeType", + "ImageResponseFormatType", + "Interaction", + "InteractionAgentConfig", + "InteractionCompletedEvent", + "InteractionCompletedEventEventType", + "InteractionCreatedEvent", + "InteractionCreatedEventEventType", + "InteractionEnvironment", + "InteractionResponseFormat", + "InteractionSseEvent", + "InteractionStatus", + "InteractionStatusUpdate", + "InteractionStatusUpdateEventType", + "InteractionStatusUpdateStatus", + "InteractionsInput", + "ListAgentsResponse", + "ListWebhooksResponse", + "McpServer", + "McpServerToolCallDelta", + "McpServerToolCallDeltaType", + "McpServerToolCallStep", + "McpServerToolCallStepType", + "McpServerToolResultDelta", + "McpServerToolResultDeltaResult", + "McpServerToolResultDeltaType", + "McpServerToolResultStep", + "McpServerToolResultStepResult", + "McpServerToolResultStepType", + "McpServerType", + "MediaResolution", + "ModalityTokens", + "ModelOption", + "ModelOptionEight", + "ModelOptionEighteen", + "ModelOptionEleven", + "ModelOptionFifteen", + "ModelOptionFive", + "ModelOptionFour", + "ModelOptionFourteen", + "ModelOptionNine", + "ModelOptionNineteen", + "ModelOptionOne", + "ModelOptionSeven", + "ModelOptionSeventeen", + "ModelOptionSix", + "ModelOptionSixteen", + "ModelOptionTen", + "ModelOptionThirteen", + "ModelOptionThree", + "ModelOptionTwelve", + "ModelOptionTwenty", + "ModelOptionTwo", + "ModelOutputStep", + "ModelOutputStepType", + "PingWebhookRequest", + "PingWebhookResponse", + "PlaceCitation", + "PlaceCitationType", + "Places", + "ResponseFormat", + "ResponseFormatList", + "ResponseModality", + "Retrieval", + "RetrievalRetrievalTypesItem", + "RetrievalType", + "ReviewSnippet", + "RotateSigningSecretResponse", + "ServiceTier", + "SigningSecret", + "SigningSecretRead", + "Source", + "SourceType", + "SpeechConfig", + "Step", + "StepDelta", + "StepDeltaData", + "StepDeltaEventType", + "StepStart", + "StepStartEventType", + "StepStop", + "StepStopEventType", + "TextAnnotationDelta", + "TextAnnotationDeltaType", + "TextContent", + "TextContentType", + "TextDelta", + "TextDeltaType", + "TextResponseFormat", + "TextResponseFormatMimeType", + "TextResponseFormatType", + "ThinkingLevel", + "ThinkingSummaries", + "ThoughtContent", + "ThoughtContentType", + "ThoughtSignatureDelta", + "ThoughtSignatureDeltaType", + "ThoughtStep", + "ThoughtStepType", + "ThoughtSummaryContent", + "ThoughtSummaryDelta", + "ThoughtSummaryDeltaType", + "Tool", + "ToolChoiceConfig", + "ToolChoiceType", + "Turn", + "TurnContent", + "UrlCitation", + "UrlCitationType", + "UrlContext", + "UrlContextCallArguments", + "UrlContextCallDelta", + "UrlContextCallDeltaType", + "UrlContextCallStep", + "UrlContextCallStepArguments", + "UrlContextCallStepType", + "UrlContextResult", + "UrlContextResultDelta", + "UrlContextResultDeltaType", + "UrlContextResultItem", + "UrlContextResultItemStatus", + "UrlContextResultStatus", + "UrlContextResultStep", + "UrlContextResultStepType", + "UrlContextType", + "Usage", + "UserInputStep", + "UserInputStepType", + "VertexAiSearchConfig", + "VideoContent", + "VideoContentMimeType", + "VideoContentType", + "VideoDelta", + "VideoDeltaMimeType", + "VideoDeltaType", + "Webhook", + "WebhookConfig", + "WebhookRead", + "WebhookState", + "WebhookSubscribedEventsItem", + "WebhookSubscribedEventsItemFive", + "WebhookSubscribedEventsItemFour", + "WebhookSubscribedEventsItemOne", + "WebhookSubscribedEventsItemSeven", + "WebhookSubscribedEventsItemSix", + "WebhookSubscribedEventsItemThree", + "WebhookSubscribedEventsItemTwo", +] diff --git a/google/genai/_fern_interactions/types/agent.py b/google/genai/_fern_interactions/types/agent.py new file mode 100644 index 000000000..f6fefb666 --- /dev/null +++ b/google/genai/_fern_interactions/types/agent.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .agent_base_environment import AgentBaseEnvironment +from .agent_tool import AgentTool + + +class Agent(UniversalBaseModel): + """ + An agent definition for the CreateAgent API. + This message is the target for annotation-parser-based JSON parsing. + New format: + { + "id": "customer-sentinel", + "base_agent": "", + "system_instruction": "...", + "base_environment": { "type": "remote", "sources": [...] }, + "tools": [ {"type": "code_execution"} ] + } + """ + + base_agent: typing.Optional[str] = pydantic.Field(default=None) + """ + The base agent to extend. + """ + + base_environment: typing.Optional[AgentBaseEnvironment] = pydantic.Field(default=None) + """ + The environment configuration for the agent. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Agent description for developers to quickly read and understand. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the agent. + """ + + system_instruction: typing.Optional[str] = pydantic.Field(default=None) + """ + System instruction for the agent. + """ + + tools: typing.Optional[typing.List[AgentTool]] = pydantic.Field(default=None) + """ + The tools available to the agent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/agent_base_environment.py b/google/genai/_fern_interactions/types/agent_base_environment.py new file mode 100644 index 000000000..e106e0c50 --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_base_environment.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_config import EnvironmentConfig + +AgentBaseEnvironment = typing.Union[str, EnvironmentConfig] diff --git a/google/genai/_fern_interactions/types/agent_egress_allowlist.py b/google/genai/_fern_interactions/types/agent_egress_allowlist.py new file mode 100644 index 000000000..ba4335aea --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_egress_allowlist.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .agent_egress_rule import AgentEgressRule + + +class AgentEgressAllowlist(UniversalBaseModel): + """ + Network egress configuration for the environment. + """ + + allowlist: typing.Optional[typing.List[AgentEgressRule]] = pydantic.Field(default=None) + """ + List of allowed domains and their configurations. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/agent_egress_rule.py b/google/genai/_fern_interactions/types/agent_egress_rule.py new file mode 100644 index 000000000..1c4e9f9cc --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_egress_rule.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class AgentEgressRule(UniversalBaseModel): + """ + A network egress rule that controls which external domains the + environment is allowed to reach. Each rule identifies a target domain + and, optionally, a set of HTTP headers to inject into every matching + outbound request. + """ + + domain: typing.Optional[str] = pydantic.Field(default=None) + """ + The domain pattern to match for this rule. + Use an exact hostname (e.g., `github.com`), a wildcard prefix + (e.g., `*.googleapis.com`), or `*` to match all domains. + """ + + transform: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Headers to inject into requests matching this rule. + Key: header name (e.g., "Authorization"). + Value: header value (e.g., "Bearer your-token"). + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/agent_option.py b/google/genai/_fern_interactions/types/agent_option.py new file mode 100644 index 000000000..805eafabd --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_option.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .agent_option_one import AgentOptionOne +from .agent_option_three import AgentOptionThree +from .agent_option_two import AgentOptionTwo + +AgentOption = typing.Union[str, AgentOptionOne, AgentOptionTwo, AgentOptionThree] diff --git a/google/genai/_fern_interactions/types/agent_option_one.py b/google/genai/_fern_interactions/types/agent_option_one.py new file mode 100644 index 000000000..ec0fff94d --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_option_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AgentOptionOne = typing.Union[typing.Literal["deep-research-pro-preview-12-2025"], typing.Any] diff --git a/google/genai/_fern_interactions/types/agent_option_three.py b/google/genai/_fern_interactions/types/agent_option_three.py new file mode 100644 index 000000000..de758c9aa --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_option_three.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AgentOptionThree = typing.Union[typing.Literal["deep-research-max-preview-04-2026"], typing.Any] diff --git a/google/genai/_fern_interactions/types/agent_option_two.py b/google/genai/_fern_interactions/types/agent_option_two.py new file mode 100644 index 000000000..471a6da77 --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_option_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AgentOptionTwo = typing.Union[typing.Literal["deep-research-preview-04-2026"], typing.Any] diff --git a/google/genai/_fern_interactions/types/agent_tool.py b/google/genai/_fern_interactions/types/agent_tool.py new file mode 100644 index 000000000..24d47eacc --- /dev/null +++ b/google/genai/_fern_interactions/types/agent_tool.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .code_execution import CodeExecution +from .google_search import GoogleSearch +from .mcp_server import McpServer +from .url_context import UrlContext + +AgentTool = typing.Union[CodeExecution, GoogleSearch, UrlContext, McpServer] diff --git a/google/genai/_fern_interactions/types/allowed_tools.py b/google/genai/_fern_interactions/types/allowed_tools.py new file mode 100644 index 000000000..b4c93bfc4 --- /dev/null +++ b/google/genai/_fern_interactions/types/allowed_tools.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .tool_choice_type import ToolChoiceType + + +class AllowedTools(UniversalBaseModel): + """ + The configuration for allowed tools. + """ + + mode: typing.Optional[ToolChoiceType] = pydantic.Field(default=None) + """ + The mode of the tool choice. + """ + + tools: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The names of the allowed tools. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/allowlist_entry.py b/google/genai/_fern_interactions/types/allowlist_entry.py new file mode 100644 index 000000000..9da5ccf89 --- /dev/null +++ b/google/genai/_fern_interactions/types/allowlist_entry.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class AllowlistEntry(UniversalBaseModel): + """ + A single domain allowlist rule with optional header injection. + """ + + domain: str = pydantic.Field() + """ + Domain to allow outbound requests to. Supports wildcards (e.g. '*.googleapis.com'). Use '*' to allow all domains. + """ + + transform: typing.Optional[typing.List[typing.Dict[str, str]]] = pydantic.Field(default=None) + """ + Headers to inject on all outbound requests matching this domain. Each entry is a flat {header_name: header_value} object. The egress proxy injects these automatically. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/annotation.py b/google/genai/_fern_interactions/types/annotation.py new file mode 100644 index 000000000..371b43851 --- /dev/null +++ b/google/genai/_fern_interactions/types/annotation.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .file_citation import FileCitation +from .place_citation import PlaceCitation +from .url_citation import UrlCitation + +Annotation = typing.Union[UrlCitation, FileCitation, PlaceCitation] diff --git a/google/genai/_fern_interactions/types/arguments_delta.py b/google/genai/_fern_interactions/types/arguments_delta.py new file mode 100644 index 000000000..0bdaf5586 --- /dev/null +++ b/google/genai/_fern_interactions/types/arguments_delta.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .arguments_delta_type import ArgumentsDeltaType + + +class ArgumentsDelta(UniversalBaseModel): + arguments: typing.Optional[str] = None + type: ArgumentsDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/arguments_delta_type.py b/google/genai/_fern_interactions/types/arguments_delta_type.py new file mode 100644 index 000000000..1abcf9dd3 --- /dev/null +++ b/google/genai/_fern_interactions/types/arguments_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArgumentsDeltaType = typing.Union[typing.Literal["arguments_delta"], typing.Any] diff --git a/google/genai/_fern_interactions/types/audio_content.py b/google/genai/_fern_interactions/types/audio_content.py new file mode 100644 index 000000000..8c6314d02 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_content.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .audio_content_mime_type import AudioContentMimeType +from .audio_content_type import AudioContentType + + +class AudioContent(UniversalBaseModel): + """ + An audio content block. + """ + + channels: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of audio channels. + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The audio content. + """ + + mime_type: typing.Optional[AudioContentMimeType] = pydantic.Field(default=None) + """ + The mime type of the audio. + """ + + sample_rate: typing.Optional[int] = pydantic.Field(default=None) + """ + The sample rate of the audio. + """ + + type: AudioContentType + uri: typing.Optional[str] = pydantic.Field(default=None) + """ + The URI of the audio. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/audio_content_mime_type.py b/google/genai/_fern_interactions/types/audio_content_mime_type.py new file mode 100644 index 000000000..0769258b3 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_content_mime_type.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioContentMimeType = typing.Union[ + typing.Literal[ + "audio/wav", + "audio/mp3", + "audio/aiff", + "audio/aac", + "audio/ogg", + "audio/flac", + "audio/mpeg", + "audio/m4a", + "audio/l16", + "audio/opus", + "audio/alaw", + "audio/mulaw", + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/audio_content_type.py b/google/genai/_fern_interactions/types/audio_content_type.py new file mode 100644 index 000000000..bb0dcaa64 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioContentType = typing.Union[typing.Literal["audio"], typing.Any] diff --git a/google/genai/_fern_interactions/types/audio_delta.py b/google/genai/_fern_interactions/types/audio_delta.py new file mode 100644 index 000000000..fb77fa396 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_delta.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .audio_delta_mime_type import AudioDeltaMimeType +from .audio_delta_type import AudioDeltaType + + +class AudioDelta(UniversalBaseModel): + channels: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of audio channels. + """ + + data: typing.Optional[str] = None + mime_type: typing.Optional[AudioDeltaMimeType] = None + rate: typing.Optional[int] = pydantic.Field(default=None) + """ + Deprecated. Use sample_rate instead. The value is ignored. + """ + + sample_rate: typing.Optional[int] = pydantic.Field(default=None) + """ + The sample rate of the audio. + """ + + type: AudioDeltaType + uri: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/audio_delta_mime_type.py b/google/genai/_fern_interactions/types/audio_delta_mime_type.py new file mode 100644 index 000000000..190bff992 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_delta_mime_type.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioDeltaMimeType = typing.Union[ + typing.Literal[ + "audio/wav", + "audio/mp3", + "audio/aiff", + "audio/aac", + "audio/ogg", + "audio/flac", + "audio/mpeg", + "audio/m4a", + "audio/l16", + "audio/opus", + "audio/alaw", + "audio/mulaw", + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/audio_delta_type.py b/google/genai/_fern_interactions/types/audio_delta_type.py new file mode 100644 index 000000000..5fa39bcb6 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioDeltaType = typing.Union[typing.Literal["audio"], typing.Any] diff --git a/google/genai/_fern_interactions/types/audio_response_format.py b/google/genai/_fern_interactions/types/audio_response_format.py new file mode 100644 index 000000000..600b58f97 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_response_format.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .audio_response_format_delivery import AudioResponseFormatDelivery +from .audio_response_format_mime_type import AudioResponseFormatMimeType +from .audio_response_format_type import AudioResponseFormatType + + +class AudioResponseFormat(UniversalBaseModel): + """ + Configuration for audio output format. + """ + + bit_rate: typing.Optional[int] = pydantic.Field(default=None) + """ + Bit rate in bits per second (bps). Only applicable for compressed formats + (MP3, Opus). + """ + + delivery: typing.Optional[AudioResponseFormatDelivery] = pydantic.Field(default=None) + """ + The delivery mode for the audio output. + """ + + mime_type: typing.Optional[AudioResponseFormatMimeType] = pydantic.Field(default=None) + """ + The MIME type of the audio output. + """ + + sample_rate: typing.Optional[int] = pydantic.Field(default=None) + """ + Sample rate in Hz. + """ + + type: AudioResponseFormatType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/audio_response_format_delivery.py b/google/genai/_fern_interactions/types/audio_response_format_delivery.py new file mode 100644 index 000000000..718913fe6 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_response_format_delivery.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioResponseFormatDelivery = typing.Union[typing.Literal["inline", "uri"], typing.Any] diff --git a/google/genai/_fern_interactions/types/audio_response_format_mime_type.py b/google/genai/_fern_interactions/types/audio_response_format_mime_type.py new file mode 100644 index 000000000..eaf23cfa3 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_response_format_mime_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioResponseFormatMimeType = typing.Union[ + typing.Literal["audio/mp3", "audio/ogg_opus", "audio/l16", "audio/wav", "audio/alaw", "audio/mulaw"], typing.Any +] diff --git a/google/genai/_fern_interactions/types/audio_response_format_type.py b/google/genai/_fern_interactions/types/audio_response_format_type.py new file mode 100644 index 000000000..c08b4a542 --- /dev/null +++ b/google/genai/_fern_interactions/types/audio_response_format_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AudioResponseFormatType = typing.Union[typing.Literal["audio"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution.py b/google/genai/_fern_interactions/types/code_execution.py new file mode 100644 index 000000000..aef341b18 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_type import CodeExecutionType + + +class CodeExecution(UniversalBaseModel): + """ + A tool that can be used by the model to execute code. + """ + + type: CodeExecutionType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_call_arguments.py b/google/genai/_fern_interactions/types/code_execution_call_arguments.py new file mode 100644 index 000000000..de4a2fc5f --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_arguments.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_call_arguments_language import CodeExecutionCallArgumentsLanguage + + +class CodeExecutionCallArguments(UniversalBaseModel): + """ + The arguments to pass to the code execution. + """ + + code: typing.Optional[str] = pydantic.Field(default=None) + """ + The code to be executed. + """ + + language: typing.Optional[CodeExecutionCallArgumentsLanguage] = pydantic.Field(default=None) + """ + Programming language of the `code`. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_call_arguments_language.py b/google/genai/_fern_interactions/types/code_execution_call_arguments_language.py new file mode 100644 index 000000000..18b91127b --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_arguments_language.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionCallArgumentsLanguage = typing.Union[typing.Literal["python"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_call_delta.py b/google/genai/_fern_interactions/types/code_execution_call_delta.py new file mode 100644 index 000000000..9372b8ff8 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_delta.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_call_arguments import CodeExecutionCallArguments +from .code_execution_call_delta_type import CodeExecutionCallDeltaType + + +class CodeExecutionCallDelta(UniversalBaseModel): + arguments: CodeExecutionCallArguments + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: CodeExecutionCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_call_delta_type.py b/google/genai/_fern_interactions/types/code_execution_call_delta_type.py new file mode 100644 index 000000000..fe81f0284 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionCallDeltaType = typing.Union[typing.Literal["code_execution_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_call_step.py b/google/genai/_fern_interactions/types/code_execution_call_step.py new file mode 100644 index 000000000..37929e003 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_step.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_call_step_arguments import CodeExecutionCallStepArguments +from .code_execution_call_step_type import CodeExecutionCallStepType + + +class CodeExecutionCallStep(UniversalBaseModel): + """ + Code execution call step. + """ + + arguments: CodeExecutionCallStepArguments = pydantic.Field() + """ + Required. The arguments to pass to the code execution. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: CodeExecutionCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_call_step_arguments.py b/google/genai/_fern_interactions/types/code_execution_call_step_arguments.py new file mode 100644 index 000000000..df6f5c9aa --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_step_arguments.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_call_step_arguments_language import CodeExecutionCallStepArgumentsLanguage + + +class CodeExecutionCallStepArguments(UniversalBaseModel): + """ + The arguments to pass to the code execution. + """ + + code: typing.Optional[str] = pydantic.Field(default=None) + """ + The code to be executed. + """ + + language: typing.Optional[CodeExecutionCallStepArgumentsLanguage] = pydantic.Field(default=None) + """ + Programming language of the `code`. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_call_step_arguments_language.py b/google/genai/_fern_interactions/types/code_execution_call_step_arguments_language.py new file mode 100644 index 000000000..0a666d006 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_step_arguments_language.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionCallStepArgumentsLanguage = typing.Union[typing.Literal["python"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_call_step_type.py b/google/genai/_fern_interactions/types/code_execution_call_step_type.py new file mode 100644 index 000000000..b70690c43 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionCallStepType = typing.Union[typing.Literal["code_execution_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_result_delta.py b/google/genai/_fern_interactions/types/code_execution_result_delta.py new file mode 100644 index 000000000..2a4dc9a4f --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_result_delta.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_result_delta_type import CodeExecutionResultDeltaType + + +class CodeExecutionResultDelta(UniversalBaseModel): + is_error: typing.Optional[bool] = None + result: str + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: CodeExecutionResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_result_delta_type.py b/google/genai/_fern_interactions/types/code_execution_result_delta_type.py new file mode 100644 index 000000000..3fbf11cbd --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionResultDeltaType = typing.Union[typing.Literal["code_execution_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_result_step.py b/google/genai/_fern_interactions/types/code_execution_result_step.py new file mode 100644 index 000000000..d5e6f3354 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_result_step.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .code_execution_result_step_type import CodeExecutionResultStepType + + +class CodeExecutionResultStep(UniversalBaseModel): + """ + Code execution result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + is_error: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the code execution resulted in an error. + """ + + result: str = pydantic.Field() + """ + Required. The output of the code execution. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: CodeExecutionResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/code_execution_result_step_type.py b/google/genai/_fern_interactions/types/code_execution_result_step_type.py new file mode 100644 index 000000000..dd3a66730 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionResultStepType = typing.Union[typing.Literal["code_execution_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/code_execution_type.py b/google/genai/_fern_interactions/types/code_execution_type.py new file mode 100644 index 000000000..86baa2716 --- /dev/null +++ b/google/genai/_fern_interactions/types/code_execution_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CodeExecutionType = typing.Union[typing.Literal["code_execution"], typing.Any] diff --git a/google/genai/_fern_interactions/types/computer_use.py b/google/genai/_fern_interactions/types/computer_use.py new file mode 100644 index 000000000..b0655c368 --- /dev/null +++ b/google/genai/_fern_interactions/types/computer_use.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .computer_use_environment import ComputerUseEnvironment +from .computer_use_type import ComputerUseType + + +class ComputerUse(UniversalBaseModel): + """ + A tool that can be used by the model to interact with the computer. + """ + + environment: typing.Optional[ComputerUseEnvironment] = pydantic.Field(default=None) + """ + The environment being operated. + """ + + excluded_predefined_functions: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The list of predefined functions that are excluded from the model call. + """ + + type: ComputerUseType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/computer_use_environment.py b/google/genai/_fern_interactions/types/computer_use_environment.py new file mode 100644 index 000000000..e848b6fc3 --- /dev/null +++ b/google/genai/_fern_interactions/types/computer_use_environment.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ComputerUseEnvironment = typing.Union[typing.Literal["browser"], typing.Any] diff --git a/google/genai/_fern_interactions/types/computer_use_type.py b/google/genai/_fern_interactions/types/computer_use_type.py new file mode 100644 index 000000000..24cc79f46 --- /dev/null +++ b/google/genai/_fern_interactions/types/computer_use_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ComputerUseType = typing.Union[typing.Literal["computer_use"], typing.Any] diff --git a/google/genai/_fern_interactions/types/content.py b/google/genai/_fern_interactions/types/content.py new file mode 100644 index 000000000..e61c0b9e0 --- /dev/null +++ b/google/genai/_fern_interactions/types/content.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .audio_content import AudioContent +from .document_content import DocumentContent +from .image_content import ImageContent +from .text_content import TextContent +from .video_content import VideoContent + +Content = typing.Union[TextContent, ImageContent, AudioContent, DocumentContent, VideoContent] diff --git a/google/genai/_fern_interactions/types/content_delta.py b/google/genai/_fern_interactions/types/content_delta.py new file mode 100644 index 000000000..6b4dc486b --- /dev/null +++ b/google/genai/_fern_interactions/types/content_delta.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .content_delta_data import ContentDeltaData +from .content_delta_event_type import ContentDeltaEventType + + +class ContentDelta(UniversalBaseModel): + delta: ContentDeltaData + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: ContentDeltaEventType + index: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/content_delta_data.py b/google/genai/_fern_interactions/types/content_delta_data.py new file mode 100644 index 000000000..44bfcf1a6 --- /dev/null +++ b/google/genai/_fern_interactions/types/content_delta_data.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .audio_delta import AudioDelta +from .code_execution_call_delta import CodeExecutionCallDelta +from .code_execution_result_delta import CodeExecutionResultDelta +from .document_delta import DocumentDelta +from .file_search_call_delta import FileSearchCallDelta +from .file_search_result_delta import FileSearchResultDelta +from .function_call_delta import FunctionCallDelta +from .function_result_delta import FunctionResultDelta +from .google_maps_call_delta import GoogleMapsCallDelta +from .google_maps_result_delta import GoogleMapsResultDelta +from .google_search_call_delta import GoogleSearchCallDelta +from .google_search_result_delta import GoogleSearchResultDelta +from .image_delta import ImageDelta +from .mcp_server_tool_call_delta import McpServerToolCallDelta +from .mcp_server_tool_result_delta import McpServerToolResultDelta +from .text_annotation_delta import TextAnnotationDelta +from .text_delta import TextDelta +from .thought_signature_delta import ThoughtSignatureDelta +from .thought_summary_delta import ThoughtSummaryDelta +from .url_context_call_delta import UrlContextCallDelta +from .url_context_result_delta import UrlContextResultDelta +from .video_delta import VideoDelta + +ContentDeltaData = typing.Union[ + TextDelta, + ImageDelta, + AudioDelta, + DocumentDelta, + VideoDelta, + ThoughtSummaryDelta, + ThoughtSignatureDelta, + FunctionCallDelta, + CodeExecutionCallDelta, + UrlContextCallDelta, + GoogleSearchCallDelta, + McpServerToolCallDelta, + FileSearchCallDelta, + GoogleMapsCallDelta, + FunctionResultDelta, + CodeExecutionResultDelta, + UrlContextResultDelta, + GoogleSearchResultDelta, + McpServerToolResultDelta, + FileSearchResultDelta, + GoogleMapsResultDelta, + TextAnnotationDelta, +] diff --git a/google/genai/_fern_interactions/types/content_delta_event_type.py b/google/genai/_fern_interactions/types/content_delta_event_type.py new file mode 100644 index 000000000..aa800e8aa --- /dev/null +++ b/google/genai/_fern_interactions/types/content_delta_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentDeltaEventType = typing.Union[typing.Literal["content.delta"], typing.Any] diff --git a/google/genai/_fern_interactions/types/content_start.py b/google/genai/_fern_interactions/types/content_start.py new file mode 100644 index 000000000..525d0c857 --- /dev/null +++ b/google/genai/_fern_interactions/types/content_start.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .content import Content +from .content_start_event_type import ContentStartEventType + + +class ContentStart(UniversalBaseModel): + content: Content + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: ContentStartEventType + index: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/content_start_event_type.py b/google/genai/_fern_interactions/types/content_start_event_type.py new file mode 100644 index 000000000..da00a567d --- /dev/null +++ b/google/genai/_fern_interactions/types/content_start_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentStartEventType = typing.Union[typing.Literal["content.start"], typing.Any] diff --git a/google/genai/_fern_interactions/types/content_stop.py b/google/genai/_fern_interactions/types/content_stop.py new file mode 100644 index 000000000..5c5d51384 --- /dev/null +++ b/google/genai/_fern_interactions/types/content_stop.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .content_stop_event_type import ContentStopEventType + + +class ContentStop(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: ContentStopEventType + index: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/content_stop_event_type.py b/google/genai/_fern_interactions/types/content_stop_event_type.py new file mode 100644 index 000000000..1a77e85fa --- /dev/null +++ b/google/genai/_fern_interactions/types/content_stop_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentStopEventType = typing.Union[typing.Literal["content.stop"], typing.Any] diff --git a/google/genai/_fern_interactions/types/create_agent_interaction_params.py b/google/genai/_fern_interactions/types/create_agent_interaction_params.py new file mode 100644 index 000000000..c0a4b2033 --- /dev/null +++ b/google/genai/_fern_interactions/types/create_agent_interaction_params.py @@ -0,0 +1,102 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .agent_option import AgentOption +from .create_agent_interaction_params_agent_config import CreateAgentInteractionParamsAgentConfig +from .create_agent_interaction_params_environment import CreateAgentInteractionParamsEnvironment +from .create_agent_interaction_params_response_format import CreateAgentInteractionParamsResponseFormat +from .interactions_input import InteractionsInput +from .response_modality import ResponseModality +from .service_tier import ServiceTier +from .tool import Tool +from .webhook_config import WebhookConfig + + +class CreateAgentInteractionParams(UniversalBaseModel): + """ + Parameters for creating agent interactions + """ + + agent: AgentOption = pydantic.Field() + """ + The name of the `Agent` used for generating the interaction. + """ + + background: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether to run the model interaction in the background. + """ + + store: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether to store the response and request for later retrieval. + """ + + stream: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether the interaction will be streamed. + """ + + environment: typing.Optional[CreateAgentInteractionParamsEnvironment] = pydantic.Field(default=None) + """ + The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + """ + + input: InteractionsInput + previous_interaction_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the previous interaction, if any. + """ + + response_format: typing.Optional[CreateAgentInteractionParamsResponseFormat] = pydantic.Field(default=None) + """ + Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + """ + + response_mime_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The mime type of the response. This is required if response_format is set. + """ + + response_modalities: typing.Optional[typing.List[ResponseModality]] = pydantic.Field(default=None) + """ + The requested modalities of the response (TEXT, IMAGE, AUDIO). + """ + + service_tier: typing.Optional[ServiceTier] = pydantic.Field(default=None) + """ + The service tier for the interaction. + """ + + system_instruction: typing.Optional[str] = pydantic.Field(default=None) + """ + System instruction for the interaction. + """ + + tools: typing.Optional[typing.List[Tool]] = pydantic.Field(default=None) + """ + A list of tool declarations the model may call during interaction. + """ + + webhook_config: typing.Optional[WebhookConfig] = pydantic.Field(default=None) + """ + Optional. Webhook configuration for receiving notifications when the + interaction completes. + """ + + agent_config: typing.Optional[CreateAgentInteractionParamsAgentConfig] = pydantic.Field(default=None) + """ + Configuration parameters for the agent interaction. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/create_agent_interaction_params_agent_config.py b/google/genai/_fern_interactions/types/create_agent_interaction_params_agent_config.py new file mode 100644 index 000000000..d82421ab2 --- /dev/null +++ b/google/genai/_fern_interactions/types/create_agent_interaction_params_agent_config.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .deep_research_agent_config import DeepResearchAgentConfig +from .dynamic_agent_config import DynamicAgentConfig + +CreateAgentInteractionParamsAgentConfig = typing.Union[DynamicAgentConfig, DeepResearchAgentConfig] diff --git a/google/genai/_fern_interactions/types/create_agent_interaction_params_environment.py b/google/genai/_fern_interactions/types/create_agent_interaction_params_environment.py new file mode 100644 index 000000000..6b707ce5b --- /dev/null +++ b/google/genai/_fern_interactions/types/create_agent_interaction_params_environment.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_config import EnvironmentConfig + +CreateAgentInteractionParamsEnvironment = typing.Union[str, EnvironmentConfig] diff --git a/google/genai/_fern_interactions/types/create_agent_interaction_params_response_format.py b/google/genai/_fern_interactions/types/create_agent_interaction_params_response_format.py new file mode 100644 index 000000000..3be819693 --- /dev/null +++ b/google/genai/_fern_interactions/types/create_agent_interaction_params_response_format.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .response_format import ResponseFormat +from .response_format_list import ResponseFormatList + +CreateAgentInteractionParamsResponseFormat = typing.Union[ResponseFormatList, ResponseFormat] diff --git a/google/genai/_fern_interactions/types/create_model_interaction_params.py b/google/genai/_fern_interactions/types/create_model_interaction_params.py new file mode 100644 index 000000000..c317b2ebd --- /dev/null +++ b/google/genai/_fern_interactions/types/create_model_interaction_params.py @@ -0,0 +1,102 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .create_model_interaction_params_environment import CreateModelInteractionParamsEnvironment +from .create_model_interaction_params_response_format import CreateModelInteractionParamsResponseFormat +from .generation_config import GenerationConfig +from .interactions_input import InteractionsInput +from .model_option import ModelOption +from .response_modality import ResponseModality +from .service_tier import ServiceTier +from .tool import Tool +from .webhook_config import WebhookConfig + + +class CreateModelInteractionParams(UniversalBaseModel): + """ + Parameters for creating model interactions + """ + + model: ModelOption = pydantic.Field() + """ + The name of the `Model` used for generating the interaction. + """ + + background: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether to run the model interaction in the background. + """ + + store: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether to store the response and request for later retrieval. + """ + + stream: typing.Optional[bool] = pydantic.Field(default=None) + """ + Input only. Whether the interaction will be streamed. + """ + + environment: typing.Optional[CreateModelInteractionParamsEnvironment] = pydantic.Field(default=None) + """ + The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + """ + + input: InteractionsInput + previous_interaction_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the previous interaction, if any. + """ + + response_format: typing.Optional[CreateModelInteractionParamsResponseFormat] = pydantic.Field(default=None) + """ + Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + """ + + response_mime_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The mime type of the response. This is required if response_format is set. + """ + + response_modalities: typing.Optional[typing.List[ResponseModality]] = pydantic.Field(default=None) + """ + The requested modalities of the response (TEXT, IMAGE, AUDIO). + """ + + service_tier: typing.Optional[ServiceTier] = pydantic.Field(default=None) + """ + The service tier for the interaction. + """ + + system_instruction: typing.Optional[str] = pydantic.Field(default=None) + """ + System instruction for the interaction. + """ + + tools: typing.Optional[typing.List[Tool]] = pydantic.Field(default=None) + """ + A list of tool declarations the model may call during interaction. + """ + + webhook_config: typing.Optional[WebhookConfig] = pydantic.Field(default=None) + """ + Optional. Webhook configuration for receiving notifications when the + interaction completes. + """ + + generation_config: typing.Optional[GenerationConfig] = pydantic.Field(default=None) + """ + Input only. Configuration parameters for the model interaction. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/create_model_interaction_params_environment.py b/google/genai/_fern_interactions/types/create_model_interaction_params_environment.py new file mode 100644 index 000000000..270064a6b --- /dev/null +++ b/google/genai/_fern_interactions/types/create_model_interaction_params_environment.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_config import EnvironmentConfig + +CreateModelInteractionParamsEnvironment = typing.Union[str, EnvironmentConfig] diff --git a/google/genai/_fern_interactions/types/create_model_interaction_params_response_format.py b/google/genai/_fern_interactions/types/create_model_interaction_params_response_format.py new file mode 100644 index 000000000..0844b9daa --- /dev/null +++ b/google/genai/_fern_interactions/types/create_model_interaction_params_response_format.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .response_format import ResponseFormat +from .response_format_list import ResponseFormatList + +CreateModelInteractionParamsResponseFormat = typing.Union[ResponseFormatList, ResponseFormat] diff --git a/google/genai/_fern_interactions/types/deep_research_agent_config.py b/google/genai/_fern_interactions/types/deep_research_agent_config.py new file mode 100644 index 000000000..e113d1eb8 --- /dev/null +++ b/google/genai/_fern_interactions/types/deep_research_agent_config.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .deep_research_agent_config_type import DeepResearchAgentConfigType +from .deep_research_agent_config_visualization import DeepResearchAgentConfigVisualization +from .thinking_summaries import ThinkingSummaries + + +class DeepResearchAgentConfig(UniversalBaseModel): + """ + Configuration for the Deep Research agent. + """ + + collaborative_planning: typing.Optional[bool] = pydantic.Field(default=None) + """ + Enables human-in-the-loop planning for the Deep Research agent. If set to + true, the Deep Research agent will provide a research plan in its response. + The agent will then proceed only if the user confirms the plan in the next + turn. + """ + + thinking_summaries: typing.Optional[ThinkingSummaries] = pydantic.Field(default=None) + """ + Whether to include thought summaries in the response. + """ + + type: DeepResearchAgentConfigType + visualization: typing.Optional[DeepResearchAgentConfigVisualization] = pydantic.Field(default=None) + """ + Whether to include visualizations in the response. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/deep_research_agent_config_type.py b/google/genai/_fern_interactions/types/deep_research_agent_config_type.py new file mode 100644 index 000000000..ac3898134 --- /dev/null +++ b/google/genai/_fern_interactions/types/deep_research_agent_config_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DeepResearchAgentConfigType = typing.Union[typing.Literal["deep-research"], typing.Any] diff --git a/google/genai/_fern_interactions/types/deep_research_agent_config_visualization.py b/google/genai/_fern_interactions/types/deep_research_agent_config_visualization.py new file mode 100644 index 000000000..f79d125d1 --- /dev/null +++ b/google/genai/_fern_interactions/types/deep_research_agent_config_visualization.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DeepResearchAgentConfigVisualization = typing.Union[typing.Literal["off", "auto"], typing.Any] diff --git a/google/genai/_fern_interactions/types/document_content.py b/google/genai/_fern_interactions/types/document_content.py new file mode 100644 index 000000000..4c1bd4e99 --- /dev/null +++ b/google/genai/_fern_interactions/types/document_content.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .document_content_mime_type import DocumentContentMimeType +from .document_content_type import DocumentContentType + + +class DocumentContent(UniversalBaseModel): + """ + A document content block. + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The document content. + """ + + mime_type: typing.Optional[DocumentContentMimeType] = pydantic.Field(default=None) + """ + The mime type of the document. + """ + + type: DocumentContentType + uri: typing.Optional[str] = pydantic.Field(default=None) + """ + The URI of the document. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/document_content_mime_type.py b/google/genai/_fern_interactions/types/document_content_mime_type.py new file mode 100644 index 000000000..95474202d --- /dev/null +++ b/google/genai/_fern_interactions/types/document_content_mime_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DocumentContentMimeType = typing.Union[typing.Literal["application/pdf"], typing.Any] diff --git a/google/genai/_fern_interactions/types/document_content_type.py b/google/genai/_fern_interactions/types/document_content_type.py new file mode 100644 index 000000000..78b85dbba --- /dev/null +++ b/google/genai/_fern_interactions/types/document_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DocumentContentType = typing.Union[typing.Literal["document"], typing.Any] diff --git a/google/genai/_fern_interactions/types/document_delta.py b/google/genai/_fern_interactions/types/document_delta.py new file mode 100644 index 000000000..9d24679a7 --- /dev/null +++ b/google/genai/_fern_interactions/types/document_delta.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .document_delta_mime_type import DocumentDeltaMimeType +from .document_delta_type import DocumentDeltaType + + +class DocumentDelta(UniversalBaseModel): + data: typing.Optional[str] = None + mime_type: typing.Optional[DocumentDeltaMimeType] = None + type: DocumentDeltaType + uri: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/document_delta_mime_type.py b/google/genai/_fern_interactions/types/document_delta_mime_type.py new file mode 100644 index 000000000..6428efdbb --- /dev/null +++ b/google/genai/_fern_interactions/types/document_delta_mime_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DocumentDeltaMimeType = typing.Union[typing.Literal["application/pdf"], typing.Any] diff --git a/google/genai/_fern_interactions/types/document_delta_type.py b/google/genai/_fern_interactions/types/document_delta_type.py new file mode 100644 index 000000000..331b39fc7 --- /dev/null +++ b/google/genai/_fern_interactions/types/document_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DocumentDeltaType = typing.Union[typing.Literal["document"], typing.Any] diff --git a/google/genai/_fern_interactions/types/dynamic_agent_config.py b/google/genai/_fern_interactions/types/dynamic_agent_config.py new file mode 100644 index 000000000..70909462f --- /dev/null +++ b/google/genai/_fern_interactions/types/dynamic_agent_config.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .dynamic_agent_config_type import DynamicAgentConfigType + + +class DynamicAgentConfig(UniversalBaseModel): + """ + Configuration for dynamic agents. + """ + + type: DynamicAgentConfigType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/dynamic_agent_config_type.py b/google/genai/_fern_interactions/types/dynamic_agent_config_type.py new file mode 100644 index 000000000..c7902edeb --- /dev/null +++ b/google/genai/_fern_interactions/types/dynamic_agent_config_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DynamicAgentConfigType = typing.Union[typing.Literal["dynamic"], typing.Any] diff --git a/google/genai/_fern_interactions/types/empty.py b/google/genai/_fern_interactions/types/empty.py new file mode 100644 index 000000000..6e05990b7 --- /dev/null +++ b/google/genai/_fern_interactions/types/empty.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class Empty(UniversalBaseModel): + """ + A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to use it as the request + or the response type of an API method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/environment_config.py b/google/genai/_fern_interactions/types/environment_config.py new file mode 100644 index 000000000..43e4d9ec1 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_config.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .environment_config_network import EnvironmentConfigNetwork +from .environment_config_type import EnvironmentConfigType +from .source import Source + + +class EnvironmentConfig(UniversalBaseModel): + """ + Configuration for a custom environment. + """ + + network: typing.Optional[EnvironmentConfigNetwork] = pydantic.Field(default=None) + """ + Network configuration for the environment. + """ + + sources: typing.Optional[typing.List[Source]] = None + type: EnvironmentConfigType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/environment_config_network.py b/google/genai/_fern_interactions/types/environment_config_network.py new file mode 100644 index 000000000..89c7b9604 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_config_network.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_config_network_one import EnvironmentConfigNetworkOne +from .environment_network_egress_allowlist import EnvironmentNetworkEgressAllowlist + +EnvironmentConfigNetwork = typing.Union[EnvironmentNetworkEgressAllowlist, EnvironmentConfigNetworkOne] diff --git a/google/genai/_fern_interactions/types/environment_config_network_one.py b/google/genai/_fern_interactions/types/environment_config_network_one.py new file mode 100644 index 000000000..51a2e9574 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_config_network_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EnvironmentConfigNetworkOne = typing.Union[typing.Literal["disabled"], typing.Any] diff --git a/google/genai/_fern_interactions/types/environment_config_type.py b/google/genai/_fern_interactions/types/environment_config_type.py new file mode 100644 index 000000000..1cc5f7e45 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_config_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EnvironmentConfigType = typing.Union[typing.Literal["remote"], typing.Any] diff --git a/google/genai/_fern_interactions/types/environment_network_egress_allowlist.py b/google/genai/_fern_interactions/types/environment_network_egress_allowlist.py new file mode 100644 index 000000000..414403cb1 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_network_egress_allowlist.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_network_egress_allowlist_allowlist import EnvironmentNetworkEgressAllowlistAllowlist +from .environment_network_egress_allowlist_one import EnvironmentNetworkEgressAllowlistOne + +EnvironmentNetworkEgressAllowlist = typing.Union[ + EnvironmentNetworkEgressAllowlistAllowlist, EnvironmentNetworkEgressAllowlistOne +] diff --git a/google/genai/_fern_interactions/types/environment_network_egress_allowlist_allowlist.py b/google/genai/_fern_interactions/types/environment_network_egress_allowlist_allowlist.py new file mode 100644 index 000000000..9ca629534 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_network_egress_allowlist_allowlist.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .allowlist_entry import AllowlistEntry + + +class EnvironmentNetworkEgressAllowlistAllowlist(UniversalBaseModel): + """ + Outbound networking configuration for the sandbox. When specified, restricts which external domains the sandbox can reach. Omit entirely to allow all outbound traffic with no header injection. + """ + + allowlist: typing.Optional[typing.List[AllowlistEntry]] = pydantic.Field(default=None) + """ + List of allowed outbound domains. Only requests to listed domains are permitted. Use [{'domain': '*'}] to allow all domains while still injecting headers on specific ones. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/environment_network_egress_allowlist_one.py b/google/genai/_fern_interactions/types/environment_network_egress_allowlist_one.py new file mode 100644 index 000000000..8f6e7ee6f --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_network_egress_allowlist_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EnvironmentNetworkEgressAllowlistOne = typing.Union[typing.Literal["disabled"], typing.Any] diff --git a/google/genai/_fern_interactions/types/environment_source.py b/google/genai/_fern_interactions/types/environment_source.py new file mode 100644 index 000000000..8c98d4be4 --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_source.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .environment_source_type import EnvironmentSourceType + + +class EnvironmentSource(UniversalBaseModel): + """ + A source to be mounted into the environment. + """ + + content: typing.Optional[str] = pydantic.Field(default=None) + """ + The inline content if `type` is `INLINE`. + """ + + encoding: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional encoding for inline content (e.g. `base64`). + """ + + source: typing.Optional[str] = pydantic.Field(default=None) + """ + The source URI. + """ + + target: typing.Optional[str] = pydantic.Field(default=None) + """ + Where the source should appear in the environment. + """ + + type: typing.Optional[EnvironmentSourceType] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/environment_source_type.py b/google/genai/_fern_interactions/types/environment_source_type.py new file mode 100644 index 000000000..3e97f262d --- /dev/null +++ b/google/genai/_fern_interactions/types/environment_source_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EnvironmentSourceType = typing.Union[typing.Literal["gcs", "inline", "repository", "skill_registry"], typing.Any] diff --git a/google/genai/_fern_interactions/types/error.py b/google/genai/_fern_interactions/types/error.py new file mode 100644 index 000000000..5b7941331 --- /dev/null +++ b/google/genai/_fern_interactions/types/error.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class Error(UniversalBaseModel): + """ + Error message from an interaction. + """ + + code: typing.Optional[str] = pydantic.Field(default=None) + """ + A URI that identifies the error type. + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + A human-readable error message. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/error_event.py b/google/genai/_fern_interactions/types/error_event.py new file mode 100644 index 000000000..238aadb31 --- /dev/null +++ b/google/genai/_fern_interactions/types/error_event.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .error import Error +from .error_event_event_type import ErrorEventEventType + + +class ErrorEvent(UniversalBaseModel): + error: typing.Optional[Error] = None + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: ErrorEventEventType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/error_event_event_type.py b/google/genai/_fern_interactions/types/error_event_event_type.py new file mode 100644 index 000000000..b25d0774c --- /dev/null +++ b/google/genai/_fern_interactions/types/error_event_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ErrorEventEventType = typing.Union[typing.Literal["error"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_citation.py b/google/genai/_fern_interactions/types/file_citation.py new file mode 100644 index 000000000..d697e639d --- /dev/null +++ b/google/genai/_fern_interactions/types/file_citation.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_citation_type import FileCitationType + + +class FileCitation(UniversalBaseModel): + """ + A file citation annotation. + """ + + custom_metadata: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + User provided metadata about the retrieved context. + """ + + document_uri: typing.Optional[str] = pydantic.Field(default=None) + """ + The URI of the file. + """ + + end_index: typing.Optional[int] = pydantic.Field(default=None) + """ + End of the attributed segment, exclusive. + """ + + file_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the file. + """ + + media_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Media ID in-case of image citations, if applicable. + """ + + page_number: typing.Optional[int] = pydantic.Field(default=None) + """ + Page number of the cited document, if applicable. + """ + + source: typing.Optional[str] = pydantic.Field(default=None) + """ + Source attributed for a portion of the text. + """ + + start_index: typing.Optional[int] = pydantic.Field(default=None) + """ + Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + type: FileCitationType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_citation_type.py b/google/genai/_fern_interactions/types/file_citation_type.py new file mode 100644 index 000000000..56d861e51 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_citation_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileCitationType = typing.Union[typing.Literal["file_citation"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_search.py b/google/genai/_fern_interactions/types/file_search.py new file mode 100644 index 000000000..0f7e579ca --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_search_type import FileSearchType + + +class FileSearch(UniversalBaseModel): + """ + A tool that can be used by the model to search files. + """ + + file_search_store_names: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The file search store names to search. + """ + + metadata_filter: typing.Optional[str] = pydantic.Field(default=None) + """ + Metadata filter to apply to the semantic retrieval documents and chunks. + """ + + top_k: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of semantic retrieval chunks to retrieve. + """ + + type: FileSearchType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_search_call_delta.py b/google/genai/_fern_interactions/types/file_search_call_delta.py new file mode 100644 index 000000000..9f98a6e4e --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_call_delta.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_search_call_delta_type import FileSearchCallDeltaType + + +class FileSearchCallDelta(UniversalBaseModel): + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FileSearchCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_search_call_delta_type.py b/google/genai/_fern_interactions/types/file_search_call_delta_type.py new file mode 100644 index 000000000..1ba2e9d80 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchCallDeltaType = typing.Union[typing.Literal["file_search_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_search_call_step.py b/google/genai/_fern_interactions/types/file_search_call_step.py new file mode 100644 index 000000000..8f9c90b20 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_call_step.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_search_call_step_type import FileSearchCallStepType + + +class FileSearchCallStep(UniversalBaseModel): + """ + File Search call step. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FileSearchCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_search_call_step_type.py b/google/genai/_fern_interactions/types/file_search_call_step_type.py new file mode 100644 index 000000000..8ff59d675 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchCallStepType = typing.Union[typing.Literal["file_search_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_search_result.py b/google/genai/_fern_interactions/types/file_search_result.py new file mode 100644 index 000000000..d66034c57 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_result.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchResult = typing.Dict[str, typing.Any] +""" +The result of the File Search. +""" diff --git a/google/genai/_fern_interactions/types/file_search_result_delta.py b/google/genai/_fern_interactions/types/file_search_result_delta.py new file mode 100644 index 000000000..69aa93f53 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_result_delta.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_search_result import FileSearchResult +from .file_search_result_delta_type import FileSearchResultDeltaType + + +class FileSearchResultDelta(UniversalBaseModel): + result: typing.List[FileSearchResult] + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FileSearchResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_search_result_delta_type.py b/google/genai/_fern_interactions/types/file_search_result_delta_type.py new file mode 100644 index 000000000..caf17a50c --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchResultDeltaType = typing.Union[typing.Literal["file_search_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_search_result_step.py b/google/genai/_fern_interactions/types/file_search_result_step.py new file mode 100644 index 000000000..daee4ea6b --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_result_step.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .file_search_result_step_type import FileSearchResultStepType + + +class FileSearchResultStep(UniversalBaseModel): + """ + File Search result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FileSearchResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/file_search_result_step_type.py b/google/genai/_fern_interactions/types/file_search_result_step_type.py new file mode 100644 index 000000000..a4b27eb17 --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchResultStepType = typing.Union[typing.Literal["file_search_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/file_search_type.py b/google/genai/_fern_interactions/types/file_search_type.py new file mode 100644 index 000000000..bf115dc9e --- /dev/null +++ b/google/genai/_fern_interactions/types/file_search_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FileSearchType = typing.Union[typing.Literal["file_search"], typing.Any] diff --git a/google/genai/_fern_interactions/types/function.py b/google/genai/_fern_interactions/types/function.py new file mode 100644 index 000000000..4a9c40de2 --- /dev/null +++ b/google/genai/_fern_interactions/types/function.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .function_type import FunctionType + + +class Function(UniversalBaseModel): + """ + A tool that can be used by the model. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + A description of the function. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the function. + """ + + parameters: typing.Optional[typing.Any] = pydantic.Field(default=None) + """ + The JSON Schema for the function's parameters. + """ + + type: FunctionType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/function_call_delta.py b/google/genai/_fern_interactions/types/function_call_delta.py new file mode 100644 index 000000000..d63b41c37 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_call_delta.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .function_call_delta_type import FunctionCallDeltaType + + +class FunctionCallDelta(UniversalBaseModel): + arguments: typing.Dict[str, typing.Any] + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + name: str + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FunctionCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/function_call_delta_type.py b/google/genai/_fern_interactions/types/function_call_delta_type.py new file mode 100644 index 000000000..9d34e26e0 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FunctionCallDeltaType = typing.Union[typing.Literal["function_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/function_call_step.py b/google/genai/_fern_interactions/types/function_call_step.py new file mode 100644 index 000000000..6f89adb84 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_call_step.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .function_call_step_type import FunctionCallStepType + + +class FunctionCallStep(UniversalBaseModel): + """ + A function tool call step. + """ + + arguments: typing.Dict[str, typing.Any] = pydantic.Field() + """ + Required. The arguments to pass to the function. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + name: str = pydantic.Field() + """ + Required. The name of the tool to call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FunctionCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/function_call_step_type.py b/google/genai/_fern_interactions/types/function_call_step_type.py new file mode 100644 index 000000000..8e9938095 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FunctionCallStepType = typing.Union[typing.Literal["function_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/function_result_delta.py b/google/genai/_fern_interactions/types/function_result_delta.py new file mode 100644 index 000000000..be8b24acd --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_delta.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .function_result_delta_result import FunctionResultDeltaResult +from .function_result_delta_type import FunctionResultDeltaType + + +class FunctionResultDelta(UniversalBaseModel): + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + is_error: typing.Optional[bool] = None + name: typing.Optional[str] = None + result: FunctionResultDeltaResult + type: FunctionResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/function_result_delta_result.py b/google/genai/_fern_interactions/types/function_result_delta_result.py new file mode 100644 index 000000000..40bf70062 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_delta_result.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .function_result_subcontent import FunctionResultSubcontent + +FunctionResultDeltaResult = typing.Union[typing.Dict[str, typing.Any], typing.List[FunctionResultSubcontent], str] diff --git a/google/genai/_fern_interactions/types/function_result_delta_type.py b/google/genai/_fern_interactions/types/function_result_delta_type.py new file mode 100644 index 000000000..802b3a991 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FunctionResultDeltaType = typing.Union[typing.Literal["function_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/function_result_step.py b/google/genai/_fern_interactions/types/function_result_step.py new file mode 100644 index 000000000..9d9e70519 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_step.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .function_result_step_result import FunctionResultStepResult +from .function_result_step_type import FunctionResultStepType + + +class FunctionResultStep(UniversalBaseModel): + """ + Result of a function tool call. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + is_error: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the tool call resulted in an error. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tool that was called. + """ + + result: FunctionResultStepResult = pydantic.Field() + """ + The result of the tool call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: FunctionResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/function_result_step_result.py b/google/genai/_fern_interactions/types/function_result_step_result.py new file mode 100644 index 000000000..0631b0cfc --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_step_result.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .function_result_subcontent import FunctionResultSubcontent + +FunctionResultStepResult = typing.Union[typing.Dict[str, typing.Any], typing.List[FunctionResultSubcontent], str] diff --git a/google/genai/_fern_interactions/types/function_result_step_type.py b/google/genai/_fern_interactions/types/function_result_step_type.py new file mode 100644 index 000000000..24d32f9c1 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FunctionResultStepType = typing.Union[typing.Literal["function_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/function_result_subcontent.py b/google/genai/_fern_interactions/types/function_result_subcontent.py new file mode 100644 index 000000000..ff1c324a3 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_result_subcontent.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .image_content import ImageContent +from .text_content import TextContent + +FunctionResultSubcontent = typing.Union[TextContent, ImageContent] diff --git a/google/genai/_fern_interactions/types/function_type.py b/google/genai/_fern_interactions/types/function_type.py new file mode 100644 index 000000000..47b9f2a85 --- /dev/null +++ b/google/genai/_fern_interactions/types/function_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +FunctionType = typing.Union[typing.Literal["function"], typing.Any] diff --git a/google/genai/_fern_interactions/types/generation_config.py b/google/genai/_fern_interactions/types/generation_config.py new file mode 100644 index 000000000..65c518955 --- /dev/null +++ b/google/genai/_fern_interactions/types/generation_config.py @@ -0,0 +1,76 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .generation_config_tool_choice import GenerationConfigToolChoice +from .image_config import ImageConfig +from .speech_config import SpeechConfig +from .thinking_level import ThinkingLevel +from .thinking_summaries import ThinkingSummaries + + +class GenerationConfig(UniversalBaseModel): + """ + Configuration parameters for model interactions. + """ + + image_config: typing.Optional[ImageConfig] = pydantic.Field(default=None) + """ + Configuration for image interaction. + """ + + max_output_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + The maximum number of tokens to include in the response. + """ + + seed: typing.Optional[int] = pydantic.Field(default=None) + """ + Seed used in decoding for reproducibility. + """ + + speech_config: typing.Optional[typing.List[SpeechConfig]] = pydantic.Field(default=None) + """ + Configuration for speech interaction. + """ + + stop_sequences: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of character sequences that will stop output interaction. + """ + + temperature: typing.Optional[float] = pydantic.Field(default=None) + """ + Controls the randomness of the output. + """ + + thinking_level: typing.Optional[ThinkingLevel] = pydantic.Field(default=None) + """ + The level of thought tokens that the model should generate. + """ + + thinking_summaries: typing.Optional[ThinkingSummaries] = pydantic.Field(default=None) + """ + Whether to include thought summaries in the response. + """ + + tool_choice: typing.Optional[GenerationConfigToolChoice] = pydantic.Field(default=None) + """ + The tool choice configuration. + """ + + top_p: typing.Optional[float] = pydantic.Field(default=None) + """ + The maximum cumulative probability of tokens to consider when sampling. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/generation_config_tool_choice.py b/google/genai/_fern_interactions/types/generation_config_tool_choice.py new file mode 100644 index 000000000..d649f92b5 --- /dev/null +++ b/google/genai/_fern_interactions/types/generation_config_tool_choice.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .tool_choice_config import ToolChoiceConfig +from .tool_choice_type import ToolChoiceType + +GenerationConfigToolChoice = typing.Union[ToolChoiceType, ToolChoiceConfig] diff --git a/google/genai/_fern_interactions/types/google_maps.py b/google/genai/_fern_interactions/types/google_maps.py new file mode 100644 index 000000000..de3542e35 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_type import GoogleMapsType + + +class GoogleMaps(UniversalBaseModel): + """ + A tool that can be used by the model to call Google Maps. + """ + + enable_widget: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether to return a widget context token in the tool call result of the + response. + """ + + latitude: typing.Optional[float] = pydantic.Field(default=None) + """ + The latitude of the user's location. + """ + + longitude: typing.Optional[float] = pydantic.Field(default=None) + """ + The longitude of the user's location. + """ + + type: GoogleMapsType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_call_arguments.py b/google/genai/_fern_interactions/types/google_maps_call_arguments.py new file mode 100644 index 000000000..81bd5148f --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleMapsCallArguments(UniversalBaseModel): + """ + The arguments to pass to the Google Maps tool. + """ + + queries: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The queries to be executed. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_call_delta.py b/google/genai/_fern_interactions/types/google_maps_call_delta.py new file mode 100644 index 000000000..5ae5f6bec --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_delta.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_call_arguments import GoogleMapsCallArguments +from .google_maps_call_delta_type import GoogleMapsCallDeltaType + + +class GoogleMapsCallDelta(UniversalBaseModel): + arguments: typing.Optional[GoogleMapsCallArguments] = pydantic.Field(default=None) + """ + The arguments to pass to the Google Maps tool. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleMapsCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_call_delta_type.py b/google/genai/_fern_interactions/types/google_maps_call_delta_type.py new file mode 100644 index 000000000..7c7d4de2f --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleMapsCallDeltaType = typing.Union[typing.Literal["google_maps_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_maps_call_step.py b/google/genai/_fern_interactions/types/google_maps_call_step.py new file mode 100644 index 000000000..c9c48d011 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_step.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_call_step_arguments import GoogleMapsCallStepArguments +from .google_maps_call_step_type import GoogleMapsCallStepType + + +class GoogleMapsCallStep(UniversalBaseModel): + """ + Google Maps call step. + """ + + arguments: typing.Optional[GoogleMapsCallStepArguments] = pydantic.Field(default=None) + """ + The arguments to pass to the Google Maps tool. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleMapsCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_call_step_arguments.py b/google/genai/_fern_interactions/types/google_maps_call_step_arguments.py new file mode 100644 index 000000000..3b93e4f59 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_step_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleMapsCallStepArguments(UniversalBaseModel): + """ + The arguments to pass to the Google Maps tool. + """ + + queries: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The queries to be executed. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_call_step_type.py b/google/genai/_fern_interactions/types/google_maps_call_step_type.py new file mode 100644 index 000000000..98a95b8d3 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleMapsCallStepType = typing.Union[typing.Literal["google_maps_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_maps_result.py b/google/genai/_fern_interactions/types/google_maps_result.py new file mode 100644 index 000000000..3d4752969 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .places import Places + + +class GoogleMapsResult(UniversalBaseModel): + """ + The result of the Google Maps. + """ + + places: typing.Optional[typing.List[Places]] = pydantic.Field(default=None) + """ + The places that were found. + """ + + widget_context_token: typing.Optional[str] = pydantic.Field(default=None) + """ + Resource name of the Google Maps widget context token. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_result_delta.py b/google/genai/_fern_interactions/types/google_maps_result_delta.py new file mode 100644 index 000000000..16b26321d --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_delta.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_result import GoogleMapsResult +from .google_maps_result_delta_type import GoogleMapsResultDeltaType + + +class GoogleMapsResultDelta(UniversalBaseModel): + result: typing.Optional[typing.List[GoogleMapsResult]] = pydantic.Field(default=None) + """ + The results of the Google Maps. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleMapsResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_result_delta_type.py b/google/genai/_fern_interactions/types/google_maps_result_delta_type.py new file mode 100644 index 000000000..3d73b1977 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleMapsResultDeltaType = typing.Union[typing.Literal["google_maps_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_maps_result_item.py b/google/genai/_fern_interactions/types/google_maps_result_item.py new file mode 100644 index 000000000..293e2e4b0 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_item.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_result_places import GoogleMapsResultPlaces + + +class GoogleMapsResultItem(UniversalBaseModel): + """ + The result of the Google Maps. + """ + + places: typing.Optional[typing.List[GoogleMapsResultPlaces]] = None + widget_context_token: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_result_places.py b/google/genai/_fern_interactions/types/google_maps_result_places.py new file mode 100644 index 000000000..d17709e69 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_places.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .review_snippet import ReviewSnippet + + +class GoogleMapsResultPlaces(UniversalBaseModel): + name: typing.Optional[str] = None + place_id: typing.Optional[str] = None + review_snippets: typing.Optional[typing.List[ReviewSnippet]] = None + url: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_result_step.py b/google/genai/_fern_interactions/types/google_maps_result_step.py new file mode 100644 index 000000000..c37bfb924 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_step.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_maps_result_item import GoogleMapsResultItem +from .google_maps_result_step_type import GoogleMapsResultStepType + + +class GoogleMapsResultStep(UniversalBaseModel): + """ + Google Maps result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + result: typing.List[GoogleMapsResultItem] + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleMapsResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_maps_result_step_type.py b/google/genai/_fern_interactions/types/google_maps_result_step_type.py new file mode 100644 index 000000000..92c47751a --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleMapsResultStepType = typing.Union[typing.Literal["google_maps_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_maps_type.py b/google/genai/_fern_interactions/types/google_maps_type.py new file mode 100644 index 000000000..74fe12a14 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_maps_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleMapsType = typing.Union[typing.Literal["google_maps"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_search.py b/google/genai/_fern_interactions/types/google_search.py new file mode 100644 index 000000000..91c79d378 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_search_search_types_item import GoogleSearchSearchTypesItem +from .google_search_type import GoogleSearchType + + +class GoogleSearch(UniversalBaseModel): + """ + A tool that can be used by the model to search Google. + """ + + search_types: typing.Optional[typing.List[GoogleSearchSearchTypesItem]] = pydantic.Field(default=None) + """ + The types of search grounding to enable. + """ + + type: GoogleSearchType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_call_arguments.py b/google/genai/_fern_interactions/types/google_search_call_arguments.py new file mode 100644 index 000000000..2186003fb --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleSearchCallArguments(UniversalBaseModel): + """ + The arguments to pass to Google Search. + """ + + queries: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Web search queries for the following-up web search. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_call_delta.py b/google/genai/_fern_interactions/types/google_search_call_delta.py new file mode 100644 index 000000000..ffa235e86 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_delta.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_search_call_arguments import GoogleSearchCallArguments +from .google_search_call_delta_type import GoogleSearchCallDeltaType + + +class GoogleSearchCallDelta(UniversalBaseModel): + arguments: GoogleSearchCallArguments + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleSearchCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_call_delta_type.py b/google/genai/_fern_interactions/types/google_search_call_delta_type.py new file mode 100644 index 000000000..2cde45e31 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchCallDeltaType = typing.Union[typing.Literal["google_search_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_search_call_step.py b/google/genai/_fern_interactions/types/google_search_call_step.py new file mode 100644 index 000000000..c85f3be6c --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_step.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_search_call_step_arguments import GoogleSearchCallStepArguments +from .google_search_call_step_search_type import GoogleSearchCallStepSearchType +from .google_search_call_step_type import GoogleSearchCallStepType + + +class GoogleSearchCallStep(UniversalBaseModel): + """ + Google Search call step. + """ + + arguments: GoogleSearchCallStepArguments = pydantic.Field() + """ + Required. The arguments to pass to Google Search. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + search_type: typing.Optional[GoogleSearchCallStepSearchType] = pydantic.Field(default=None) + """ + The type of search grounding enabled. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleSearchCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_call_step_arguments.py b/google/genai/_fern_interactions/types/google_search_call_step_arguments.py new file mode 100644 index 000000000..459266cdf --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_step_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleSearchCallStepArguments(UniversalBaseModel): + """ + The arguments to pass to Google Search. + """ + + queries: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Web search queries for the following-up web search. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_call_step_search_type.py b/google/genai/_fern_interactions/types/google_search_call_step_search_type.py new file mode 100644 index 000000000..35a95f241 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_step_search_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchCallStepSearchType = typing.Union[ + typing.Literal["web_search", "image_search", "enterprise_web_search"], typing.Any +] diff --git a/google/genai/_fern_interactions/types/google_search_call_step_type.py b/google/genai/_fern_interactions/types/google_search_call_step_type.py new file mode 100644 index 000000000..d0c7765fb --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchCallStepType = typing.Union[typing.Literal["google_search_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_search_result.py b/google/genai/_fern_interactions/types/google_search_result.py new file mode 100644 index 000000000..c10ed11d5 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleSearchResult(UniversalBaseModel): + """ + The result of the Google Search. + """ + + search_suggestions: typing.Optional[str] = pydantic.Field(default=None) + """ + Web content snippet that can be embedded in a web page or an app webview. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_result_delta.py b/google/genai/_fern_interactions/types/google_search_result_delta.py new file mode 100644 index 000000000..671f2f9b6 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result_delta.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_search_result import GoogleSearchResult +from .google_search_result_delta_type import GoogleSearchResultDeltaType + + +class GoogleSearchResultDelta(UniversalBaseModel): + is_error: typing.Optional[bool] = None + result: typing.List[GoogleSearchResult] + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleSearchResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_result_delta_type.py b/google/genai/_fern_interactions/types/google_search_result_delta_type.py new file mode 100644 index 000000000..1d5e99c24 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchResultDeltaType = typing.Union[typing.Literal["google_search_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_search_result_item.py b/google/genai/_fern_interactions/types/google_search_result_item.py new file mode 100644 index 000000000..e0adcc4c4 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result_item.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class GoogleSearchResultItem(UniversalBaseModel): + """ + The result of the Google Search. + """ + + search_suggestions: typing.Optional[str] = pydantic.Field(default=None) + """ + Web content snippet that can be embedded in a web page or an app webview. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_result_step.py b/google/genai/_fern_interactions/types/google_search_result_step.py new file mode 100644 index 000000000..3b667f1c3 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result_step.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .google_search_result_item import GoogleSearchResultItem +from .google_search_result_step_type import GoogleSearchResultStepType + + +class GoogleSearchResultStep(UniversalBaseModel): + """ + Google Search result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + is_error: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Google Search resulted in an error. + """ + + result: typing.List[GoogleSearchResultItem] = pydantic.Field() + """ + Required. The results of the Google Search. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: GoogleSearchResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/google_search_result_step_type.py b/google/genai/_fern_interactions/types/google_search_result_step_type.py new file mode 100644 index 000000000..31d4d8ce0 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchResultStepType = typing.Union[typing.Literal["google_search_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/google_search_search_types_item.py b/google/genai/_fern_interactions/types/google_search_search_types_item.py new file mode 100644 index 000000000..78f2606f0 --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_search_types_item.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchSearchTypesItem = typing.Union[ + typing.Literal["web_search", "image_search", "enterprise_web_search"], typing.Any +] diff --git a/google/genai/_fern_interactions/types/google_search_type.py b/google/genai/_fern_interactions/types/google_search_type.py new file mode 100644 index 000000000..052e0380a --- /dev/null +++ b/google/genai/_fern_interactions/types/google_search_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GoogleSearchType = typing.Union[typing.Literal["google_search"], typing.Any] diff --git a/google/genai/_fern_interactions/types/grounding_tool_count.py b/google/genai/_fern_interactions/types/grounding_tool_count.py new file mode 100644 index 000000000..878528b3d --- /dev/null +++ b/google/genai/_fern_interactions/types/grounding_tool_count.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .grounding_tool_count_type import GroundingToolCountType + + +class GroundingToolCount(UniversalBaseModel): + """ + The number of grounding tool counts. + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of grounding tool counts. + """ + + type: typing.Optional[GroundingToolCountType] = pydantic.Field(default=None) + """ + The grounding tool type associated with the count. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/grounding_tool_count_type.py b/google/genai/_fern_interactions/types/grounding_tool_count_type.py new file mode 100644 index 000000000..7b9362884 --- /dev/null +++ b/google/genai/_fern_interactions/types/grounding_tool_count_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +GroundingToolCountType = typing.Union[typing.Literal["google_search", "google_maps", "retrieval"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_config.py b/google/genai/_fern_interactions/types/image_config.py new file mode 100644 index 000000000..bbe4fa7be --- /dev/null +++ b/google/genai/_fern_interactions/types/image_config.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .image_config_aspect_ratio import ImageConfigAspectRatio +from .image_config_image_size import ImageConfigImageSize + + +class ImageConfig(UniversalBaseModel): + """ + The configuration for image interaction. + """ + + aspect_ratio: typing.Optional[ImageConfigAspectRatio] = None + image_size: typing.Optional[ImageConfigImageSize] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/image_config_aspect_ratio.py b/google/genai/_fern_interactions/types/image_config_aspect_ratio.py new file mode 100644 index 000000000..3b039163d --- /dev/null +++ b/google/genai/_fern_interactions/types/image_config_aspect_ratio.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageConfigAspectRatio = typing.Union[ + typing.Literal["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9", "1:8", "8:1", "1:4", "4:1"], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/image_config_image_size.py b/google/genai/_fern_interactions/types/image_config_image_size.py new file mode 100644 index 000000000..ee0da111a --- /dev/null +++ b/google/genai/_fern_interactions/types/image_config_image_size.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageConfigImageSize = typing.Union[typing.Literal["1K", "2K", "4K", "512"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_content.py b/google/genai/_fern_interactions/types/image_content.py new file mode 100644 index 000000000..f4fcd411c --- /dev/null +++ b/google/genai/_fern_interactions/types/image_content.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .image_content_mime_type import ImageContentMimeType +from .image_content_type import ImageContentType +from .media_resolution import MediaResolution + + +class ImageContent(UniversalBaseModel): + """ + An image content block. + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The image content. + """ + + mime_type: typing.Optional[ImageContentMimeType] = pydantic.Field(default=None) + """ + The mime type of the image. + """ + + resolution: typing.Optional[MediaResolution] = pydantic.Field(default=None) + """ + The resolution of the media. + """ + + type: ImageContentType + uri: typing.Optional[str] = pydantic.Field(default=None) + """ + The URI of the image. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/image_content_mime_type.py b/google/genai/_fern_interactions/types/image_content_mime_type.py new file mode 100644 index 000000000..795de81c8 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_content_mime_type.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageContentMimeType = typing.Union[ + typing.Literal[ + "image/png", "image/jpeg", "image/webp", "image/heic", "image/heif", "image/gif", "image/bmp", "image/tiff" + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/image_content_type.py b/google/genai/_fern_interactions/types/image_content_type.py new file mode 100644 index 000000000..1b29abcd1 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageContentType = typing.Union[typing.Literal["image"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_delta.py b/google/genai/_fern_interactions/types/image_delta.py new file mode 100644 index 000000000..5c91c6dbc --- /dev/null +++ b/google/genai/_fern_interactions/types/image_delta.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .image_delta_mime_type import ImageDeltaMimeType +from .image_delta_type import ImageDeltaType +from .media_resolution import MediaResolution + + +class ImageDelta(UniversalBaseModel): + data: typing.Optional[str] = None + mime_type: typing.Optional[ImageDeltaMimeType] = None + resolution: typing.Optional[MediaResolution] = pydantic.Field(default=None) + """ + The resolution of the media. + """ + + type: ImageDeltaType + uri: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/image_delta_mime_type.py b/google/genai/_fern_interactions/types/image_delta_mime_type.py new file mode 100644 index 000000000..5dc4da1b8 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_delta_mime_type.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageDeltaMimeType = typing.Union[ + typing.Literal[ + "image/png", "image/jpeg", "image/webp", "image/heic", "image/heif", "image/gif", "image/bmp", "image/tiff" + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/image_delta_type.py b/google/genai/_fern_interactions/types/image_delta_type.py new file mode 100644 index 000000000..646ae4f45 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageDeltaType = typing.Union[typing.Literal["image"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_response_format.py b/google/genai/_fern_interactions/types/image_response_format.py new file mode 100644 index 000000000..462c39cde --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .image_response_format_aspect_ratio import ImageResponseFormatAspectRatio +from .image_response_format_delivery import ImageResponseFormatDelivery +from .image_response_format_image_size import ImageResponseFormatImageSize +from .image_response_format_mime_type import ImageResponseFormatMimeType +from .image_response_format_type import ImageResponseFormatType + + +class ImageResponseFormat(UniversalBaseModel): + """ + Configuration for image output format. + """ + + aspect_ratio: typing.Optional[ImageResponseFormatAspectRatio] = pydantic.Field(default=None) + """ + The aspect ratio for the image output. + """ + + delivery: typing.Optional[ImageResponseFormatDelivery] = pydantic.Field(default=None) + """ + The delivery mode for the image output. + """ + + image_size: typing.Optional[ImageResponseFormatImageSize] = pydantic.Field(default=None) + """ + The size of the image output. + """ + + mime_type: typing.Optional[ImageResponseFormatMimeType] = pydantic.Field(default=None) + """ + The MIME type of the image output. + """ + + type: ImageResponseFormatType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/image_response_format_aspect_ratio.py b/google/genai/_fern_interactions/types/image_response_format_aspect_ratio.py new file mode 100644 index 000000000..2c398a0f6 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format_aspect_ratio.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageResponseFormatAspectRatio = typing.Union[ + typing.Literal["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9", "1:8", "8:1", "1:4", "4:1"], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/image_response_format_delivery.py b/google/genai/_fern_interactions/types/image_response_format_delivery.py new file mode 100644 index 000000000..b70d84135 --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format_delivery.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageResponseFormatDelivery = typing.Union[typing.Literal["inline", "uri"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_response_format_image_size.py b/google/genai/_fern_interactions/types/image_response_format_image_size.py new file mode 100644 index 000000000..16fb5905a --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format_image_size.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageResponseFormatImageSize = typing.Union[typing.Literal["512", "1K", "2K", "4K"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_response_format_mime_type.py b/google/genai/_fern_interactions/types/image_response_format_mime_type.py new file mode 100644 index 000000000..f1914d89e --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format_mime_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageResponseFormatMimeType = typing.Union[typing.Literal["image/jpeg"], typing.Any] diff --git a/google/genai/_fern_interactions/types/image_response_format_type.py b/google/genai/_fern_interactions/types/image_response_format_type.py new file mode 100644 index 000000000..ebf96a88c --- /dev/null +++ b/google/genai/_fern_interactions/types/image_response_format_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ImageResponseFormatType = typing.Union[typing.Literal["image"], typing.Any] diff --git a/google/genai/_fern_interactions/types/interaction.py b/google/genai/_fern_interactions/types/interaction.py new file mode 100644 index 000000000..45cdf4e46 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction.py @@ -0,0 +1,146 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .agent_option import AgentOption +from .generation_config import GenerationConfig +from .interaction_agent_config import InteractionAgentConfig +from .interaction_environment import InteractionEnvironment +from .interaction_response_format import InteractionResponseFormat +from .interaction_status import InteractionStatus +from .interactions_input import InteractionsInput +from .model_option import ModelOption +from .response_modality import ResponseModality +from .service_tier import ServiceTier +from .step import Step +from .tool import Tool +from .usage import Usage +from .webhook_config import WebhookConfig + + +class Interaction(UniversalBaseModel): + """ + The Interaction resource. + """ + + model: typing.Optional[ModelOption] = pydantic.Field(default=None) + """ + The name of the `Model` used for generating the interaction. + """ + + agent: typing.Optional[AgentOption] = pydantic.Field(default=None) + """ + The name of the `Agent` used for generating the interaction. + """ + + created: dt.datetime = pydantic.Field() + """ + Required. Output only. The time at which the response was created in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + + environment: typing.Optional[InteractionEnvironment] = pydantic.Field(default=None) + """ + The environment configuration for the interaction. Can be an object specifying remote environment sources or a string referencing an existing environment ID. + """ + + environment_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The environment ID for the interaction. Only populated if environment + config is set in the request. + """ + + id: str = pydantic.Field() + """ + Required. Output only. A unique identifier for the interaction completion. + """ + + input: typing.Optional[InteractionsInput] = None + previous_interaction_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the previous interaction, if any. + """ + + response_format: typing.Optional[InteractionResponseFormat] = pydantic.Field(default=None) + """ + Enforces that the generated response is a JSON object that complies with the JSON schema specified in this field. + """ + + response_mime_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The mime type of the response. This is required if response_format is set. + """ + + response_modalities: typing.Optional[typing.List[ResponseModality]] = pydantic.Field(default=None) + """ + The requested modalities of the response (TEXT, IMAGE, AUDIO). + """ + + role: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The role of the interaction. + """ + + service_tier: typing.Optional[ServiceTier] = pydantic.Field(default=None) + """ + The service tier for the interaction. + """ + + status: InteractionStatus = pydantic.Field() + """ + Required. Output only. The status of the interaction. + """ + + steps: typing.Optional[typing.List[Step]] = pydantic.Field(default=None) + """ + Required. Output only. The steps that make up the interaction. + """ + + system_instruction: typing.Optional[str] = pydantic.Field(default=None) + """ + System instruction for the interaction. + """ + + tools: typing.Optional[typing.List[Tool]] = pydantic.Field(default=None) + """ + A list of tool declarations the model may call during interaction. + """ + + updated: dt.datetime = pydantic.Field() + """ + Required. Output only. The time at which the response was last updated in ISO 8601 format + (YYYY-MM-DDThh:mm:ssZ). + """ + + usage: typing.Optional[Usage] = pydantic.Field(default=None) + """ + Output only. Statistics on the interaction request's token usage. + """ + + webhook_config: typing.Optional[WebhookConfig] = pydantic.Field(default=None) + """ + Optional. Webhook configuration for receiving notifications when the + interaction completes. + """ + + generation_config: typing.Optional[GenerationConfig] = pydantic.Field(default=None) + """ + Input only. Configuration parameters for the model interaction. + """ + + agent_config: typing.Optional[InteractionAgentConfig] = pydantic.Field(default=None) + """ + Configuration parameters for the agent interaction. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/interaction_agent_config.py b/google/genai/_fern_interactions/types/interaction_agent_config.py new file mode 100644 index 000000000..d152fd215 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_agent_config.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .deep_research_agent_config import DeepResearchAgentConfig +from .dynamic_agent_config import DynamicAgentConfig + +InteractionAgentConfig = typing.Union[DynamicAgentConfig, DeepResearchAgentConfig] diff --git a/google/genai/_fern_interactions/types/interaction_completed_event.py b/google/genai/_fern_interactions/types/interaction_completed_event.py new file mode 100644 index 000000000..afefcb829 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_completed_event.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .interaction import Interaction +from .interaction_completed_event_event_type import InteractionCompletedEventEventType + + +class InteractionCompletedEvent(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: InteractionCompletedEventEventType + interaction: Interaction = pydantic.Field() + """ + Required. The completed interaction with empty outputs to reduce the payload size. + Use the preceding ContentDelta events for the actual output. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/interaction_completed_event_event_type.py b/google/genai/_fern_interactions/types/interaction_completed_event_event_type.py new file mode 100644 index 000000000..164c25191 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_completed_event_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +InteractionCompletedEventEventType = typing.Union[typing.Literal["interaction.completed"], typing.Any] diff --git a/google/genai/_fern_interactions/types/interaction_created_event.py b/google/genai/_fern_interactions/types/interaction_created_event.py new file mode 100644 index 000000000..cc9dd505c --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_created_event.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .interaction import Interaction +from .interaction_created_event_event_type import InteractionCreatedEventEventType + + +class InteractionCreatedEvent(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: InteractionCreatedEventEventType + interaction: Interaction + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/interaction_created_event_event_type.py b/google/genai/_fern_interactions/types/interaction_created_event_event_type.py new file mode 100644 index 000000000..5c1dc88c4 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_created_event_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +InteractionCreatedEventEventType = typing.Union[typing.Literal["interaction.created"], typing.Any] diff --git a/google/genai/_fern_interactions/types/interaction_environment.py b/google/genai/_fern_interactions/types/interaction_environment.py new file mode 100644 index 000000000..5d0c59ead --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_environment.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .environment_config import EnvironmentConfig + +InteractionEnvironment = typing.Union[str, EnvironmentConfig] diff --git a/google/genai/_fern_interactions/types/interaction_response_format.py b/google/genai/_fern_interactions/types/interaction_response_format.py new file mode 100644 index 000000000..0ed1d7c81 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_response_format.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .response_format import ResponseFormat +from .response_format_list import ResponseFormatList + +InteractionResponseFormat = typing.Union[ResponseFormatList, ResponseFormat] diff --git a/google/genai/_fern_interactions/types/interaction_sse_event.py b/google/genai/_fern_interactions/types/interaction_sse_event.py new file mode 100644 index 000000000..98c662c2c --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_sse_event.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .error_event import ErrorEvent +from .interaction_completed_event import InteractionCompletedEvent +from .interaction_created_event import InteractionCreatedEvent +from .interaction_status_update import InteractionStatusUpdate +from .step_delta import StepDelta +from .step_start import StepStart +from .step_stop import StepStop + +InteractionSseEvent = typing.Union[ + InteractionCreatedEvent, + InteractionCompletedEvent, + InteractionStatusUpdate, + ErrorEvent, + StepStart, + StepDelta, + StepStop, +] diff --git a/google/genai/_fern_interactions/types/interaction_status.py b/google/genai/_fern_interactions/types/interaction_status.py new file mode 100644 index 000000000..40e263a74 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +InteractionStatus = typing.Union[ + typing.Literal["in_progress", "requires_action", "completed", "failed", "cancelled", "incomplete"], typing.Any +] diff --git a/google/genai/_fern_interactions/types/interaction_status_update.py b/google/genai/_fern_interactions/types/interaction_status_update.py new file mode 100644 index 000000000..609de7ef1 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_status_update.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .interaction_status_update_event_type import InteractionStatusUpdateEventType +from .interaction_status_update_status import InteractionStatusUpdateStatus + + +class InteractionStatusUpdate(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: InteractionStatusUpdateEventType + interaction_id: str + status: InteractionStatusUpdateStatus + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/interaction_status_update_event_type.py b/google/genai/_fern_interactions/types/interaction_status_update_event_type.py new file mode 100644 index 000000000..fff61abc9 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_status_update_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +InteractionStatusUpdateEventType = typing.Union[typing.Literal["interaction.status_update"], typing.Any] diff --git a/google/genai/_fern_interactions/types/interaction_status_update_status.py b/google/genai/_fern_interactions/types/interaction_status_update_status.py new file mode 100644 index 000000000..a10a987e9 --- /dev/null +++ b/google/genai/_fern_interactions/types/interaction_status_update_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +InteractionStatusUpdateStatus = typing.Union[ + typing.Literal["in_progress", "requires_action", "completed", "failed", "cancelled", "incomplete"], typing.Any +] diff --git a/google/genai/_fern_interactions/types/interactions_input.py b/google/genai/_fern_interactions/types/interactions_input.py new file mode 100644 index 000000000..0538db485 --- /dev/null +++ b/google/genai/_fern_interactions/types/interactions_input.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .content import Content +from .step import Step + +InteractionsInput = typing.Union[str, typing.List[Step], typing.List[Content], Content] diff --git a/google/genai/_fern_interactions/types/list_agents_response.py b/google/genai/_fern_interactions/types/list_agents_response.py new file mode 100644 index 000000000..8688e7df8 --- /dev/null +++ b/google/genai/_fern_interactions/types/list_agents_response.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .agent import Agent + + +class ListAgentsResponse(UniversalBaseModel): + agents: typing.Optional[typing.List[Agent]] = None + next_page_token: typing_extensions.Annotated[ + typing.Optional[str], FieldMetadata(alias="nextPageToken"), pydantic.Field(alias="nextPageToken") + ] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/list_webhooks_response.py b/google/genai/_fern_interactions/types/list_webhooks_response.py new file mode 100644 index 000000000..53c94582a --- /dev/null +++ b/google/genai/_fern_interactions/types/list_webhooks_response.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .webhook_read import WebhookRead + + +class ListWebhooksResponse(UniversalBaseModel): + """ + Response message for WebhookService.ListWebhooks. + """ + + next_page_token: typing.Optional[str] = pydantic.Field(default=None) + """ + A token, which can be sent as `page_token` to retrieve the next page. + If this field is omitted, there are no subsequent pages. + """ + + webhooks: typing.Optional[typing.List[WebhookRead]] = pydantic.Field(default=None) + """ + The webhooks. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server.py b/google/genai/_fern_interactions/types/mcp_server.py new file mode 100644 index 000000000..6a2c0ceb3 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .allowed_tools import AllowedTools +from .mcp_server_type import McpServerType + + +class McpServer(UniversalBaseModel): + """ + A MCPServer is a server that can be called by the model to perform actions. + """ + + allowed_tools: typing.Optional[typing.List[AllowedTools]] = pydantic.Field(default=None) + """ + The allowed tools. + """ + + headers: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional: Fields for authentication headers, timeouts, etc., if needed. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the MCPServer. + """ + + type: McpServerType + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The full URL for the MCPServer endpoint. + Example: "https://api.example.com/mcp" + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_call_delta.py b/google/genai/_fern_interactions/types/mcp_server_tool_call_delta.py new file mode 100644 index 000000000..b838d3ea4 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_call_delta.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .mcp_server_tool_call_delta_type import McpServerToolCallDeltaType + + +class McpServerToolCallDelta(UniversalBaseModel): + arguments: typing.Dict[str, typing.Any] + name: str + server_name: str + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: McpServerToolCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_call_delta_type.py b/google/genai/_fern_interactions/types/mcp_server_tool_call_delta_type.py new file mode 100644 index 000000000..ca979f9b1 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +McpServerToolCallDeltaType = typing.Union[typing.Literal["mcp_server_tool_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_call_step.py b/google/genai/_fern_interactions/types/mcp_server_tool_call_step.py new file mode 100644 index 000000000..fd9f944f2 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_call_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .mcp_server_tool_call_step_type import McpServerToolCallStepType + + +class McpServerToolCallStep(UniversalBaseModel): + """ + MCPServer tool call step. + """ + + arguments: typing.Dict[str, typing.Any] = pydantic.Field() + """ + Required. The JSON object of arguments for the function. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + name: str = pydantic.Field() + """ + Required. The name of the tool which was called. + """ + + server_name: str = pydantic.Field() + """ + Required. The name of the used MCP server. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: McpServerToolCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_call_step_type.py b/google/genai/_fern_interactions/types/mcp_server_tool_call_step_type.py new file mode 100644 index 000000000..6d7c8aa4c --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +McpServerToolCallStepType = typing.Union[typing.Literal["mcp_server_tool_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_delta.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta.py new file mode 100644 index 000000000..5b4bd6e2c --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .mcp_server_tool_result_delta_result import McpServerToolResultDeltaResult +from .mcp_server_tool_result_delta_type import McpServerToolResultDeltaType + + +class McpServerToolResultDelta(UniversalBaseModel): + name: typing.Optional[str] = None + result: McpServerToolResultDeltaResult + server_name: typing.Optional[str] = None + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: McpServerToolResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_result.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_result.py new file mode 100644 index 000000000..f3fcb59a7 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_result.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .function_result_subcontent import FunctionResultSubcontent + +McpServerToolResultDeltaResult = typing.Union[typing.Dict[str, typing.Any], typing.List[FunctionResultSubcontent], str] diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_type.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_type.py new file mode 100644 index 000000000..a57864e77 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +McpServerToolResultDeltaType = typing.Union[typing.Literal["mcp_server_tool_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_step.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_step.py new file mode 100644 index 000000000..a7a38205e --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_step.py @@ -0,0 +1,50 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .mcp_server_tool_result_step_result import McpServerToolResultStepResult +from .mcp_server_tool_result_step_type import McpServerToolResultStepType + + +class McpServerToolResultStep(UniversalBaseModel): + """ + MCPServer tool result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the tool which is called for this specific tool call. + """ + + result: McpServerToolResultStepResult = pydantic.Field() + """ + The output from the MCP server call. Can be simple text or rich content. + """ + + server_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the used MCP server. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: McpServerToolResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_step_result.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_step_result.py new file mode 100644 index 000000000..3015e3b17 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_step_result.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .function_result_subcontent import FunctionResultSubcontent + +McpServerToolResultStepResult = typing.Union[typing.Dict[str, typing.Any], str, typing.List[FunctionResultSubcontent]] diff --git a/google/genai/_fern_interactions/types/mcp_server_tool_result_step_type.py b/google/genai/_fern_interactions/types/mcp_server_tool_result_step_type.py new file mode 100644 index 000000000..1162ff56a --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_tool_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +McpServerToolResultStepType = typing.Union[typing.Literal["mcp_server_tool_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/mcp_server_type.py b/google/genai/_fern_interactions/types/mcp_server_type.py new file mode 100644 index 000000000..fcb30c108 --- /dev/null +++ b/google/genai/_fern_interactions/types/mcp_server_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +McpServerType = typing.Union[typing.Literal["mcp_server"], typing.Any] diff --git a/google/genai/_fern_interactions/types/media_resolution.py b/google/genai/_fern_interactions/types/media_resolution.py new file mode 100644 index 000000000..548f506b1 --- /dev/null +++ b/google/genai/_fern_interactions/types/media_resolution.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MediaResolution = typing.Union[typing.Literal["low", "medium", "high", "ultra_high"], typing.Any] diff --git a/google/genai/_fern_interactions/types/modality_tokens.py b/google/genai/_fern_interactions/types/modality_tokens.py new file mode 100644 index 000000000..a3325c135 --- /dev/null +++ b/google/genai/_fern_interactions/types/modality_tokens.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .response_modality import ResponseModality + + +class ModalityTokens(UniversalBaseModel): + """ + The token count for a single response modality. + """ + + modality: typing.Optional[ResponseModality] = pydantic.Field(default=None) + """ + The modality associated with the token count. + """ + + tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of tokens for the modality. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/model_option.py b/google/genai/_fern_interactions/types/model_option.py new file mode 100644 index 000000000..d59b3498f --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .model_option_eight import ModelOptionEight +from .model_option_eighteen import ModelOptionEighteen +from .model_option_eleven import ModelOptionEleven +from .model_option_fifteen import ModelOptionFifteen +from .model_option_five import ModelOptionFive +from .model_option_four import ModelOptionFour +from .model_option_fourteen import ModelOptionFourteen +from .model_option_nine import ModelOptionNine +from .model_option_nineteen import ModelOptionNineteen +from .model_option_one import ModelOptionOne +from .model_option_seven import ModelOptionSeven +from .model_option_seventeen import ModelOptionSeventeen +from .model_option_six import ModelOptionSix +from .model_option_sixteen import ModelOptionSixteen +from .model_option_ten import ModelOptionTen +from .model_option_thirteen import ModelOptionThirteen +from .model_option_three import ModelOptionThree +from .model_option_twelve import ModelOptionTwelve +from .model_option_twenty import ModelOptionTwenty +from .model_option_two import ModelOptionTwo + +ModelOption = typing.Union[ + str, + ModelOptionOne, + ModelOptionTwo, + ModelOptionThree, + ModelOptionFour, + ModelOptionFive, + ModelOptionSix, + ModelOptionSeven, + ModelOptionEight, + ModelOptionNine, + ModelOptionTen, + ModelOptionEleven, + ModelOptionTwelve, + ModelOptionThirteen, + ModelOptionFourteen, + ModelOptionFifteen, + ModelOptionSixteen, + ModelOptionSeventeen, + ModelOptionEighteen, + ModelOptionNineteen, + ModelOptionTwenty, +] diff --git a/google/genai/_fern_interactions/types/model_option_eight.py b/google/genai/_fern_interactions/types/model_option_eight.py new file mode 100644 index 000000000..bbcbffcc3 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_eight.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionEight = typing.Union[typing.Literal["gemini-2.5-flash-preview-tts"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_eighteen.py b/google/genai/_fern_interactions/types/model_option_eighteen.py new file mode 100644 index 000000000..7ea8859a4 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_eighteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionEighteen = typing.Union[typing.Literal["gemini-3.1-flash-tts-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_eleven.py b/google/genai/_fern_interactions/types/model_option_eleven.py new file mode 100644 index 000000000..3be7f8fdc --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_eleven.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionEleven = typing.Union[typing.Literal["gemini-3-flash-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_fifteen.py b/google/genai/_fern_interactions/types/model_option_fifteen.py new file mode 100644 index 000000000..c95baceec --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_fifteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionFifteen = typing.Union[typing.Literal["gemini-3.1-flash-image-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_five.py b/google/genai/_fern_interactions/types/model_option_five.py new file mode 100644 index 000000000..a3812c92e --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_five.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionFive = typing.Union[typing.Literal["gemini-2.5-flash-lite-preview-09-2025"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_four.py b/google/genai/_fern_interactions/types/model_option_four.py new file mode 100644 index 000000000..6c90b5d51 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_four.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionFour = typing.Union[typing.Literal["gemini-2.5-flash-lite"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_fourteen.py b/google/genai/_fern_interactions/types/model_option_fourteen.py new file mode 100644 index 000000000..a5f5097c3 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_fourteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionFourteen = typing.Union[typing.Literal["gemini-3.1-pro-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_nine.py b/google/genai/_fern_interactions/types/model_option_nine.py new file mode 100644 index 000000000..cd6cb65e3 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_nine.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionNine = typing.Union[typing.Literal["gemini-2.5-pro"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_nineteen.py b/google/genai/_fern_interactions/types/model_option_nineteen.py new file mode 100644 index 000000000..1acf60cdb --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_nineteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionNineteen = typing.Union[typing.Literal["lyria-3-clip-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_one.py b/google/genai/_fern_interactions/types/model_option_one.py new file mode 100644 index 000000000..e517015b2 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionOne = typing.Union[typing.Literal["gemini-2.5-computer-use-preview-10-2025"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_seven.py b/google/genai/_fern_interactions/types/model_option_seven.py new file mode 100644 index 000000000..e8395cae6 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_seven.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionSeven = typing.Union[typing.Literal["gemini-2.5-flash-preview-09-2025"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_seventeen.py b/google/genai/_fern_interactions/types/model_option_seventeen.py new file mode 100644 index 000000000..409378dbd --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_seventeen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionSeventeen = typing.Union[typing.Literal["gemini-3.1-flash-lite-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_six.py b/google/genai/_fern_interactions/types/model_option_six.py new file mode 100644 index 000000000..36d28e5bc --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_six.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionSix = typing.Union[typing.Literal["gemini-2.5-flash-native-audio-preview-12-2025"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_sixteen.py b/google/genai/_fern_interactions/types/model_option_sixteen.py new file mode 100644 index 000000000..5f94636ec --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_sixteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionSixteen = typing.Union[typing.Literal["gemini-3.1-flash-lite"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_ten.py b/google/genai/_fern_interactions/types/model_option_ten.py new file mode 100644 index 000000000..822412162 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_ten.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionTen = typing.Union[typing.Literal["gemini-2.5-pro-preview-tts"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_thirteen.py b/google/genai/_fern_interactions/types/model_option_thirteen.py new file mode 100644 index 000000000..477befd49 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_thirteen.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionThirteen = typing.Union[typing.Literal["gemini-3-pro-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_three.py b/google/genai/_fern_interactions/types/model_option_three.py new file mode 100644 index 000000000..1fa6d6650 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_three.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionThree = typing.Union[typing.Literal["gemini-2.5-flash-image"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_twelve.py b/google/genai/_fern_interactions/types/model_option_twelve.py new file mode 100644 index 000000000..1230f8e41 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_twelve.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionTwelve = typing.Union[typing.Literal["gemini-3-pro-image-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_twenty.py b/google/genai/_fern_interactions/types/model_option_twenty.py new file mode 100644 index 000000000..a4232903e --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_twenty.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionTwenty = typing.Union[typing.Literal["lyria-3-pro-preview"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_option_two.py b/google/genai/_fern_interactions/types/model_option_two.py new file mode 100644 index 000000000..9b835842f --- /dev/null +++ b/google/genai/_fern_interactions/types/model_option_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOptionTwo = typing.Union[typing.Literal["gemini-2.5-flash"], typing.Any] diff --git a/google/genai/_fern_interactions/types/model_output_step.py b/google/genai/_fern_interactions/types/model_output_step.py new file mode 100644 index 000000000..a600661a0 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_output_step.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .content import Content +from .model_output_step_type import ModelOutputStepType + + +class ModelOutputStep(UniversalBaseModel): + """ + Output generated by the model. + """ + + content: typing.Optional[typing.List[Content]] = None + type: ModelOutputStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/model_output_step_type.py b/google/genai/_fern_interactions/types/model_output_step_type.py new file mode 100644 index 000000000..0c6454599 --- /dev/null +++ b/google/genai/_fern_interactions/types/model_output_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ModelOutputStepType = typing.Union[typing.Literal["model_output"], typing.Any] diff --git a/google/genai/_fern_interactions/types/ping_webhook_request.py b/google/genai/_fern_interactions/types/ping_webhook_request.py new file mode 100644 index 000000000..79f1209b8 --- /dev/null +++ b/google/genai/_fern_interactions/types/ping_webhook_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PingWebhookRequest = typing.Dict[str, typing.Any] +""" +Request message for WebhookService.PingWebhook. +""" diff --git a/google/genai/_fern_interactions/types/ping_webhook_response.py b/google/genai/_fern_interactions/types/ping_webhook_response.py new file mode 100644 index 000000000..7ddaabcf4 --- /dev/null +++ b/google/genai/_fern_interactions/types/ping_webhook_response.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PingWebhookResponse = typing.Dict[str, typing.Any] +""" +Response message for WebhookService.PingWebhook. +""" diff --git a/google/genai/_fern_interactions/types/place_citation.py b/google/genai/_fern_interactions/types/place_citation.py new file mode 100644 index 000000000..eb99a204b --- /dev/null +++ b/google/genai/_fern_interactions/types/place_citation.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .place_citation_type import PlaceCitationType +from .review_snippet import ReviewSnippet + + +class PlaceCitation(UniversalBaseModel): + """ + A place citation annotation. + """ + + end_index: typing.Optional[int] = pydantic.Field(default=None) + """ + End of the attributed segment, exclusive. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Title of the place. + """ + + place_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the place, in `places/{place_id}` format. + """ + + review_snippets: typing.Optional[typing.List[ReviewSnippet]] = pydantic.Field(default=None) + """ + Snippets of reviews that are used to generate answers about the + features of a given place in Google Maps. + """ + + start_index: typing.Optional[int] = pydantic.Field(default=None) + """ + Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + type: PlaceCitationType + url: typing.Optional[str] = pydantic.Field(default=None) + """ + URI reference of the place. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/place_citation_type.py b/google/genai/_fern_interactions/types/place_citation_type.py new file mode 100644 index 000000000..be7c3f08e --- /dev/null +++ b/google/genai/_fern_interactions/types/place_citation_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PlaceCitationType = typing.Union[typing.Literal["place_citation"], typing.Any] diff --git a/google/genai/_fern_interactions/types/places.py b/google/genai/_fern_interactions/types/places.py new file mode 100644 index 000000000..ab6f25270 --- /dev/null +++ b/google/genai/_fern_interactions/types/places.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .review_snippet import ReviewSnippet + + +class Places(UniversalBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Title of the place. + """ + + place_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the place, in `places/{place_id}` format. + """ + + review_snippets: typing.Optional[typing.List[ReviewSnippet]] = pydantic.Field(default=None) + """ + Snippets of reviews that are used to generate answers about the + features of a given place in Google Maps. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + URI reference of the place. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/response_format.py b/google/genai/_fern_interactions/types/response_format.py new file mode 100644 index 000000000..64638d770 --- /dev/null +++ b/google/genai/_fern_interactions/types/response_format.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .audio_response_format import AudioResponseFormat +from .image_response_format import ImageResponseFormat +from .text_response_format import TextResponseFormat + +ResponseFormat = typing.Union[ + AudioResponseFormat, TextResponseFormat, ImageResponseFormat, typing.Dict[str, typing.Any] +] diff --git a/google/genai/_fern_interactions/types/response_format_list.py b/google/genai/_fern_interactions/types/response_format_list.py new file mode 100644 index 000000000..b93ba8313 --- /dev/null +++ b/google/genai/_fern_interactions/types/response_format_list.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .response_format import ResponseFormat + +ResponseFormatList = typing.List[ResponseFormat] diff --git a/google/genai/_fern_interactions/types/response_modality.py b/google/genai/_fern_interactions/types/response_modality.py new file mode 100644 index 000000000..c8daf27af --- /dev/null +++ b/google/genai/_fern_interactions/types/response_modality.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ResponseModality = typing.Union[typing.Literal["text", "image", "audio", "video", "document"], typing.Any] diff --git a/google/genai/_fern_interactions/types/retrieval.py b/google/genai/_fern_interactions/types/retrieval.py new file mode 100644 index 000000000..3047b1751 --- /dev/null +++ b/google/genai/_fern_interactions/types/retrieval.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .retrieval_retrieval_types_item import RetrievalRetrievalTypesItem +from .retrieval_type import RetrievalType +from .vertex_ai_search_config import VertexAiSearchConfig + + +class Retrieval(UniversalBaseModel): + """ + A tool that can be used by the model to retrieve files. + """ + + retrieval_types: typing.Optional[typing.List[RetrievalRetrievalTypesItem]] = pydantic.Field(default=None) + """ + The types of file retrieval to enable. + """ + + type: RetrievalType + vertex_ai_search_config: typing.Optional[VertexAiSearchConfig] = pydantic.Field(default=None) + """ + Used to specify configuration for VertexAISearch. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/retrieval_retrieval_types_item.py b/google/genai/_fern_interactions/types/retrieval_retrieval_types_item.py new file mode 100644 index 000000000..67196200d --- /dev/null +++ b/google/genai/_fern_interactions/types/retrieval_retrieval_types_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RetrievalRetrievalTypesItem = typing.Union[typing.Literal["vertex_ai_search"], typing.Any] diff --git a/google/genai/_fern_interactions/types/retrieval_type.py b/google/genai/_fern_interactions/types/retrieval_type.py new file mode 100644 index 000000000..8cfcfa74e --- /dev/null +++ b/google/genai/_fern_interactions/types/retrieval_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RetrievalType = typing.Union[typing.Literal["retrieval"], typing.Any] diff --git a/google/genai/_fern_interactions/types/review_snippet.py b/google/genai/_fern_interactions/types/review_snippet.py new file mode 100644 index 000000000..3ca06fc78 --- /dev/null +++ b/google/genai/_fern_interactions/types/review_snippet.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class ReviewSnippet(UniversalBaseModel): + """ + Encapsulates a snippet of a user review that answers a question about + the features of a specific place in Google Maps. + """ + + review_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the review snippet. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + Title of the review. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + A link that corresponds to the user review on Google Maps. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/rotate_signing_secret_response.py b/google/genai/_fern_interactions/types/rotate_signing_secret_response.py new file mode 100644 index 000000000..55929d4b4 --- /dev/null +++ b/google/genai/_fern_interactions/types/rotate_signing_secret_response.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class RotateSigningSecretResponse(UniversalBaseModel): + """ + Response message for WebhookService.RotateSigningSecret. + """ + + secret: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The newly generated signing secret. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/service_tier.py b/google/genai/_fern_interactions/types/service_tier.py new file mode 100644 index 000000000..61c8b5334 --- /dev/null +++ b/google/genai/_fern_interactions/types/service_tier.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ServiceTier = typing.Union[typing.Literal["flex", "standard", "priority"], typing.Any] diff --git a/google/genai/_fern_interactions/types/signing_secret.py b/google/genai/_fern_interactions/types/signing_secret.py new file mode 100644 index 000000000..6bf026120 --- /dev/null +++ b/google/genai/_fern_interactions/types/signing_secret.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class SigningSecret(UniversalBaseModel): + """ + Represents a signing secret used to verify webhook payloads. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/signing_secret_read.py b/google/genai/_fern_interactions/types/signing_secret_read.py new file mode 100644 index 000000000..f73289216 --- /dev/null +++ b/google/genai/_fern_interactions/types/signing_secret_read.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class SigningSecretRead(UniversalBaseModel): + """ + Represents a signing secret used to verify webhook payloads. + """ + + expire_time: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + Output only. The expiration date of the signing secret. + """ + + truncated_secret: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The truncated version of the signing secret. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/source.py b/google/genai/_fern_interactions/types/source.py new file mode 100644 index 000000000..5d1357e43 --- /dev/null +++ b/google/genai/_fern_interactions/types/source.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .source_type import SourceType + + +class Source(UniversalBaseModel): + """ + A source to be mounted into the environment. + """ + + content: typing.Optional[str] = pydantic.Field(default=None) + """ + The inline content if `type` is `INLINE`. + """ + + encoding: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional encoding for inline content (e.g. `base64`). + """ + + source: typing.Optional[str] = pydantic.Field(default=None) + """ + The source of the environment. + For GCS, this is the GCS path. + For GitHub, this is the GitHub path. + """ + + target: typing.Optional[str] = pydantic.Field(default=None) + """ + Where the source should appear in the environment. + """ + + type: typing.Optional[SourceType] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/source_type.py b/google/genai/_fern_interactions/types/source_type.py new file mode 100644 index 000000000..5f99d8412 --- /dev/null +++ b/google/genai/_fern_interactions/types/source_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SourceType = typing.Union[typing.Literal["gcs", "inline", "repository", "skill_registry"], typing.Any] diff --git a/google/genai/_fern_interactions/types/speech_config.py b/google/genai/_fern_interactions/types/speech_config.py new file mode 100644 index 000000000..34e175716 --- /dev/null +++ b/google/genai/_fern_interactions/types/speech_config.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class SpeechConfig(UniversalBaseModel): + """ + The configuration for speech interaction. + """ + + language: typing.Optional[str] = pydantic.Field(default=None) + """ + The language of the speech. + """ + + speaker: typing.Optional[str] = pydantic.Field(default=None) + """ + The speaker's name, it should match the speaker name given in the prompt. + """ + + voice: typing.Optional[str] = pydantic.Field(default=None) + """ + The voice of the speaker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/step.py b/google/genai/_fern_interactions/types/step.py new file mode 100644 index 000000000..9c8961b0f --- /dev/null +++ b/google/genai/_fern_interactions/types/step.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .code_execution_call_step import CodeExecutionCallStep +from .code_execution_result_step import CodeExecutionResultStep +from .file_search_call_step import FileSearchCallStep +from .file_search_result_step import FileSearchResultStep +from .function_call_step import FunctionCallStep +from .function_result_step import FunctionResultStep +from .google_maps_call_step import GoogleMapsCallStep +from .google_maps_result_step import GoogleMapsResultStep +from .google_search_call_step import GoogleSearchCallStep +from .google_search_result_step import GoogleSearchResultStep +from .mcp_server_tool_call_step import McpServerToolCallStep +from .mcp_server_tool_result_step import McpServerToolResultStep +from .model_output_step import ModelOutputStep +from .thought_step import ThoughtStep +from .url_context_call_step import UrlContextCallStep +from .url_context_result_step import UrlContextResultStep +from .user_input_step import UserInputStep + +Step = typing.Union[ + UserInputStep, + ModelOutputStep, + ThoughtStep, + FunctionCallStep, + CodeExecutionCallStep, + UrlContextCallStep, + McpServerToolCallStep, + GoogleSearchCallStep, + FileSearchCallStep, + GoogleMapsCallStep, + FunctionResultStep, + CodeExecutionResultStep, + UrlContextResultStep, + GoogleSearchResultStep, + McpServerToolResultStep, + FileSearchResultStep, + GoogleMapsResultStep, +] diff --git a/google/genai/_fern_interactions/types/step_delta.py b/google/genai/_fern_interactions/types/step_delta.py new file mode 100644 index 000000000..d8a0e200f --- /dev/null +++ b/google/genai/_fern_interactions/types/step_delta.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .step_delta_data import StepDeltaData +from .step_delta_event_type import StepDeltaEventType + + +class StepDelta(UniversalBaseModel): + delta: StepDeltaData + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: StepDeltaEventType + index: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/step_delta_data.py b/google/genai/_fern_interactions/types/step_delta_data.py new file mode 100644 index 000000000..72f2fa9ec --- /dev/null +++ b/google/genai/_fern_interactions/types/step_delta_data.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .arguments_delta import ArgumentsDelta +from .audio_delta import AudioDelta +from .code_execution_call_delta import CodeExecutionCallDelta +from .code_execution_result_delta import CodeExecutionResultDelta +from .document_delta import DocumentDelta +from .file_search_call_delta import FileSearchCallDelta +from .file_search_result_delta import FileSearchResultDelta +from .function_result_delta import FunctionResultDelta +from .google_maps_call_delta import GoogleMapsCallDelta +from .google_maps_result_delta import GoogleMapsResultDelta +from .google_search_call_delta import GoogleSearchCallDelta +from .google_search_result_delta import GoogleSearchResultDelta +from .image_delta import ImageDelta +from .mcp_server_tool_call_delta import McpServerToolCallDelta +from .mcp_server_tool_result_delta import McpServerToolResultDelta +from .text_annotation_delta import TextAnnotationDelta +from .text_delta import TextDelta +from .thought_signature_delta import ThoughtSignatureDelta +from .thought_summary_delta import ThoughtSummaryDelta +from .url_context_call_delta import UrlContextCallDelta +from .url_context_result_delta import UrlContextResultDelta +from .video_delta import VideoDelta + +StepDeltaData = typing.Union[ + TextDelta, + ImageDelta, + AudioDelta, + DocumentDelta, + VideoDelta, + ThoughtSummaryDelta, + ThoughtSignatureDelta, + TextAnnotationDelta, + ArgumentsDelta, + CodeExecutionCallDelta, + UrlContextCallDelta, + GoogleSearchCallDelta, + McpServerToolCallDelta, + FileSearchCallDelta, + GoogleMapsCallDelta, + CodeExecutionResultDelta, + UrlContextResultDelta, + GoogleSearchResultDelta, + McpServerToolResultDelta, + FileSearchResultDelta, + GoogleMapsResultDelta, + FunctionResultDelta, +] diff --git a/google/genai/_fern_interactions/types/step_delta_event_type.py b/google/genai/_fern_interactions/types/step_delta_event_type.py new file mode 100644 index 000000000..48b684433 --- /dev/null +++ b/google/genai/_fern_interactions/types/step_delta_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +StepDeltaEventType = typing.Union[typing.Literal["step.delta"], typing.Any] diff --git a/google/genai/_fern_interactions/types/step_start.py b/google/genai/_fern_interactions/types/step_start.py new file mode 100644 index 000000000..9311026c8 --- /dev/null +++ b/google/genai/_fern_interactions/types/step_start.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .step import Step +from .step_start_event_type import StepStartEventType + + +class StepStart(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: StepStartEventType + index: int + step: Step + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/step_start_event_type.py b/google/genai/_fern_interactions/types/step_start_event_type.py new file mode 100644 index 000000000..a28e0cc38 --- /dev/null +++ b/google/genai/_fern_interactions/types/step_start_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +StepStartEventType = typing.Union[typing.Literal["step.start"], typing.Any] diff --git a/google/genai/_fern_interactions/types/step_stop.py b/google/genai/_fern_interactions/types/step_stop.py new file mode 100644 index 000000000..86a68b0c9 --- /dev/null +++ b/google/genai/_fern_interactions/types/step_stop.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .step_stop_event_type import StepStopEventType + + +class StepStop(UniversalBaseModel): + event_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The event_id token to be used to resume the interaction stream, from + this event. + """ + + event_type: StepStopEventType + index: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/step_stop_event_type.py b/google/genai/_fern_interactions/types/step_stop_event_type.py new file mode 100644 index 000000000..27b4ac429 --- /dev/null +++ b/google/genai/_fern_interactions/types/step_stop_event_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +StepStopEventType = typing.Union[typing.Literal["step.stop"], typing.Any] diff --git a/google/genai/_fern_interactions/types/text_annotation_delta.py b/google/genai/_fern_interactions/types/text_annotation_delta.py new file mode 100644 index 000000000..066e2a3f4 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_annotation_delta.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .annotation import Annotation +from .text_annotation_delta_type import TextAnnotationDeltaType + + +class TextAnnotationDelta(UniversalBaseModel): + annotations: typing.Optional[typing.List[Annotation]] = pydantic.Field(default=None) + """ + Citation information for model-generated content. + """ + + type: TextAnnotationDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/text_annotation_delta_type.py b/google/genai/_fern_interactions/types/text_annotation_delta_type.py new file mode 100644 index 000000000..11ff5db2a --- /dev/null +++ b/google/genai/_fern_interactions/types/text_annotation_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TextAnnotationDeltaType = typing.Union[typing.Literal["text_annotation_delta"], typing.Any] diff --git a/google/genai/_fern_interactions/types/text_content.py b/google/genai/_fern_interactions/types/text_content.py new file mode 100644 index 000000000..e2fb9083b --- /dev/null +++ b/google/genai/_fern_interactions/types/text_content.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .annotation import Annotation +from .text_content_type import TextContentType + + +class TextContent(UniversalBaseModel): + """ + A text content block. + """ + + annotations: typing.Optional[typing.List[Annotation]] = pydantic.Field(default=None) + """ + Citation information for model-generated content. + """ + + text: str = pydantic.Field() + """ + Required. The text content. + """ + + type: TextContentType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/text_content_type.py b/google/genai/_fern_interactions/types/text_content_type.py new file mode 100644 index 000000000..8fc38fae2 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TextContentType = typing.Union[typing.Literal["text"], typing.Any] diff --git a/google/genai/_fern_interactions/types/text_delta.py b/google/genai/_fern_interactions/types/text_delta.py new file mode 100644 index 000000000..2b0005fb8 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_delta.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .text_delta_type import TextDeltaType + + +class TextDelta(UniversalBaseModel): + text: str + type: TextDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/text_delta_type.py b/google/genai/_fern_interactions/types/text_delta_type.py new file mode 100644 index 000000000..5fdae7b51 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TextDeltaType = typing.Union[typing.Literal["text"], typing.Any] diff --git a/google/genai/_fern_interactions/types/text_response_format.py b/google/genai/_fern_interactions/types/text_response_format.py new file mode 100644 index 000000000..59108913c --- /dev/null +++ b/google/genai/_fern_interactions/types/text_response_format.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .text_response_format_mime_type import TextResponseFormatMimeType +from .text_response_format_type import TextResponseFormatType + + +class TextResponseFormat(UniversalBaseModel): + """ + Configuration for text output format. + """ + + mime_type: typing.Optional[TextResponseFormatMimeType] = pydantic.Field(default=None) + """ + The MIME type of the text output. + """ + + schema_: typing_extensions.Annotated[ + typing.Optional[typing.Dict[str, typing.Any]], + FieldMetadata(alias="schema"), + pydantic.Field( + alias="schema", + description="The JSON schema that the output should conform to. Only applicable when\nmime_type is application/json.", + ), + ] = None + type: TextResponseFormatType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/text_response_format_mime_type.py b/google/genai/_fern_interactions/types/text_response_format_mime_type.py new file mode 100644 index 000000000..253851024 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_response_format_mime_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TextResponseFormatMimeType = typing.Union[typing.Literal["application/json", "text/plain"], typing.Any] diff --git a/google/genai/_fern_interactions/types/text_response_format_type.py b/google/genai/_fern_interactions/types/text_response_format_type.py new file mode 100644 index 000000000..0d4c2e2f7 --- /dev/null +++ b/google/genai/_fern_interactions/types/text_response_format_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TextResponseFormatType = typing.Union[typing.Literal["text"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thinking_level.py b/google/genai/_fern_interactions/types/thinking_level.py new file mode 100644 index 000000000..9c75656a6 --- /dev/null +++ b/google/genai/_fern_interactions/types/thinking_level.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThinkingLevel = typing.Union[typing.Literal["minimal", "low", "medium", "high"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thinking_summaries.py b/google/genai/_fern_interactions/types/thinking_summaries.py new file mode 100644 index 000000000..2dc80e7f2 --- /dev/null +++ b/google/genai/_fern_interactions/types/thinking_summaries.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThinkingSummaries = typing.Union[typing.Literal["auto", "none"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thought_content.py b/google/genai/_fern_interactions/types/thought_content.py new file mode 100644 index 000000000..3785acf8f --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_content.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .thought_content_type import ThoughtContentType +from .thought_summary_content import ThoughtSummaryContent + + +class ThoughtContent(UniversalBaseModel): + """ + A thought content block. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + Signature to match the backend source to be part of the generation. + """ + + summary: typing.Optional[typing.List[ThoughtSummaryContent]] = pydantic.Field(default=None) + """ + A summary of the thought. + """ + + type: ThoughtContentType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/thought_content_type.py b/google/genai/_fern_interactions/types/thought_content_type.py new file mode 100644 index 000000000..85cfd628d --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThoughtContentType = typing.Union[typing.Literal["thought"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thought_signature_delta.py b/google/genai/_fern_interactions/types/thought_signature_delta.py new file mode 100644 index 000000000..447b51a2f --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_signature_delta.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .thought_signature_delta_type import ThoughtSignatureDeltaType + + +class ThoughtSignatureDelta(UniversalBaseModel): + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + Signature to match the backend source to be part of the generation. + """ + + type: ThoughtSignatureDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/thought_signature_delta_type.py b/google/genai/_fern_interactions/types/thought_signature_delta_type.py new file mode 100644 index 000000000..4157881e5 --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_signature_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThoughtSignatureDeltaType = typing.Union[typing.Literal["thought_signature"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thought_step.py b/google/genai/_fern_interactions/types/thought_step.py new file mode 100644 index 000000000..2c7f63808 --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_step.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .thought_step_type import ThoughtStepType +from .thought_summary_content import ThoughtSummaryContent + + +class ThoughtStep(UniversalBaseModel): + """ + A thought step. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + summary: typing.Optional[typing.List[ThoughtSummaryContent]] = pydantic.Field(default=None) + """ + A summary of the thought. + """ + + type: ThoughtStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/thought_step_type.py b/google/genai/_fern_interactions/types/thought_step_type.py new file mode 100644 index 000000000..907897612 --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThoughtStepType = typing.Union[typing.Literal["thought"], typing.Any] diff --git a/google/genai/_fern_interactions/types/thought_summary_content.py b/google/genai/_fern_interactions/types/thought_summary_content.py new file mode 100644 index 000000000..a57bcfcce --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_summary_content.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .image_content import ImageContent +from .text_content import TextContent + +ThoughtSummaryContent = typing.Union[TextContent, ImageContent] diff --git a/google/genai/_fern_interactions/types/thought_summary_delta.py b/google/genai/_fern_interactions/types/thought_summary_delta.py new file mode 100644 index 000000000..b1ab53251 --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_summary_delta.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .thought_summary_content import ThoughtSummaryContent +from .thought_summary_delta_type import ThoughtSummaryDeltaType + + +class ThoughtSummaryDelta(UniversalBaseModel): + content: typing.Optional[ThoughtSummaryContent] = pydantic.Field(default=None) + """ + A new summary item to be added to the thought. + """ + + type: ThoughtSummaryDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/thought_summary_delta_type.py b/google/genai/_fern_interactions/types/thought_summary_delta_type.py new file mode 100644 index 000000000..de73e2474 --- /dev/null +++ b/google/genai/_fern_interactions/types/thought_summary_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ThoughtSummaryDeltaType = typing.Union[typing.Literal["thought_summary"], typing.Any] diff --git a/google/genai/_fern_interactions/types/tool.py b/google/genai/_fern_interactions/types/tool.py new file mode 100644 index 000000000..9ac6751d7 --- /dev/null +++ b/google/genai/_fern_interactions/types/tool.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .code_execution import CodeExecution +from .computer_use import ComputerUse +from .file_search import FileSearch +from .function import Function +from .google_maps import GoogleMaps +from .google_search import GoogleSearch +from .mcp_server import McpServer +from .retrieval import Retrieval +from .url_context import UrlContext + +Tool = typing.Union[ + Function, CodeExecution, UrlContext, ComputerUse, McpServer, GoogleSearch, FileSearch, GoogleMaps, Retrieval +] diff --git a/google/genai/_fern_interactions/types/tool_choice_config.py b/google/genai/_fern_interactions/types/tool_choice_config.py new file mode 100644 index 000000000..c1d4ae7da --- /dev/null +++ b/google/genai/_fern_interactions/types/tool_choice_config.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .allowed_tools import AllowedTools + + +class ToolChoiceConfig(UniversalBaseModel): + """ + The tool choice configuration containing allowed tools. + """ + + allowed_tools: typing.Optional[AllowedTools] = pydantic.Field(default=None) + """ + The allowed tools. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/tool_choice_type.py b/google/genai/_fern_interactions/types/tool_choice_type.py new file mode 100644 index 000000000..69bb8ebee --- /dev/null +++ b/google/genai/_fern_interactions/types/tool_choice_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ToolChoiceType = typing.Union[typing.Literal["auto", "any", "none", "validated"], typing.Any] diff --git a/google/genai/_fern_interactions/types/turn.py b/google/genai/_fern_interactions/types/turn.py new file mode 100644 index 000000000..5ff6fa47d --- /dev/null +++ b/google/genai/_fern_interactions/types/turn.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .turn_content import TurnContent + + +class Turn(UniversalBaseModel): + content: typing.Optional[TurnContent] = None + role: typing.Optional[str] = pydantic.Field(default=None) + """ + The originator of this turn. Must be user for input or model for + model output. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/turn_content.py b/google/genai/_fern_interactions/types/turn_content.py new file mode 100644 index 000000000..5815c2bab --- /dev/null +++ b/google/genai/_fern_interactions/types/turn_content.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .content import Content + +TurnContent = typing.Union[typing.List[Content], str] diff --git a/google/genai/_fern_interactions/types/url_citation.py b/google/genai/_fern_interactions/types/url_citation.py new file mode 100644 index 000000000..7b84e0fcb --- /dev/null +++ b/google/genai/_fern_interactions/types/url_citation.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_citation_type import UrlCitationType + + +class UrlCitation(UniversalBaseModel): + """ + A URL citation annotation. + """ + + end_index: typing.Optional[int] = pydantic.Field(default=None) + """ + End of the attributed segment, exclusive. + """ + + start_index: typing.Optional[int] = pydantic.Field(default=None) + """ + Start of segment of the response that is attributed to this source. + + Index indicates the start of the segment, measured in bytes. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the URL. + """ + + type: UrlCitationType + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_citation_type.py b/google/genai/_fern_interactions/types/url_citation_type.py new file mode 100644 index 000000000..2352d7468 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_citation_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlCitationType = typing.Union[typing.Literal["url_citation"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context.py b/google/genai/_fern_interactions/types/url_context.py new file mode 100644 index 000000000..8cf9ec152 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_type import UrlContextType + + +class UrlContext(UniversalBaseModel): + """ + A tool that can be used by the model to fetch URL context. + """ + + type: UrlContextType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_call_arguments.py b/google/genai/_fern_interactions/types/url_context_call_arguments.py new file mode 100644 index 000000000..051b83183 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class UrlContextCallArguments(UniversalBaseModel): + """ + The arguments to pass to the URL context. + """ + + urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The URLs to fetch. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_call_delta.py b/google/genai/_fern_interactions/types/url_context_call_delta.py new file mode 100644 index 000000000..a7a396a10 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_delta.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_call_arguments import UrlContextCallArguments +from .url_context_call_delta_type import UrlContextCallDeltaType + + +class UrlContextCallDelta(UniversalBaseModel): + arguments: UrlContextCallArguments + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: UrlContextCallDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_call_delta_type.py b/google/genai/_fern_interactions/types/url_context_call_delta_type.py new file mode 100644 index 000000000..d0e2bc308 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextCallDeltaType = typing.Union[typing.Literal["url_context_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_call_step.py b/google/genai/_fern_interactions/types/url_context_call_step.py new file mode 100644 index 000000000..4854ddcf5 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_step.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_call_step_arguments import UrlContextCallStepArguments +from .url_context_call_step_type import UrlContextCallStepType + + +class UrlContextCallStep(UniversalBaseModel): + """ + URL context call step. + """ + + arguments: UrlContextCallStepArguments = pydantic.Field() + """ + Required. The arguments to pass to the URL context. + """ + + id: str = pydantic.Field() + """ + Required. A unique ID for this specific tool call. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: UrlContextCallStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_call_step_arguments.py b/google/genai/_fern_interactions/types/url_context_call_step_arguments.py new file mode 100644 index 000000000..f13edb0d2 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_step_arguments.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class UrlContextCallStepArguments(UniversalBaseModel): + """ + The arguments to pass to the URL context. + """ + + urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The URLs to fetch. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_call_step_type.py b/google/genai/_fern_interactions/types/url_context_call_step_type.py new file mode 100644 index 000000000..2516345fc --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_call_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextCallStepType = typing.Union[typing.Literal["url_context_call"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_result.py b/google/genai/_fern_interactions/types/url_context_result.py new file mode 100644 index 000000000..d5e8c9606 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_result_status import UrlContextResultStatus + + +class UrlContextResult(UniversalBaseModel): + """ + The result of the URL context. + """ + + status: typing.Optional[UrlContextResultStatus] = pydantic.Field(default=None) + """ + The status of the URL retrieval. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL that was fetched. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_result_delta.py b/google/genai/_fern_interactions/types/url_context_result_delta.py new file mode 100644 index 000000000..68e287ae7 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_delta.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_result import UrlContextResult +from .url_context_result_delta_type import UrlContextResultDeltaType + + +class UrlContextResultDelta(UniversalBaseModel): + is_error: typing.Optional[bool] = None + result: typing.List[UrlContextResult] + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: UrlContextResultDeltaType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_result_delta_type.py b/google/genai/_fern_interactions/types/url_context_result_delta_type.py new file mode 100644 index 000000000..1b17f711c --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextResultDeltaType = typing.Union[typing.Literal["url_context_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_result_item.py b/google/genai/_fern_interactions/types/url_context_result_item.py new file mode 100644 index 000000000..607db7b9a --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_item.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_result_item_status import UrlContextResultItemStatus + + +class UrlContextResultItem(UniversalBaseModel): + """ + The result of the URL context. + """ + + status: typing.Optional[UrlContextResultItemStatus] = pydantic.Field(default=None) + """ + The status of the URL retrieval. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL that was fetched. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_result_item_status.py b/google/genai/_fern_interactions/types/url_context_result_item_status.py new file mode 100644 index 000000000..878762a0f --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_item_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextResultItemStatus = typing.Union[typing.Literal["success", "error", "paywall", "unsafe"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_result_status.py b/google/genai/_fern_interactions/types/url_context_result_status.py new file mode 100644 index 000000000..850c33a77 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextResultStatus = typing.Union[typing.Literal["success", "error", "paywall", "unsafe"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_result_step.py b/google/genai/_fern_interactions/types/url_context_result_step.py new file mode 100644 index 000000000..7dfafffe7 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_step.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .url_context_result_item import UrlContextResultItem +from .url_context_result_step_type import UrlContextResultStepType + + +class UrlContextResultStep(UniversalBaseModel): + """ + URL context result step. + """ + + call_id: str = pydantic.Field() + """ + Required. ID to match the ID from the function call block. + """ + + is_error: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the URL context resulted in an error. + """ + + result: typing.List[UrlContextResultItem] = pydantic.Field() + """ + Required. The results of the URL context. + """ + + signature: typing.Optional[str] = pydantic.Field(default=None) + """ + A signature hash for backend validation. + """ + + type: UrlContextResultStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/url_context_result_step_type.py b/google/genai/_fern_interactions/types/url_context_result_step_type.py new file mode 100644 index 000000000..970ee0e5b --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_result_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextResultStepType = typing.Union[typing.Literal["url_context_result"], typing.Any] diff --git a/google/genai/_fern_interactions/types/url_context_type.py b/google/genai/_fern_interactions/types/url_context_type.py new file mode 100644 index 000000000..20a200339 --- /dev/null +++ b/google/genai/_fern_interactions/types/url_context_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UrlContextType = typing.Union[typing.Literal["url_context"], typing.Any] diff --git a/google/genai/_fern_interactions/types/usage.py b/google/genai/_fern_interactions/types/usage.py new file mode 100644 index 000000000..d3d6f0ed5 --- /dev/null +++ b/google/genai/_fern_interactions/types/usage.py @@ -0,0 +1,79 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .grounding_tool_count import GroundingToolCount +from .modality_tokens import ModalityTokens + + +class Usage(UniversalBaseModel): + """ + Statistics on the interaction request's token usage. + """ + + cached_tokens_by_modality: typing.Optional[typing.List[ModalityTokens]] = pydantic.Field(default=None) + """ + A breakdown of cached token usage by modality. + """ + + grounding_tool_count: typing.Optional[typing.List[GroundingToolCount]] = pydantic.Field(default=None) + """ + Grounding tool count. + """ + + input_tokens_by_modality: typing.Optional[typing.List[ModalityTokens]] = pydantic.Field(default=None) + """ + A breakdown of input token usage by modality. + """ + + output_tokens_by_modality: typing.Optional[typing.List[ModalityTokens]] = pydantic.Field(default=None) + """ + A breakdown of output token usage by modality. + """ + + tool_use_tokens_by_modality: typing.Optional[typing.List[ModalityTokens]] = pydantic.Field(default=None) + """ + A breakdown of tool-use token usage by modality. + """ + + total_cached_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of tokens in the cached part of the prompt (the cached content). + """ + + total_input_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of tokens in the prompt (context). + """ + + total_output_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of tokens across all the generated responses. + """ + + total_thought_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of tokens of thoughts for thinking models. + """ + + total_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Total token count for the interaction request (prompt + responses + other + internal tokens). + """ + + total_tool_use_tokens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of tokens present in tool-use prompt(s). + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/user_input_step.py b/google/genai/_fern_interactions/types/user_input_step.py new file mode 100644 index 000000000..4282f9bd6 --- /dev/null +++ b/google/genai/_fern_interactions/types/user_input_step.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .content import Content +from .user_input_step_type import UserInputStepType + + +class UserInputStep(UniversalBaseModel): + """ + Input provided by the user. + """ + + content: typing.Optional[typing.List[Content]] = None + type: UserInputStepType + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/user_input_step_type.py b/google/genai/_fern_interactions/types/user_input_step_type.py new file mode 100644 index 000000000..4664e8d7e --- /dev/null +++ b/google/genai/_fern_interactions/types/user_input_step_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UserInputStepType = typing.Union[typing.Literal["user_input"], typing.Any] diff --git a/google/genai/_fern_interactions/types/vertex_ai_search_config.py b/google/genai/_fern_interactions/types/vertex_ai_search_config.py new file mode 100644 index 000000000..5d6d40868 --- /dev/null +++ b/google/genai/_fern_interactions/types/vertex_ai_search_config.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class VertexAiSearchConfig(UniversalBaseModel): + """ + Used to specify configuration for VertexAISearch. + """ + + datastores: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Optional. Used to specify Vertex AI Search datastores. + """ + + engine: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. Used to specify Vertex AI Search engine. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/video_content.py b/google/genai/_fern_interactions/types/video_content.py new file mode 100644 index 000000000..e02146172 --- /dev/null +++ b/google/genai/_fern_interactions/types/video_content.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .media_resolution import MediaResolution +from .video_content_mime_type import VideoContentMimeType +from .video_content_type import VideoContentType + + +class VideoContent(UniversalBaseModel): + """ + A video content block. + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The video content. + """ + + mime_type: typing.Optional[VideoContentMimeType] = pydantic.Field(default=None) + """ + The mime type of the video. + """ + + resolution: typing.Optional[MediaResolution] = pydantic.Field(default=None) + """ + The resolution of the media. + """ + + type: VideoContentType + uri: typing.Optional[str] = pydantic.Field(default=None) + """ + The URI of the video. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/video_content_mime_type.py b/google/genai/_fern_interactions/types/video_content_mime_type.py new file mode 100644 index 000000000..146db6774 --- /dev/null +++ b/google/genai/_fern_interactions/types/video_content_mime_type.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +VideoContentMimeType = typing.Union[ + typing.Literal[ + "video/mp4", + "video/mpeg", + "video/mpg", + "video/mov", + "video/avi", + "video/x-flv", + "video/webm", + "video/wmv", + "video/3gpp", + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/video_content_type.py b/google/genai/_fern_interactions/types/video_content_type.py new file mode 100644 index 000000000..434eb01bc --- /dev/null +++ b/google/genai/_fern_interactions/types/video_content_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +VideoContentType = typing.Union[typing.Literal["video"], typing.Any] diff --git a/google/genai/_fern_interactions/types/video_delta.py b/google/genai/_fern_interactions/types/video_delta.py new file mode 100644 index 000000000..7adc276a6 --- /dev/null +++ b/google/genai/_fern_interactions/types/video_delta.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .media_resolution import MediaResolution +from .video_delta_mime_type import VideoDeltaMimeType +from .video_delta_type import VideoDeltaType + + +class VideoDelta(UniversalBaseModel): + data: typing.Optional[str] = None + mime_type: typing.Optional[VideoDeltaMimeType] = None + resolution: typing.Optional[MediaResolution] = pydantic.Field(default=None) + """ + The resolution of the media. + """ + + type: VideoDeltaType + uri: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/video_delta_mime_type.py b/google/genai/_fern_interactions/types/video_delta_mime_type.py new file mode 100644 index 000000000..ee080f46f --- /dev/null +++ b/google/genai/_fern_interactions/types/video_delta_mime_type.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +VideoDeltaMimeType = typing.Union[ + typing.Literal[ + "video/mp4", + "video/mpeg", + "video/mpg", + "video/mov", + "video/avi", + "video/x-flv", + "video/webm", + "video/wmv", + "video/3gpp", + ], + typing.Any, +] diff --git a/google/genai/_fern_interactions/types/video_delta_type.py b/google/genai/_fern_interactions/types/video_delta_type.py new file mode 100644 index 000000000..2185b0f23 --- /dev/null +++ b/google/genai/_fern_interactions/types/video_delta_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +VideoDeltaType = typing.Union[typing.Literal["video"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook.py b/google/genai/_fern_interactions/types/webhook.py new file mode 100644 index 000000000..244d4f2b7 --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .webhook_subscribed_events_item import WebhookSubscribedEventsItem + + +class Webhook(UniversalBaseModel): + """ + A Webhook resource. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. The user-provided name of the webhook. + """ + + subscribed_events: typing.List[WebhookSubscribedEventsItem] = pydantic.Field() + """ + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + + uri: str = pydantic.Field() + """ + Required. The URI to which webhook events will be sent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/webhook_config.py b/google/genai/_fern_interactions/types/webhook_config.py new file mode 100644 index 000000000..9118afef6 --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_config.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class WebhookConfig(UniversalBaseModel): + """ + Message for configuring webhook events for a request. + """ + + uris: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Optional. If set, these webhook URIs will be used for webhook events instead of the + registered webhooks. + """ + + user_metadata: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Optional. The user metadata that will be returned on each event emission to the + webhooks. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/webhook_read.py b/google/genai/_fern_interactions/types/webhook_read.py new file mode 100644 index 000000000..b1b5429df --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_read.py @@ -0,0 +1,78 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .signing_secret_read import SigningSecretRead +from .webhook_state import WebhookState +from .webhook_subscribed_events_item import WebhookSubscribedEventsItem + + +class WebhookRead(UniversalBaseModel): + """ + A Webhook resource. + """ + + create_time: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + Output only. The timestamp when the webhook was created. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The ID of the webhook. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. The user-provided name of the webhook. + """ + + new_signing_secret: typing.Optional[str] = pydantic.Field(default=None) + """ + Output only. The new signing secret for the webhook. Only populated on create. + """ + + signing_secrets: typing.Optional[typing.List[SigningSecretRead]] = pydantic.Field(default=None) + """ + Output only. The signing secrets associated with this webhook. + """ + + state: typing.Optional[WebhookState] = pydantic.Field(default=None) + """ + Output only. The state of the webhook. + """ + + subscribed_events: typing.List[WebhookSubscribedEventsItem] = pydantic.Field() + """ + Required. The events that the webhook is subscribed to. + Available events: + - batch.succeeded + - batch.expired + - batch.failed + - interaction.requires_action + - interaction.completed + - interaction.failed + - video.generated + """ + + update_time: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + Output only. The timestamp when the webhook was last updated. + """ + + uri: str = pydantic.Field() + """ + Required. The URI to which webhook events will be sent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/google/genai/_fern_interactions/types/webhook_state.py b/google/genai/_fern_interactions/types/webhook_state.py new file mode 100644 index 000000000..9b4137278 --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookState = typing.Union[typing.Literal["enabled", "disabled", "disabled_due_to_failed_deliveries"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item.py new file mode 100644 index 000000000..628d1a4c2 --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .webhook_subscribed_events_item_five import WebhookSubscribedEventsItemFive +from .webhook_subscribed_events_item_four import WebhookSubscribedEventsItemFour +from .webhook_subscribed_events_item_one import WebhookSubscribedEventsItemOne +from .webhook_subscribed_events_item_seven import WebhookSubscribedEventsItemSeven +from .webhook_subscribed_events_item_six import WebhookSubscribedEventsItemSix +from .webhook_subscribed_events_item_three import WebhookSubscribedEventsItemThree +from .webhook_subscribed_events_item_two import WebhookSubscribedEventsItemTwo + +WebhookSubscribedEventsItem = typing.Union[ + str, + WebhookSubscribedEventsItemOne, + WebhookSubscribedEventsItemTwo, + WebhookSubscribedEventsItemThree, + WebhookSubscribedEventsItemFour, + WebhookSubscribedEventsItemFive, + WebhookSubscribedEventsItemSix, + WebhookSubscribedEventsItemSeven, +] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_five.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_five.py new file mode 100644 index 000000000..cfbf231da --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_five.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemFive = typing.Union[typing.Literal["interaction.completed"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_four.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_four.py new file mode 100644 index 000000000..3e6b8123f --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_four.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemFour = typing.Union[typing.Literal["interaction.requires_action"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_one.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_one.py new file mode 100644 index 000000000..7d81f1f4b --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemOne = typing.Union[typing.Literal["batch.succeeded"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_seven.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_seven.py new file mode 100644 index 000000000..fa97ed493 --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_seven.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemSeven = typing.Union[typing.Literal["video.generated"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_six.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_six.py new file mode 100644 index 000000000..65823d8ae --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_six.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemSix = typing.Union[typing.Literal["interaction.failed"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_three.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_three.py new file mode 100644 index 000000000..796563f0b --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_three.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemThree = typing.Union[typing.Literal["batch.failed"], typing.Any] diff --git a/google/genai/_fern_interactions/types/webhook_subscribed_events_item_two.py b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_two.py new file mode 100644 index 000000000..b474eab6b --- /dev/null +++ b/google/genai/_fern_interactions/types/webhook_subscribed_events_item_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WebhookSubscribedEventsItemTwo = typing.Union[typing.Literal["batch.expired"], typing.Any] diff --git a/google/genai/_fern_interactions/version.py b/google/genai/_fern_interactions/version.py new file mode 100644 index 000000000..d51d28b35 --- /dev/null +++ b/google/genai/_fern_interactions/version.py @@ -0,0 +1,3 @@ +from importlib import metadata + +__version__ = metadata.version("gemini_next_gen_api") diff --git a/google/genai/_interactions_wrapper.py b/google/genai/_interactions_wrapper.py new file mode 100644 index 000000000..7b5205107 --- /dev/null +++ b/google/genai/_interactions_wrapper.py @@ -0,0 +1,338 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Wrapper for Fern-backed interactions to preserve Stainless signature.""" + +import typing +from typing import Any, Iterable, List, Optional, Union + +from ._fern_interactions.core.request_options import RequestOptions +from ._fern_interactions.fern_interactions.client import AsyncFernInteractionsClient, FernInteractionsClient +from ._fern_interactions.types import CreateAgentInteractionParams, CreateModelInteractionParams + + +def _make_fern_request_options( + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, +) -> Optional[RequestOptions]: + opts: RequestOptions = {} + if extra_headers: + opts['additional_headers'] = extra_headers + if extra_query: + opts['additional_query_parameters'] = extra_query + if extra_body: + opts['additional_body_parameters'] = extra_body + if timeout is not None: + opts['timeout_in_seconds'] = int(timeout) + return opts if opts else None + + +class Interactions: + """Wrapper for FernInteractionsClient to preserve Stainless signature.""" + + def __init__(self, fern_client: FernInteractionsClient): + self._fern_client = fern_client + + def create( + self, + *, + input: Optional[Any] = None, + model: Optional[str] = None, + agent: Optional[str] = None, + background: Optional[bool] = None, + environment: Optional[Any] = None, + generation_config: Optional[Any] = None, + previous_interaction_id: Optional[str] = None, + response_format: Optional[Any] = None, + response_mime_type: Optional[str] = None, + response_modalities: Optional[List[str]] = None, + service_tier: Optional[str] = None, + store: Optional[bool] = None, + stream: Optional[bool] = None, + system_instruction: Optional[str] = None, + tools: Optional[Iterable[Any]] = None, + webhook_config: Optional[Any] = None, + agent_config: Optional[Any] = None, + # Support raw request for backward compatibility + request: Optional[Any] = None, + request_options: Optional[Any] = None, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + if request is not None: + if stream: + return self._fern_client.create_stream(request=request, request_options=request_options) + else: + return self._fern_client.create(request=request, request_options=request_options) + + if input is None: + raise TypeError("missing 1 required keyword-only argument: 'input'") + + if request_options is None: + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + + if model is not None: + params = { + "model": model, + "input": input, + "background": background, + "store": store, + "stream": stream, + "environment": environment, + "previous_interaction_id": previous_interaction_id, + "response_format": response_format, + "response_mime_type": response_mime_type, + "response_modalities": response_modalities, + "service_tier": service_tier, + "system_instruction": system_instruction, + "tools": tools, + "webhook_config": webhook_config, + "generation_config": generation_config, + } + params = {k: v for k, v in params.items() if v is not None} + req = CreateModelInteractionParams(**params) + elif agent is not None: + params = { + "agent": agent, + "input": input, + "background": background, + "store": store, + "stream": stream, + "environment": environment, + "previous_interaction_id": previous_interaction_id, + "response_format": response_format, + "response_mime_type": response_mime_type, + "response_modalities": response_modalities, + "service_tier": service_tier, + "system_instruction": system_instruction, + "tools": tools, + "webhook_config": webhook_config, + "agent_config": agent_config, + } + params = {k: v for k, v in params.items() if v is not None} + req = CreateAgentInteractionParams(**params) + else: + raise ValueError("Either model or agent must be specified") + + if stream: + return self._fern_client.create_stream(request=req, request_options=request_options) + else: + return self._fern_client.create(request=req, request_options=request_options) + + def get( + self, + id: str, + *, + stream: Optional[bool] = None, + include_input: Optional[bool] = None, + last_event_id: Optional[str] = None, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + if stream: + return self._fern_client.get_stream( + id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + request_options=request_options, + ) + else: + return self._fern_client.get( + id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + request_options=request_options, + ) + + def cancel( + self, + id: str, + *, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + return self._fern_client.cancel(id, request_options=request_options) + + def delete( + self, + id: str, + *, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + return self._fern_client.delete(id, request_options=request_options) + + +class AsyncInteractions: + """Wrapper for AsyncFernInteractionsClient to preserve Stainless signature.""" + + def __init__(self, fern_client: AsyncFernInteractionsClient): + self._fern_client = fern_client + + async def create( + self, + *, + input: Optional[Any] = None, + model: Optional[str] = None, + agent: Optional[str] = None, + background: Optional[bool] = None, + environment: Optional[Any] = None, + generation_config: Optional[Any] = None, + previous_interaction_id: Optional[str] = None, + response_format: Optional[Any] = None, + response_mime_type: Optional[str] = None, + response_modalities: Optional[List[str]] = None, + service_tier: Optional[str] = None, + store: Optional[bool] = None, + stream: Optional[bool] = None, + system_instruction: Optional[str] = None, + tools: Optional[Iterable[Any]] = None, + webhook_config: Optional[Any] = None, + agent_config: Optional[Any] = None, + # Support raw request for backward compatibility + request: Optional[Any] = None, + request_options: Optional[Any] = None, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + if request is not None: + if stream: + return self._fern_client.create_stream(request=request, request_options=request_options) + else: + return await self._fern_client.create(request=request, request_options=request_options) + + if input is None: + raise TypeError("missing 1 required keyword-only argument: 'input'") + + if request_options is None: + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + + if model is not None: + params = { + "model": model, + "input": input, + "background": background, + "store": store, + "stream": stream, + "environment": environment, + "previous_interaction_id": previous_interaction_id, + "response_format": response_format, + "response_mime_type": response_mime_type, + "response_modalities": response_modalities, + "service_tier": service_tier, + "system_instruction": system_instruction, + "tools": tools, + "webhook_config": webhook_config, + "generation_config": generation_config, + } + params = {k: v for k, v in params.items() if v is not None} + req = CreateModelInteractionParams(**params) + elif agent is not None: + params = { + "agent": agent, + "input": input, + "background": background, + "store": store, + "stream": stream, + "environment": environment, + "previous_interaction_id": previous_interaction_id, + "response_format": response_format, + "response_mime_type": response_mime_type, + "response_modalities": response_modalities, + "service_tier": service_tier, + "system_instruction": system_instruction, + "tools": tools, + "webhook_config": webhook_config, + "agent_config": agent_config, + } + params = {k: v for k, v in params.items() if v is not None} + req = CreateAgentInteractionParams(**params) + else: + raise ValueError("Either model or agent must be specified") + + if stream: + return self._fern_client.create_stream(request=req, request_options=request_options) + else: + return await self._fern_client.create(request=req, request_options=request_options) + + async def get( + self, + id: str, + *, + stream: Optional[bool] = None, + include_input: Optional[bool] = None, + last_event_id: Optional[str] = None, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + if stream: + return self._fern_client.get_stream( + id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + request_options=request_options, + ) + else: + return await self._fern_client.get( + id, + stream=stream, + last_event_id=last_event_id, + include_input=include_input, + request_options=request_options, + ) + + async def cancel( + self, + id: str, + *, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + return await self._fern_client.cancel(id, request_options=request_options) + + async def delete( + self, + id: str, + *, + extra_headers: Optional[dict] = None, + extra_query: Optional[dict] = None, + extra_body: Optional[dict] = None, + timeout: Optional[float] = None, + ): + request_options = _make_fern_request_options(extra_headers, extra_query, extra_body, timeout) + return await self._fern_client.delete(id, request_options=request_options) diff --git a/google/genai/client.py b/google/genai/client.py index 55663b888..e57dce306 100644 --- a/google/genai/client.py +++ b/google/genai/client.py @@ -15,7 +15,8 @@ import asyncio import os -from typing import Any, Optional, Union +import re +from typing import Any, Optional, Union, cast import google.auth import pydantic @@ -45,10 +46,31 @@ from ._interactions import AsyncGeminiNextGenAPIClient, DEFAULT_MAX_RETRIES, GeminiNextGenAPIClient from . import _interactions -from ._interactions.resources import AsyncInteractionsResource as AsyncNextGenInteractionsResource, InteractionsResource as NextGenInteractionsResource -from ._interactions.resources import WebhooksResource, AsyncWebhooksResource, AgentsResource, AsyncAgentsResource -_interactions_experimental_warned = False -_agent_experimental_warned = False + +from ._fern_interactions.client import ( + AsyncGeminiNextGenAPIClient as _FernAsyncClient, + GeminiNextGenAPIClient as _FernSyncClient, +) +from ._fern_interactions.core.client_wrapper import ( + AsyncClientWrapper as _FernAsyncWrapper, + SyncClientWrapper as _FernSyncWrapper, +) +from ._fern_interactions.fern_interactions.client import ( + AsyncFernInteractionsClient as _FernAsyncInteractionsClient, + FernInteractionsClient as _FernSyncInteractionsClient, +) +from ._interactions_wrapper import AsyncInteractions, Interactions +from ._fern_interactions.fern_webhooks.client import ( + AsyncFernWebhooksClient as _FernAsyncWebhooksClient, + FernWebhooksClient as _FernSyncWebhooksClient, +) +from ._fern_interactions.fern_agents.client import ( + AsyncFernAgentsClient as _FernAsyncAgentsClient, + FernAgentsClient as _FernSyncAgentsClient, +) + +_fern_interactions_experimental_warned = False +_FERN_DEFAULT_MAX_RETRIES = 2 class AsyncGeminiNextGenAPIClientAdapter(_interactions.AsyncGeminiNextGenAPIClientAdapter): """Adapter for the Gemini NextGen API Client.""" @@ -104,6 +126,198 @@ def get_auth_headers(self) -> dict[str, str]: return headers +# --------------------------------------------------------------------------- +# Shared warning helpers (used by both Stainless and Fern nextgen paths) +# --------------------------------------------------------------------------- + +def _warn_unsupported_http_opts(http_opts: HttpOptions) -> None: + if http_opts.extra_body: + warnings.warn( + 'extra_body properties are not supported in `.interactions` yet', + category=UserWarning, + stacklevel=5, + ) + retry_opts = http_opts.retry_options + if retry_opts is not None and ( + retry_opts.initial_delay is not None + or retry_opts.max_delay is not None + or retry_opts.exp_base is not None + or retry_opts.jitter is not None + or retry_opts.http_status_codes is not None + ): + warnings.warn( + 'Granular retry options are not supported in `.interactions` yet', + category=UserWarning, + stacklevel=5, + ) + + +def _warn_aiohttp_fallback(http_opts: HttpOptions) -> None: + async_client_args = http_opts.async_client_args or {} + if has_aiohttp and 'transport' not in async_client_args: + warnings.warn( + 'Async interactions client cannot use aiohttp, fallingback to httpx.', + category=UserWarning, + stacklevel=5, + ) + + +def _resolve_max_retries(http_opts: HttpOptions) -> int: + retry_opts = http_opts.retry_options + if retry_opts is not None and retry_opts.attempts is not None: + return retry_opts.attempts + return _FERN_DEFAULT_MAX_RETRIES + + +# --------------------------------------------------------------------------- +# Fern auth-bridging wrapper subclasses +# --------------------------------------------------------------------------- + +class _FernGenAiSyncClientWrapper(_FernSyncWrapper): + """Bridges Fern's static auth to BaseApiClient's dynamic auth.""" + + def __init__(self, *, api_client: BaseApiClient, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._api_client = api_client + + def get_headers(self) -> dict[str, str]: + headers = super().get_headers() + if self._api_client.api_key: + headers['x-goog-api-key'] = self._api_client.api_key + return headers + headers.pop('x-goog-api-key', None) + token = self._api_client._access_token() + headers['Authorization'] = f'Bearer {token}' + if (creds := self._api_client._credentials) and creds.quota_project_id: + headers['x-goog-user-project'] = creds.quota_project_id + return headers + + +class _FernGenAiAsyncClientWrapper(_FernAsyncWrapper): + """Async equivalent of _FernGenAiSyncClientWrapper.""" + + def __init__(self, *, api_client: BaseApiClient, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._api_client = api_client + + def get_headers(self) -> dict[str, str]: + headers = super().get_headers() + if self._api_client.api_key: + headers['x-goog-api-key'] = self._api_client.api_key + else: + headers.pop('x-goog-api-key', None) + return headers + + async def async_get_headers(self) -> dict[str, str]: + headers = self.get_headers() + if not self._api_client.api_key: + token = await self._api_client._async_access_token() + headers['Authorization'] = f'Bearer {token}' + if (creds := self._api_client._credentials) and creds.quota_project_id: + headers['x-goog-user-project'] = creds.quota_project_id + return headers + + +# --------------------------------------------------------------------------- +# Base URL + api_version helpers +# --------------------------------------------------------------------------- + +_VERSION_SUFFIX = re.compile(r'/v\d+[a-z0-9]*/?$') + + +def _fern_base_url(api_client: BaseApiClient) -> str: + http_opts = api_client._http_options + if api_client.vertexai: + # User already supplied a fully-formed URL with project/location -> trust as-is. + if http_opts.base_url and '/projects/' in http_opts.base_url: + return http_opts.base_url.rstrip('/') + # Trust the host BaseApiClient already picked (handles global, + # multi-regional, api_key+vertex, custom base_url). Append the + # project/location path Fern's per-call template will be joined onto. + host = (http_opts.base_url or f'https://{api_client.location}-aiplatform.googleapis.com').rstrip('/') + version = http_opts.api_version or 'v1beta1' + return f'{host}/{version}/projects/{api_client.project}/locations/{api_client.location}' + if http_opts.base_url: + return _VERSION_SUFFIX.sub('', http_opts.base_url).rstrip('/') + return 'https://generativelanguage.googleapis.com' + + +# --------------------------------------------------------------------------- +# Lazy Fern nextgen-client builders +# --------------------------------------------------------------------------- + +def _build_sync_fern_nextgen(api_client: BaseApiClient) -> _FernSyncClient: + http_opts = api_client._http_options + _warn_unsupported_http_opts(http_opts) + max_retries = _resolve_max_retries(http_opts) + base_url = _fern_base_url(api_client) + # Path template per Fern raw client: `{wrapper.api_version}/{method.api_version}/`. + # Vertex base_url already embeds the version, so wrapper api_version stays "". + # Gemini puts the version on the wrapper so callers can pass "" per-call. + wrapper_api_version = '' if api_client.vertexai else (http_opts.api_version or 'v1beta') + api_key = api_client.api_key or 'vertex-oauth' + + nextgen = _FernSyncClient( + base_url=base_url, + api_key=api_key, + api_version=wrapper_api_version, + headers=http_opts.headers, + httpx_client=api_client._httpx_client, + timeout=http_opts.timeout / 1000 if http_opts.timeout else None, + max_retries=max_retries, + ) + + nextgen._client_wrapper = _FernGenAiSyncClientWrapper( + api_client=api_client, + base_url=base_url, + api_version=wrapper_api_version, + api_key=api_key, + headers=http_opts.headers, + httpx_client=api_client._httpx_client or nextgen._client_wrapper.httpx_client.httpx_client, + timeout=http_opts.timeout / 1000 if http_opts.timeout else None, + max_retries=max_retries, + ) + nextgen._fern_interactions = None + nextgen._fern_webhooks = None + nextgen._fern_agents = None + return nextgen + + +def _build_async_fern_nextgen(api_client: BaseApiClient) -> _FernAsyncClient: + http_opts = api_client._http_options + _warn_unsupported_http_opts(http_opts) + _warn_aiohttp_fallback(http_opts) + max_retries = _resolve_max_retries(http_opts) + base_url = _fern_base_url(api_client) + wrapper_api_version = '' if api_client.vertexai else (http_opts.api_version or 'v1beta') + api_key = api_client.api_key or 'vertex-oauth' + + nextgen = _FernAsyncClient( + base_url=base_url, + api_key=api_key, + api_version=wrapper_api_version, + headers=http_opts.headers, + httpx_client=api_client._async_httpx_client, + timeout=http_opts.timeout / 1000 if http_opts.timeout else None, + max_retries=max_retries, + ) + + nextgen._client_wrapper = _FernGenAiAsyncClientWrapper( + api_client=api_client, + base_url=base_url, + api_version=wrapper_api_version, + api_key=api_key, + headers=http_opts.headers, + httpx_client=api_client._async_httpx_client or nextgen._client_wrapper.httpx_client.httpx_client, + timeout=http_opts.timeout / 1000 if http_opts.timeout else None, + max_retries=max_retries, + ) + nextgen._fern_interactions = None + nextgen._fern_webhooks = None + nextgen._fern_agents = None + return nextgen + + class AsyncClient: """Client for making asynchronous (non-blocking) requests.""" @@ -120,6 +334,7 @@ def __init__(self, api_client: BaseApiClient): self._tokens = AsyncTokens(self._api_client) self._operations = AsyncOperations(self._api_client) self._nextgen_client_instance: Optional[AsyncGeminiNextGenAPIClient] = None + self._fern_nextgen_client_instance: Optional[_FernAsyncClient] = None @property def _nextgen_client(self) -> AsyncGeminiNextGenAPIClient: @@ -189,32 +404,55 @@ def _nextgen_client(self) -> AsyncGeminiNextGenAPIClient: return self._nextgen_client_instance @property - def interactions(self) -> AsyncNextGenInteractionsResource: - global _interactions_experimental_warned - if not _interactions_experimental_warned: - _interactions_experimental_warned = True + def _fern_nextgen_client(self) -> _FernAsyncClient: + if self._fern_nextgen_client_instance is None: + self._fern_nextgen_client_instance = _build_async_fern_nextgen(self._api_client) + return self._fern_nextgen_client_instance + + @property + def _has_fern_nextgen_client(self) -> bool: + return getattr(self, '_fern_nextgen_client_instance', None) is not None + + def _warn_fern_experimental(self) -> None: + global _fern_interactions_experimental_warned + if not _fern_interactions_experimental_warned: + _fern_interactions_experimental_warned = True warnings.warn( - 'Interactions usage is experimental and may change in future versions.', + 'Fern-backed integrations (fern_interactions / fern_webhooks / ' + 'fern_agents) are experimental and may change in future versions.', category=UserWarning, stacklevel=1, ) - return self._nextgen_client.interactions @property - def webhooks(self) -> AsyncWebhooksResource: - return self._nextgen_client.webhooks + def fern_interactions(self) -> _FernAsyncInteractionsClient: + self._warn_fern_experimental() + return cast(_FernAsyncInteractionsClient, self._fern_nextgen_client.fern_interactions) @property - def agents(self) -> AsyncAgentsResource: - global _agent_experimental_warned - if not _agent_experimental_warned: - _agent_experimental_warned = True - warnings.warn( - 'Agents usage is experimental and may change in future versions.', - category=UserWarning, - stacklevel=1, - ) - return self._nextgen_client.agents + def fern_webhooks(self) -> _FernAsyncWebhooksClient: + self._warn_fern_experimental() + return cast(_FernAsyncWebhooksClient, self._fern_nextgen_client.fern_webhooks) + + @property + def fern_agents(self) -> _FernAsyncAgentsClient: + self._warn_fern_experimental() + return cast(_FernAsyncAgentsClient, self._fern_nextgen_client.fern_agents) + + @property + def interactions(self) -> AsyncInteractions: + self._warn_fern_experimental() + return AsyncInteractions(cast(_FernAsyncInteractionsClient, self._fern_nextgen_client.fern_interactions)) + + @property + def webhooks(self) -> _FernAsyncWebhooksClient: + self._warn_fern_experimental() + return cast(_FernAsyncWebhooksClient, self._fern_nextgen_client.fern_webhooks) + + @property + def agents(self) -> _FernAsyncAgentsClient: + self._warn_fern_experimental() + return cast(_FernAsyncAgentsClient, self._fern_nextgen_client.fern_agents) @property def _has_nextgen_client(self) -> bool: @@ -293,6 +531,9 @@ async def aclose(self) -> None: if self._has_nextgen_client: await self._nextgen_client.close() + if self._has_fern_nextgen_client: + self._fern_nextgen_client_instance = None + async def __aenter__(self) -> 'AsyncClient': return self @@ -475,6 +716,7 @@ def __init__( self._tokens = Tokens(self._api_client) self._operations = Operations(self._api_client) self._nextgen_client_instance: Optional[GeminiNextGenAPIClient] = None + self._fern_nextgen_client_instance: Optional[_FernSyncClient] = None @staticmethod def _get_api_client( @@ -566,32 +808,55 @@ def _nextgen_client(self) -> GeminiNextGenAPIClient: return self._nextgen_client_instance @property - def interactions(self) -> NextGenInteractionsResource: - global _interactions_experimental_warned - if not _interactions_experimental_warned: - _interactions_experimental_warned = True + def _fern_nextgen_client(self) -> _FernSyncClient: + if self._fern_nextgen_client_instance is None: + self._fern_nextgen_client_instance = _build_sync_fern_nextgen(self._api_client) + return self._fern_nextgen_client_instance + + @property + def _has_fern_nextgen_client(self) -> bool: + return getattr(self, '_fern_nextgen_client_instance', None) is not None + + def _warn_fern_experimental(self) -> None: + global _fern_interactions_experimental_warned + if not _fern_interactions_experimental_warned: + _fern_interactions_experimental_warned = True warnings.warn( - 'Interactions usage is experimental and may change in future versions.', - category=UserWarning, - stacklevel=2, + 'Fern-backed integrations (fern_interactions / fern_webhooks / ' + 'fern_agents) are experimental and may change in future versions.', + category=UserWarning, + stacklevel=2, ) - return self._nextgen_client.interactions @property - def webhooks(self) -> WebhooksResource: - return self._nextgen_client.webhooks + def fern_interactions(self) -> _FernSyncInteractionsClient: + self._warn_fern_experimental() + return cast(_FernSyncInteractionsClient, self._fern_nextgen_client.fern_interactions) @property - def agents(self) -> AgentsResource: - global _agent_experimental_warned - if not _agent_experimental_warned: - _agent_experimental_warned = True - warnings.warn( - 'Agents usage is experimental and may change in future versions.', - category=UserWarning, - stacklevel=2, - ) - return self._nextgen_client.agents + def fern_webhooks(self) -> _FernSyncWebhooksClient: + self._warn_fern_experimental() + return cast(_FernSyncWebhooksClient, self._fern_nextgen_client.fern_webhooks) + + @property + def fern_agents(self) -> _FernSyncAgentsClient: + self._warn_fern_experimental() + return cast(_FernSyncAgentsClient, self._fern_nextgen_client.fern_agents) + + @property + def interactions(self) -> Interactions: + self._warn_fern_experimental() + return Interactions(cast(_FernSyncInteractionsClient, self._fern_nextgen_client.fern_interactions)) + + @property + def webhooks(self) -> _FernSyncWebhooksClient: + self._warn_fern_experimental() + return cast(_FernSyncWebhooksClient, self._fern_nextgen_client.fern_webhooks) + + @property + def agents(self) -> _FernSyncAgentsClient: + self._warn_fern_experimental() + return cast(_FernSyncAgentsClient, self._fern_nextgen_client.fern_agents) @property def _has_nextgen_client(self) -> bool: @@ -675,6 +940,9 @@ def close(self) -> None: if self._has_nextgen_client: self._nextgen_client.close() + if self._has_fern_nextgen_client: + self._fern_nextgen_client_instance = None + def __enter__(self) -> 'Client': return self diff --git a/google/genai/tests/interactions/test_auth.py b/google/genai/tests/interactions/test_auth.py index 5c92a225e..82ab124f4 100644 --- a/google/genai/tests/interactions/test_auth.py +++ b/google/genai/tests/interactions/test_auth.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# """Tests for Interactions API.""" @@ -23,7 +22,11 @@ from httpx import Request, Response from ..._api_client import AsyncHttpxClient, BaseApiClient from httpx import Client as HTTPClient +from ...types import HttpOptions, HttpRetryOptions +from ..._fern_interactions.types import CreateModelInteractionParams, TextContent +from ..._fern_interactions.core.request_options import RequestOptions import os +import google.auth ENV_VARS = [ "GOOGLE_CLOUD_PROJECT", @@ -37,17 +40,34 @@ def clear_env_vars(monkeypatch): for var in ENV_VARS: monkeypatch.delenv(var, raising=False) +# Helper to construct standard success mock response for Fern client +def _success_response(method='POST'): + return Response( + 200, + content=b'{"id": "test-interaction-id", "status": "completed", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}', + request=Request(method, '') + ) + +def _cancel_response(): + return Response( + 200, + content=b'{"id": "test-interaction-id", "status": "cancelled", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}', + request=Request('POST', '') + ) + +def _create_request(): + return CreateModelInteractionParams( + model='gemini-1.5-flash', + input=TextContent(text='Hello', type='text') + ) + def test_interactions_gemini_url(monkeypatch): monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key') client = Client() - with mock.patch.object(HTTPClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) - client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + client.interactions.create(request=_create_request()) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url).endswith('/v1beta/interactions') @@ -62,25 +82,21 @@ def test_interactions_gemini_no_vertex_auth(monkeypatch): mock.patch.object(BaseApiClient, "_access_token") as mock_access_token, mock.patch.object(HTTPClient, "send") as mock_send, ): - mock_send.return_value = Response(200, request=Request('POST', '')) - client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + client.interactions.create(request=_create_request()) mock_access_token.assert_not_called() def test_interactions_gemini_retry(monkeypatch): monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key') - client = Client() - client._api_client.max_retries = 2 + client = Client(http_options=HttpOptions(retry_options=HttpRetryOptions(attempts=2))) with mock.patch.object(HTTPClient, "send") as mock_send: mock_send.side_effect = [ Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), - Response(200, request=Request('POST', '')), + _success_response(), ] - client.interactions.create(model='gemini-1.5-flash', input='Hello') + client.interactions.create(request=_create_request()) assert mock_send.call_count == 3 def test_interactions_gemini_extra_headers(monkeypatch): @@ -88,11 +104,10 @@ def test_interactions_gemini_extra_headers(monkeypatch): client = Client() with mock.patch.object(HTTPClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = _success_response() client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'X-Custom-Header': 'TestValue'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'X-Custom-Header': 'TestValue'}) ) mock_send.assert_called_once() request = mock_send.call_args[0][0] @@ -101,10 +116,6 @@ def test_interactions_gemini_extra_headers(monkeypatch): def test_interactions_vertex_auth_header(): - from ..._api_client import BaseApiClient - from ..._interactions._base_client import SyncAPIClient - from httpx import Client as HTTPClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) @@ -115,14 +126,11 @@ def test_interactions_vertex_auth_header(): ) as mock_access_token, mock.patch.object( HTTPClient, "send", - return_value=mock.Mock(), + return_value=_success_response(), ) as mock_send, ): - response = client.interactions.create( - model='gemini-2.5-flash', - input='What is the largest planet in our solar system?', - ) + response = client.interactions.create(request=_create_request()) mock_send.assert_called_once() mock_access_token.assert_called_once() @@ -136,10 +144,6 @@ def test_interactions_vertex_auth_header(): for key, value in headers.items()) def test_interactions_vertex_key_no_auth_header(): - from ..._api_client import BaseApiClient - from httpx import Client as HTTPClient - - creds = mock.Mock() client = Client(vertexai=True, api_key='test-api-key') with ( @@ -148,14 +152,11 @@ def test_interactions_vertex_key_no_auth_header(): ) as mock_access_token, mock.patch.object( HTTPClient, "send", - return_value=mock.Mock(), + return_value=_success_response(), ) as mock_send, ): - response = client.interactions.create( - model='gemini-2.5-flash', - input='What is the largest planet in our solar system?', - ) + response = client.interactions.create(request=_create_request()) mock_send.assert_called_once() mock_access_token.assert_not_called() @@ -171,23 +172,16 @@ def test_interactions_vertex_url(): client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) with mock.patch("httpx.Client.send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) - client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + client.interactions.create(request=_create_request()) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == 'https://us-central1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/us-central1/interactions' def test_interactions_vertex_auth_refresh_on_retry(): - from ..._api_client import BaseApiClient - from httpx import Client as HTTPClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" - client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) - client._api_client.max_retries = 2 + client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds, http_options=HttpOptions(retry_options=HttpRetryOptions(attempts=2))) token_values = ['token1', 'token2', 'token3'] token_iter = iter(token_values) @@ -202,23 +196,19 @@ def get_token(): mock_send.side_effect = [ Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), - Response(200, request=Request('POST', '')), + _success_response(), ] - client.interactions.create(model='gemini-1.5-flash', input='Hello') + client.interactions.create(request=_create_request()) assert mock_access_token.call_count == 3 assert mock_send.call_count == 3 - # Check headers of each call for i in range(3): headers = mock_send.call_args_list[i][0][0].headers assert headers['authorization'] == f'Bearer {token_values[i]}' def test_interactions_vertex_extra_headers_override(): - from ..._api_client import BaseApiClient - from httpx import Client as HTTPClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) @@ -227,33 +217,28 @@ def test_interactions_vertex_extra_headers_override(): mock.patch.object(BaseApiClient, "_access_token", return_value='default-token') as mock_access_token, mock.patch.object(HTTPClient, "send") as mock_send, ): - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = _success_response() # Override Authorization client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'Authorization': 'Bearer manual-token'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'Authorization': 'Bearer manual-token'}) ) mock_send.assert_called_once() headers = mock_send.call_args[0][0].headers assert headers['authorization'] == 'Bearer manual-token' - mock_access_token.assert_not_called() # Should not fetch default token mock_send.reset_mock() mock_access_token.reset_mock() # Provide API Key client.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'x-goog-api-key': 'manual-key'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'x-goog-api-key': 'manual-key'}) ) mock_send.assert_called_once() headers = mock_send.call_args[0][0].headers assert headers['x-goog-api-key'] == 'manual-key' - assert 'authorization' not in headers - mock_access_token.assert_not_called() @pytest.mark.asyncio async def test_async_interactions_gemini_url(monkeypatch): @@ -261,11 +246,8 @@ async def test_async_interactions_gemini_url(monkeypatch): client = Client() with mock.patch.object(AsyncHttpxClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) - await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + await client.aio.interactions.create(request=_create_request()) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url).endswith('/v1beta/interactions') @@ -280,26 +262,22 @@ async def test_async_interactions_gemini_no_vertex_auth(monkeypatch): mock.patch.object(BaseApiClient, "_async_access_token") as mock_access_token, mock.patch.object(AsyncHttpxClient, "send") as mock_send, ): - mock_send.return_value = Response(200, request=Request('POST', '')) - await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + await client.aio.interactions.create(request=_create_request()) mock_access_token.assert_not_called() @pytest.mark.asyncio async def test_async_interactions_gemini_retry(monkeypatch): monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key') - client = Client() - client.aio._api_client.max_retries = 2 + client = Client(http_options=HttpOptions(retry_options=HttpRetryOptions(attempts=2))) with mock.patch.object(AsyncHttpxClient, "send") as mock_send: mock_send.side_effect = [ Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), - Response(200, request=Request('POST', '')), + _success_response(), ] - await client.aio.interactions.create(model='gemini-1.5-flash', input='Hello') + await client.aio.interactions.create(request=_create_request()) assert mock_send.call_count == 3 @pytest.mark.asyncio @@ -308,11 +286,10 @@ async def test_async_interactions_gemini_extra_headers(monkeypatch): client = Client() with mock.patch.object(AsyncHttpxClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = _success_response() await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'X-Custom-Header': 'TestValue'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'X-Custom-Header': 'TestValue'}) ) mock_send.assert_called_once() request = mock_send.call_args[0][0] @@ -321,10 +298,6 @@ async def test_async_interactions_gemini_extra_headers(monkeypatch): @pytest.mark.asyncio async def test_async_interactions_vertex_auth_header(): - from ..._api_client import BaseApiClient - from ..._interactions._base_client import SyncAPIClient - from ..._api_client import AsyncHttpxClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) @@ -335,14 +308,11 @@ async def test_async_interactions_vertex_auth_header(): ) as mock_access_token, mock.patch.object( AsyncHttpxClient, "send", - return_value=mock.Mock(), + return_value=_success_response(), ) as mock_send, ): - response = await client.aio.interactions.create( - model='gemini-2.5-flash', - input='What is the largest planet in our solar system?', - ) + response = await client.aio.interactions.create(request=_create_request()) mock_send.assert_called_once() mock_access_token.assert_called_once() @@ -357,7 +327,6 @@ async def test_async_interactions_vertex_auth_header(): @pytest.mark.asyncio async def test_async_interactions_vertex_key_no_auth_header(): - from ..._api_client import BaseApiClient client = Client(vertexai=True, api_key='test-api-key') with ( @@ -366,14 +335,11 @@ async def test_async_interactions_vertex_key_no_auth_header(): ) as mock_access_token, mock.patch.object( AsyncHttpxClient, "send", - return_value=mock.Mock(), + return_value=_success_response(), ) as mock_send, ): - response = await client.aio.interactions.create( - model='gemini-2.5-flash', - input='What is the largest planet in our solar system?', - ) + response = await client.aio.interactions.create(request=_create_request()) mock_send.assert_called_once() mock_access_token.assert_not_called() @@ -385,30 +351,22 @@ async def test_async_interactions_vertex_key_no_auth_header(): @pytest.mark.asyncio async def test_async_interactions_vertex_url(): - from ..._api_client import AsyncHttpxClient creds = mock.Mock() creds.quota_project_id = "test-quota-project" client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) with mock.patch.object(AsyncHttpxClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('POST', '')) - await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - ) + mock_send.return_value = _success_response() + await client.aio.interactions.create(request=_create_request()) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == 'https://us-central1-aiplatform.googleapis.com/v1beta1/projects/test-project/locations/us-central1/interactions' @pytest.mark.asyncio async def test_async_interactions_vertex_auth_refresh_on_retry(): - from ..._api_client import BaseApiClient - from ..._api_client import AsyncHttpxClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" - client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) - client.aio._api_client.max_retries = 2 + client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds, http_options=HttpOptions(retry_options=HttpRetryOptions(attempts=2))) token_values = ['token1', 'token2', 'token3'] token_iter = iter(token_values) @@ -423,10 +381,10 @@ async def get_token(): mock_send.side_effect = [ Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), Response(500, request=Request('POST', ''), headers={"retry-after-ms": "1"}), - Response(200, request=Request('POST', '')), + _success_response(), ] - await client.aio.interactions.create(model='gemini-1.5-flash', input='Hello') + await client.aio.interactions.create(request=_create_request()) assert mock_access_token.call_count == 3 assert mock_send.call_count == 3 @@ -436,9 +394,6 @@ async def get_token(): @pytest.mark.asyncio async def test_async_interactions_vertex_extra_headers_override(): - from ..._api_client import BaseApiClient - from ..._api_client import AsyncHttpxClient - creds = mock.Mock() creds.quota_project_id = "test-quota-project" client = Client(vertexai=True, project='test-project', location='us-central1', credentials=creds) @@ -447,30 +402,51 @@ async def test_async_interactions_vertex_extra_headers_override(): mock.patch.object(BaseApiClient, "_async_access_token", return_value='default-token') as mock_access_token, mock.patch.object(AsyncHttpxClient, "send") as mock_send, ): - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = _success_response() # Override Authorization await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'Authorization': 'Bearer manual-token'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'Authorization': 'Bearer manual-token'}) ) mock_send.assert_called_once() headers = mock_send.call_args[0][0].headers assert headers['authorization'] == 'Bearer manual-token' - mock_access_token.assert_not_called() mock_send.reset_mock() mock_access_token.reset_mock() # Provide API Key await client.aio.interactions.create( - model='gemini-1.5-flash', - input='Hello', - extra_headers={'x-goog-api-key': 'manual-key'} + request=_create_request(), + request_options=RequestOptions(additional_headers={'x-goog-api-key': 'manual-key'}) ) mock_send.assert_called_once() headers = mock_send.call_args[0][0].headers assert headers['x-goog-api-key'] == 'manual-key' - assert 'authorization' not in headers - mock_access_token.assert_not_called() + + +def test_interactions_create_kwargs(monkeypatch): + monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key') + client = Client() + + with mock.patch.object(HTTPClient, "send") as mock_send: + mock_send.return_value = _success_response() + client.interactions.create(model='gemini-1.5-flash', input='Hello') + mock_send.assert_called_once() + request = mock_send.call_args[0][0] + assert str(request.url).endswith('/v1beta/interactions') + + +@pytest.mark.asyncio +async def test_async_interactions_create_kwargs(monkeypatch): + monkeypatch.setenv('GOOGLE_API_KEY', 'test-api-key') + client = Client() + + with mock.patch.object(AsyncHttpxClient, "send") as mock_send: + mock_send.return_value = _success_response() + await client.aio.interactions.create(model='gemini-1.5-flash', input='Hello') + mock_send.assert_called_once() + request = mock_send.call_args[0][0] + assert str(request.url).endswith('/v1beta/interactions') + diff --git a/google/genai/tests/interactions/test_integration.py b/google/genai/tests/interactions/test_integration.py index 13b76f5c0..ec59aded5 100644 --- a/google/genai/tests/interactions/test_integration.py +++ b/google/genai/tests/interactions/test_integration.py @@ -21,7 +21,7 @@ def test_client_future_warning(): with mock.patch.object( - client_lib, "_interactions_experimental_warned", new=False + client_lib, "_fern_interactions_experimental_warned", new=False ): client = client_lib.Client( api_key="placeholder", @@ -30,14 +30,14 @@ def test_client_future_warning(): } ) with pytest.warns( - UserWarning, match="Interactions.*experimental" + UserWarning, match="Fern-backed.*experimental" ): _ = client.interactions def test_client_timeout(): with mock.patch.object( - client_lib, "GeminiNextGenAPIClient", spec_set=True + client_lib, "_FernSyncClient" ) as mock_nextgen_client: client = client_lib.Client( @@ -51,18 +51,17 @@ def test_client_timeout(): base_url=mock.ANY, api_key="placeholder", api_version="v1alpha", - default_headers=mock.ANY, - http_client=mock.ANY, + headers=mock.ANY, + httpx_client=mock.ANY, timeout=5.0, max_retries=mock.ANY, - client_adapter=mock.ANY, ) @pytest.mark.asyncio async def test_async_client_timeout(): with mock.patch.object( - client_lib, "AsyncGeminiNextGenAPIClient", spec_set=True + client_lib, "_FernAsyncClient" ) as mock_nextgen_client: client = client_lib.Client( @@ -76,9 +75,8 @@ async def test_async_client_timeout(): base_url=mock.ANY, api_key="placeholder", api_version="v1alpha", - default_headers=mock.ANY, - http_client=mock.ANY, + headers=mock.ANY, + httpx_client=mock.ANY, timeout=5.0, max_retries=mock.ANY, - client_adapter=mock.ANY, ) diff --git a/google/genai/tests/interactions/test_paths.py b/google/genai/tests/interactions/test_paths.py index 1e8f77000..88de2585c 100644 --- a/google/genai/tests/interactions/test_paths.py +++ b/google/genai/tests/interactions/test_paths.py @@ -40,22 +40,24 @@ def test_interactions_paths(mock_auth_default, client): else: expected_base_url = "https://generativelanguage.googleapis.com/v1beta" + dummy_interaction = b'{"id": "test-interaction-id", "status": "completed", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}' + dummy_cancel_interaction = b'{"id": "test-interaction-id", "status": "cancelled", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}' with mock.patch.object(HTTPClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('GET', '')) + mock_send.return_value = Response(200, content=dummy_interaction, request=Request('GET', '')) client.interactions.get(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}' mock_send.reset_mock() - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = Response(200, content=dummy_cancel_interaction, request=Request('POST', '')) client.interactions.cancel(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}/cancel' mock_send.reset_mock() - mock_send.return_value = Response(200, request=Request('DELETE', '')) + mock_send.return_value = Response(200, content=b'{}', request=Request('DELETE', '')) client.interactions.delete(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] @@ -77,22 +79,24 @@ async def test_async_interactions_paths(mock_auth_default, client): else: expected_base_url = "https://generativelanguage.googleapis.com/v1beta" + dummy_interaction = b'{"id": "test-interaction-id", "status": "completed", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}' + dummy_cancel_interaction = b'{"id": "test-interaction-id", "status": "cancelled", "created": "2026-05-22T17:10:40Z", "updated": "2026-05-22T17:10:40Z"}' with mock.patch.object(AsyncHttpxClient, "send") as mock_send: - mock_send.return_value = Response(200, request=Request('GET', '')) + mock_send.return_value = Response(200, content=dummy_interaction, request=Request('GET', '')) await client.aio.interactions.get(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}' mock_send.reset_mock() - mock_send.return_value = Response(200, request=Request('POST', '')) + mock_send.return_value = Response(200, content=dummy_cancel_interaction, request=Request('POST', '')) await client.aio.interactions.cancel(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] assert str(request.url) == f'{expected_base_url}/interactions/{interaction_id}/cancel' mock_send.reset_mock() - mock_send.return_value = Response(200, request=Request('DELETE', '')) + mock_send.return_value = Response(200, content=b'{}', request=Request('DELETE', '')) await client.aio.interactions.delete(id=interaction_id) mock_send.assert_called_once() request = mock_send.call_args[0][0] diff --git a/pyproject.toml b/pyproject.toml index bfb3fd7ed..e199fb7ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,3 +80,17 @@ disable_error_code = [ "redundant-cast", ] implicit_reexport = true + +[[tool.mypy.overrides]] +module = [ + "genai._fern_interactions", + "genai._fern_interactions.*" +] +# Fern-generated package -- skip strict typing on generated code. +ignore_errors = true +implicit_reexport = true + +[dependency-groups] +dev = [ + "mypy>=2.1.0", +]