diff --git a/.gitignore b/.gitignore
index 87797408..95ceb189 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
.prism.log
-.vscode
_dev
__pycache__
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 89d8ff81..9e93838e 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.2.1"
+ ".": "2.3.0-rc1"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 38d9f82d..9cd26179 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 32
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/writerai%2Fwriter-fab7a71148b6f413a4425ca9f2ce3d42557b65d35ab28c6f64daa7fce6d0ffe2.yml
-openapi_spec_hash: 0ead6944545bc40172176e15cc704633
-config_hash: c0c9f57ab19252f82cf765939edc61de
+configured_endpoints: 33
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/writerai%2Fwriter-0bbaae2a69204cb3ccf6dc4fbb0cef810a4e8c78f09ac639e253e84df0add53a.yml
+openapi_spec_hash: 0a9be554ca3af860e3831bd776e50f56
+config_hash: 7a38bab086b53b43d2a719cb4d883264
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..5b010307
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "python.analysis.importFormat": "relative",
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1bca812..371677e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,36 @@
# Changelog
+## 2.3.0-rc1 (2025-08-12)
+
+Full Changelog: [v2.2.1...v2.3.0-rc1](https://github.com/writer/writer-python/compare/v2.2.1...v2.3.0-rc1)
+
+### Features
+
+* **api:** add web KG and web search ([127319b](https://github.com/writer/writer-python/commit/127319bf155e738a86e344330e3b59a252ae5bd3))
+* **client:** support file upload requests ([aa0d06d](https://github.com/writer/writer-python/commit/aa0d06d2b73d9912a8542de146c8c0d0d0d150b2))
+
+
+### Bug Fixes
+
+* Add web_search_data to streaming tests. ([73f3db7](https://github.com/writer/writer-python/commit/73f3db7b36bde194d971afc2b16812b88e98e079))
+* **parsing:** ignore empty metadata ([8874173](https://github.com/writer/writer-python/commit/8874173e4d0d73c1209bfedee5bb29b5d720c9ec))
+* **parsing:** parse extra field types ([d1f5948](https://github.com/writer/writer-python/commit/d1f5948323742a54080dbd008532ce4be17289ba))
+
+
+### Chores
+
+* **internal:** fix ruff target version ([851522b](https://github.com/writer/writer-python/commit/851522bde50ed18925feb4d07191418be908e6ec))
+* **internal:** update comment in script ([279d7df](https://github.com/writer/writer-python/commit/279d7df957f5fd0b75fcd6a4bbae84e79f1f1288))
+* **project:** add settings file for vscode ([1c5e20c](https://github.com/writer/writer-python/commit/1c5e20ca653e671b71dc5b8e7d82db9a9dd2a51e))
+* update @stainless-api/prism-cli to v5.15.0 ([cdbd4b3](https://github.com/writer/writer-python/commit/cdbd4b3a5551b96ecda64be222ef9ac89e527794))
+
+
+### Documentation
+
+* **api:** updates to API spec ([4482f85](https://github.com/writer/writer-python/commit/4482f8521bf9e126dbe1f51e298efb1b74b2c100))
+* **api:** updates to API spec ([fe26f83](https://github.com/writer/writer-python/commit/fe26f8328201752961a0e1b7d3c418c357b0ccf7))
+* **api:** updates to API spec ([a780648](https://github.com/writer/writer-python/commit/a780648ff3063801cdb349ad5cc275139e7726f9))
+
## 2.2.1 (2025-07-16)
Full Changelog: [v2.2.0...v2.2.1](https://github.com/writer/writer-python/compare/v2.2.0...v2.2.1)
diff --git a/README.md b/README.md
index b0ae7de7..ce698aeb 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ To install the package from PyPI, use `pip`:
```sh
# install from PyPI
-pip install writer-sdk
+pip install --pre writer-sdk
```
## Prequisites
@@ -116,7 +116,7 @@ You can enable this by installing `aiohttp`:
```sh
# install from PyPI
-pip install writer-sdk[aiohttp]
+pip install --pre writer-sdk[aiohttp]
```
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
diff --git a/api.md b/api.md
index 5ebeffe3..d2641c43 100644
--- a/api.md
+++ b/api.md
@@ -123,7 +123,9 @@ from writerai.types import (
Question,
QuestionResponseChunk,
GraphCreateResponse,
+ GraphRetrieveResponse,
GraphUpdateResponse,
+ GraphListResponse,
GraphDeleteResponse,
GraphRemoveFileFromGraphResponse,
)
@@ -132,9 +134,9 @@ from writerai.types import (
Methods:
- client.graphs.create(\*\*params) -> GraphCreateResponse
-- client.graphs.retrieve(graph_id) -> Graph
+- client.graphs.retrieve(graph_id) -> GraphRetrieveResponse
- client.graphs.update(graph_id, \*\*params) -> GraphUpdateResponse
-- client.graphs.list(\*\*params) -> SyncCursorPage[Graph]
+- client.graphs.list(\*\*params) -> SyncCursorPage[GraphListResponse]
- client.graphs.delete(graph_id) -> GraphDeleteResponse
- client.graphs.add_file_to_graph(graph_id, \*\*params) -> File
- client.graphs.question(\*\*params) -> Question
@@ -166,6 +168,7 @@ from writerai.types import (
ToolAIDetectResponse,
ToolContextAwareSplittingResponse,
ToolParsePdfResponse,
+ ToolWebSearchResponse,
)
```
@@ -174,6 +177,7 @@ Methods:
- client.tools.ai_detect(\*\*params) -> ToolAIDetectResponse
- client.tools.context_aware_splitting(\*\*params) -> ToolContextAwareSplittingResponse
- client.tools.parse_pdf(file_id, \*\*params) -> ToolParsePdfResponse
+- client.tools.web_search(\*\*params) -> ToolWebSearchResponse
## Comprehend
diff --git a/pyproject.toml b/pyproject.toml
index c3cfa50d..8ad71678 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "writer-sdk"
-version = "2.2.1"
+version = "2.3.0-rc1"
description = "The official Python library for the writer API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -162,7 +162,7 @@ reportPrivateUsage = false
[tool.ruff]
line-length = 120
output-format = "grouped"
-target-version = "py37"
+target-version = "py38"
[tool.ruff.format]
docstring-code-format = true
diff --git a/scripts/mock b/scripts/mock
index d2814ae6..0b28f6ea 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}"
# Run prism mock on the given spec
if [ "$1" == "--daemon" ]; then
- npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log &
+ npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log &
# Wait for server to come online
echo -n "Waiting for server"
@@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then
echo
else
- npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL"
+ npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL"
fi
diff --git a/scripts/test b/scripts/test
index 2b878456..dbeda2d2 100755
--- a/scripts/test
+++ b/scripts/test
@@ -43,7 +43,7 @@ elif ! prism_is_running ; then
echo -e "To run the server, pass in the path or url of your OpenAPI"
echo -e "spec to the prism command:"
echo
- echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}"
+ echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}"
echo
exit 1
diff --git a/src/writerai/_base_client.py b/src/writerai/_base_client.py
index 768491b9..d1fb3996 100644
--- a/src/writerai/_base_client.py
+++ b/src/writerai/_base_client.py
@@ -534,7 +534,10 @@ def _build_request(
is_body_allowed = options.method.lower() != "get"
if is_body_allowed:
- kwargs["json"] = json_data if is_given(json_data) else None
+ if isinstance(json_data, bytes):
+ kwargs["content"] = json_data
+ else:
+ kwargs["json"] = json_data if is_given(json_data) else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
diff --git a/src/writerai/_files.py b/src/writerai/_files.py
index 9e6ac220..05dbb749 100644
--- a/src/writerai/_files.py
+++ b/src/writerai/_files.py
@@ -69,12 +69,12 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes:
return file
if is_tuple_t(file):
- return (file[0], _read_file_content(file[1]), *file[2:])
+ return (file[0], read_file_content(file[1]), *file[2:])
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
-def _read_file_content(file: FileContent) -> HttpxFileContent:
+def read_file_content(file: FileContent) -> HttpxFileContent:
if isinstance(file, os.PathLike):
return pathlib.Path(file).read_bytes()
return file
@@ -118,12 +118,12 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
return file
if is_tuple_t(file):
- return (file[0], await _async_read_file_content(file[1]), *file[2:])
+ return (file[0], await async_read_file_content(file[1]), *file[2:])
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
-async def _async_read_file_content(file: FileContent) -> HttpxFileContent:
+async def async_read_file_content(file: FileContent) -> HttpxFileContent:
if isinstance(file, os.PathLike):
return await anyio.Path(file).read_bytes()
diff --git a/src/writerai/_models.py b/src/writerai/_models.py
index 49331ca1..486b948c 100644
--- a/src/writerai/_models.py
+++ b/src/writerai/_models.py
@@ -209,14 +209,18 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
else:
fields_values[name] = field_get_default(field)
+ extra_field_type = _get_extra_fields_type(__cls)
+
_extra = {}
for key, value in values.items():
if key not in model_fields:
+ parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value
+
if PYDANTIC_V2:
- _extra[key] = value
+ _extra[key] = parsed
else:
_fields_set.add(key)
- fields_values[key] = value
+ fields_values[key] = parsed
object.__setattr__(m, "__dict__", fields_values)
@@ -371,6 +375,23 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None))
+def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None:
+ if not PYDANTIC_V2:
+ # TODO
+ return None
+
+ schema = cls.__pydantic_core_schema__
+ if schema["type"] == "model":
+ fields = schema["schema"]
+ if fields["type"] == "model-fields":
+ extras = fields.get("extras_schema")
+ if extras and "cls" in extras:
+ # mypy can't narrow the type
+ return extras["cls"] # type: ignore[no-any-return]
+
+ return None
+
+
def is_basemodel(type_: type) -> bool:
"""Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`"""
if is_union(type_):
@@ -440,7 +461,7 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]
type_ = type_.__value__ # type: ignore[unreachable]
# unwrap `Annotated[T, ...]` -> `T`
- if metadata is not None:
+ if metadata is not None and len(metadata) > 0:
meta: tuple[Any, ...] = tuple(metadata)
elif is_annotated_type(type_):
meta = get_args(type_)[1:]
diff --git a/src/writerai/_version.py b/src/writerai/_version.py
index 4fba2a61..7849f83a 100644
--- a/src/writerai/_version.py
+++ b/src/writerai/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "writerai"
-__version__ = "2.2.1" # x-release-please-version
+__version__ = "2.3.0-rc1" # x-release-please-version
diff --git a/src/writerai/resources/applications/graphs.py b/src/writerai/resources/applications/graphs.py
index e87e147a..9ab86c4e 100644
--- a/src/writerai/resources/applications/graphs.py
+++ b/src/writerai/resources/applications/graphs.py
@@ -56,7 +56,7 @@ def update(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ApplicationGraphsResponse:
"""
- Updates the Knowledge Graphs listed and associates them with the no-code agent.
+ Updates the list of Knowledge Graphs associated with a no-code chat agent.
Args:
graph_ids: A list of Knowledge Graph IDs to associate with the application. Note that this
@@ -150,7 +150,7 @@ async def update(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ApplicationGraphsResponse:
"""
- Updates the Knowledge Graphs listed and associates them with the no-code agent.
+ Updates the list of Knowledge Graphs associated with a no-code chat agent.
Args:
graph_ids: A list of Knowledge Graph IDs to associate with the application. Note that this
diff --git a/src/writerai/resources/chat.py b/src/writerai/resources/chat.py
index a497e894..c6b0b0a5 100644
--- a/src/writerai/resources/chat.py
+++ b/src/writerai/resources/chat.py
@@ -86,7 +86,7 @@ def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -99,8 +99,10 @@ def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -127,17 +129,26 @@ def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
@@ -182,7 +193,7 @@ def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -199,8 +210,10 @@ def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -223,17 +236,26 @@ def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
@@ -278,7 +300,7 @@ def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -295,8 +317,10 @@ def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -319,17 +343,26 @@ def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
@@ -627,7 +660,7 @@ async def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -640,8 +673,10 @@ async def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -668,17 +703,26 @@ async def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
@@ -723,7 +767,7 @@ async def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -740,8 +784,10 @@ async def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -764,17 +810,26 @@ async def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
@@ -819,7 +874,7 @@ async def chat(
The response shown
below is for non-streaming. To learn about streaming responses, see the
- [chat completion guide](https://dev.writer.com/api-guides/chat-completion).
+ [chat completion guide](https://dev.writer.com/home/chat-completion).
Args:
messages: An array of message objects that form the conversation history or context for
@@ -836,8 +891,10 @@ async def chat(
logprobs: Specifies whether to return log probabilities of the output tokens.
max_tokens: Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
n: Specifies the number of completions (responses) to generate from the model in a
single request. This parameter allows for generating multiple responses,
@@ -860,17 +917,26 @@ async def chat(
temperature results in more varied and less predictable text, while a lower
temperature produces more deterministic and conservative outputs.
- tool_choice: Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ tool_choice:
+ Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
tools: An array containing tool definitions for tools that the model can use to
generate responses. The tool definitions use JSON schema. You can define your
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
top_p: Sets the threshold for "nucleus sampling," a technique to focus the model's
token generation on the most likely subset of tokens. Only tokens with
diff --git a/src/writerai/resources/completions.py b/src/writerai/resources/completions.py
index 4a1c971e..081ec326 100644
--- a/src/writerai/resources/completions.py
+++ b/src/writerai/resources/completions.py
@@ -66,8 +66,10 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> Completion:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
@@ -129,8 +131,10 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> Stream[CompletionChunk]:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
@@ -192,8 +196,10 @@ def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> Completion | Stream[CompletionChunk]:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
@@ -322,8 +328,10 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> Completion:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
@@ -385,8 +393,10 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncStream[CompletionChunk]:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
@@ -448,8 +458,10 @@ async def create(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> Completion | AsyncStream[CompletionChunk]:
- """
- Text generation
+ """Generate text completions using the specified model and prompt.
+
+ This endpoint is
+ useful for text generation tasks that don't require conversational context.
Args:
model: The [ID of the model](https://dev.writer.com/home/models) to use for generating
diff --git a/src/writerai/resources/files.py b/src/writerai/resources/files.py
index 73b45fab..f77c4709 100644
--- a/src/writerai/resources/files.py
+++ b/src/writerai/resources/files.py
@@ -67,7 +67,8 @@ def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> File:
"""
- Retrieve file
+ Retrieve detailed information about a specific file, including its metadata,
+ status, and associated graphs.
Args:
extra_headers: Send extra headers
@@ -105,12 +106,12 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> SyncCursorPage[File]:
- """List files
+ """
+ Retrieve a paginated list of files with optional filtering by status, graph
+ association, and file type.
Args:
- after: The ID of the last object in the previous page.
-
- This parameter instructs the API
+ after: The ID of the last object in the previous page. This parameter instructs the API
to return the next page of results.
before: The ID of the first object in the previous page. This parameter instructs the
@@ -173,8 +174,9 @@ def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> FileDeleteResponse:
- """
- Delete file
+ """Permanently delete a file from the system.
+
+ This action cannot be undone.
Args:
extra_headers: Send extra headers
@@ -206,8 +208,10 @@ def download(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> BinaryAPIResponse:
- """
- Download file
+ """Download the binary content of a file.
+
+ The response will contain the file data
+ in the appropriate MIME type.
Args:
extra_headers: Send extra headers
@@ -240,8 +244,10 @@ def retry(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> FileRetryResponse:
- """
- Retry failed files
+ """Retry processing of files that previously failed to process.
+
+ This will
+ re-attempt the processing of the specified files.
Args:
file_ids: The unique identifier of the files to retry.
@@ -276,8 +282,10 @@ def upload(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> File:
- """
- Upload file
+ """Upload a new file to the system.
+
+ Supports various file formats including PDF,
+ DOC, DOCX, PPT, PPTX, JPG, PNG, EML, HTML, SRT, CSV, XLS, and XLSX.
Args:
extra_headers: Send extra headers
@@ -335,7 +343,8 @@ async def retrieve(
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> File:
"""
- Retrieve file
+ Retrieve detailed information about a specific file, including its metadata,
+ status, and associated graphs.
Args:
extra_headers: Send extra headers
@@ -373,12 +382,12 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncPaginator[File, AsyncCursorPage[File]]:
- """List files
+ """
+ Retrieve a paginated list of files with optional filtering by status, graph
+ association, and file type.
Args:
- after: The ID of the last object in the previous page.
-
- This parameter instructs the API
+ after: The ID of the last object in the previous page. This parameter instructs the API
to return the next page of results.
before: The ID of the first object in the previous page. This parameter instructs the
@@ -441,8 +450,9 @@ async def delete(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> FileDeleteResponse:
- """
- Delete file
+ """Permanently delete a file from the system.
+
+ This action cannot be undone.
Args:
extra_headers: Send extra headers
@@ -474,8 +484,10 @@ async def download(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> AsyncBinaryAPIResponse:
- """
- Download file
+ """Download the binary content of a file.
+
+ The response will contain the file data
+ in the appropriate MIME type.
Args:
extra_headers: Send extra headers
@@ -508,8 +520,10 @@ async def retry(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> FileRetryResponse:
- """
- Retry failed files
+ """Retry processing of files that previously failed to process.
+
+ This will
+ re-attempt the processing of the specified files.
Args:
file_ids: The unique identifier of the files to retry.
@@ -544,8 +558,10 @@ async def upload(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> File:
- """
- Upload file
+ """Upload a new file to the system.
+
+ Supports various file formats including PDF,
+ DOC, DOCX, PPT, PPTX, JPG, PNG, EML, HTML, SRT, CSV, XLS, and XLSX.
Args:
extra_headers: Send extra headers
diff --git a/src/writerai/resources/graphs.py b/src/writerai/resources/graphs.py
index 8af1c882..3d806def 100644
--- a/src/writerai/resources/graphs.py
+++ b/src/writerai/resources/graphs.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import List
+from typing import List, Iterable
from typing_extensions import Literal, overload
import httpx
@@ -27,12 +27,13 @@
from .._streaming import Stream, AsyncStream
from ..pagination import SyncCursorPage, AsyncCursorPage
from ..types.file import File
-from ..types.graph import Graph
from .._base_client import AsyncPaginator, make_request_options
from ..types.question import Question
+from ..types.graph_list_response import GraphListResponse
from ..types.graph_create_response import GraphCreateResponse
from ..types.graph_delete_response import GraphDeleteResponse
from ..types.graph_update_response import GraphUpdateResponse
+from ..types.graph_retrieve_response import GraphRetrieveResponse
from ..types.question_response_chunk import QuestionResponseChunk
from ..types.graph_remove_file_from_graph_response import GraphRemoveFileFromGraphResponse
@@ -114,7 +115,7 @@ def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Graph:
+ ) -> GraphRetrieveResponse:
"""
Retrieve a Knowledge Graph.
@@ -134,7 +135,7 @@ def retrieve(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Graph,
+ cast_to=GraphRetrieveResponse,
)
def update(
@@ -143,6 +144,7 @@ def update(
*,
description: str | NotGiven = NOT_GIVEN,
name: str | NotGiven = NOT_GIVEN,
+ urls: Iterable[graph_update_params.URL] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -160,6 +162,10 @@ def update(
name: The name of the Knowledge Graph (max 255 characters). Omitting this field leaves
the name unchanged.
+ urls: An array of web connector URLs to update for this Knowledge Graph. You can only
+ connect URLs to Knowledge Graphs with the type `web`. To clear the list of URLs,
+ set this field to an empty array.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -176,6 +182,7 @@ def update(
{
"description": description,
"name": name,
+ "urls": urls,
},
graph_update_params.GraphUpdateParams,
),
@@ -198,7 +205,7 @@ def list(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> SyncCursorPage[Graph]:
+ ) -> SyncCursorPage[GraphListResponse]:
"""
Retrieve a list of Knowledge Graphs.
@@ -225,7 +232,7 @@ def list(
"""
return self._get_api_list(
"/v1/graphs",
- page=SyncCursorPage[Graph],
+ page=SyncCursorPage[GraphListResponse],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -241,7 +248,7 @@ def list(
graph_list_params.GraphListParams,
),
),
- model=Graph,
+ model=GraphListResponse,
)
def delete(
@@ -613,7 +620,7 @@ async def retrieve(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> Graph:
+ ) -> GraphRetrieveResponse:
"""
Retrieve a Knowledge Graph.
@@ -633,7 +640,7 @@ async def retrieve(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=Graph,
+ cast_to=GraphRetrieveResponse,
)
async def update(
@@ -642,6 +649,7 @@ async def update(
*,
description: str | NotGiven = NOT_GIVEN,
name: str | NotGiven = NOT_GIVEN,
+ urls: Iterable[graph_update_params.URL] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
@@ -659,6 +667,10 @@ async def update(
name: The name of the Knowledge Graph (max 255 characters). Omitting this field leaves
the name unchanged.
+ urls: An array of web connector URLs to update for this Knowledge Graph. You can only
+ connect URLs to Knowledge Graphs with the type `web`. To clear the list of URLs,
+ set this field to an empty array.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -675,6 +687,7 @@ async def update(
{
"description": description,
"name": name,
+ "urls": urls,
},
graph_update_params.GraphUpdateParams,
),
@@ -697,7 +710,7 @@ def list(
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> AsyncPaginator[Graph, AsyncCursorPage[Graph]]:
+ ) -> AsyncPaginator[GraphListResponse, AsyncCursorPage[GraphListResponse]]:
"""
Retrieve a list of Knowledge Graphs.
@@ -724,7 +737,7 @@ def list(
"""
return self._get_api_list(
"/v1/graphs",
- page=AsyncCursorPage[Graph],
+ page=AsyncCursorPage[GraphListResponse],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -740,7 +753,7 @@ def list(
graph_list_params.GraphListParams,
),
),
- model=Graph,
+ model=GraphListResponse,
)
async def delete(
diff --git a/src/writerai/resources/models.py b/src/writerai/resources/models.py
index 6714725e..b7515656 100644
--- a/src/writerai/resources/models.py
+++ b/src/writerai/resources/models.py
@@ -49,7 +49,10 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ModelListResponse:
- """List models"""
+ """
+ Retrieve a list of available models that can be used for text generation, chat
+ completions, and other AI tasks.
+ """
return self._get(
"/v1/models",
options=make_request_options(
@@ -89,7 +92,10 @@ async def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> ModelListResponse:
- """List models"""
+ """
+ Retrieve a list of available models that can be used for text generation, chat
+ completions, and other AI tasks.
+ """
return await self._get(
"/v1/models",
options=make_request_options(
diff --git a/src/writerai/resources/tools/tools.py b/src/writerai/resources/tools/tools.py
index 132ea276..cab8fb75 100644
--- a/src/writerai/resources/tools/tools.py
+++ b/src/writerai/resources/tools/tools.py
@@ -2,11 +2,17 @@
from __future__ import annotations
+from typing import List, Union
from typing_extensions import Literal
import httpx
-from ...types import tool_ai_detect_params, tool_parse_pdf_params, tool_context_aware_splitting_params
+from ...types import (
+ tool_ai_detect_params,
+ tool_parse_pdf_params,
+ tool_web_search_params,
+ tool_context_aware_splitting_params,
+)
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
@@ -28,6 +34,7 @@
from ..._base_client import make_request_options
from ...types.tool_ai_detect_response import ToolAIDetectResponse
from ...types.tool_parse_pdf_response import ToolParsePdfResponse
+from ...types.tool_web_search_response import ToolWebSearchResponse
from ...types.tool_context_aware_splitting_response import ToolContextAwareSplittingResponse
__all__ = ["ToolsResource", "AsyncToolsResource"]
@@ -177,6 +184,279 @@ def parse_pdf(
cast_to=ToolParsePdfResponse,
)
+ def web_search(
+ self,
+ *,
+ chunks_per_source: int | NotGiven = NOT_GIVEN,
+ country: Literal[
+ "afghanistan",
+ "albania",
+ "algeria",
+ "andorra",
+ "angola",
+ "argentina",
+ "armenia",
+ "australia",
+ "austria",
+ "azerbaijan",
+ "bahamas",
+ "bahrain",
+ "bangladesh",
+ "barbados",
+ "belarus",
+ "belgium",
+ "belize",
+ "benin",
+ "bhutan",
+ "bolivia",
+ "bosnia and herzegovina",
+ "botswana",
+ "brazil",
+ "brunei",
+ "bulgaria",
+ "burkina faso",
+ "burundi",
+ "cambodia",
+ "cameroon",
+ "canada",
+ "cape verde",
+ "central african republic",
+ "chad",
+ "chile",
+ "china",
+ "colombia",
+ "comoros",
+ "congo",
+ "costa rica",
+ "croatia",
+ "cuba",
+ "cyprus",
+ "czech republic",
+ "denmark",
+ "djibouti",
+ "dominican republic",
+ "ecuador",
+ "egypt",
+ "el salvador",
+ "equatorial guinea",
+ "eritrea",
+ "estonia",
+ "ethiopia",
+ "fiji",
+ "finland",
+ "france",
+ "gabon",
+ "gambia",
+ "georgia",
+ "germany",
+ "ghana",
+ "greece",
+ "guatemala",
+ "guinea",
+ "haiti",
+ "honduras",
+ "hungary",
+ "iceland",
+ "india",
+ "indonesia",
+ "iran",
+ "iraq",
+ "ireland",
+ "israel",
+ "italy",
+ "jamaica",
+ "japan",
+ "jordan",
+ "kazakhstan",
+ "kenya",
+ "kuwait",
+ "kyrgyzstan",
+ "latvia",
+ "lebanon",
+ "lesotho",
+ "liberia",
+ "libya",
+ "liechtenstein",
+ "lithuania",
+ "luxembourg",
+ "madagascar",
+ "malawi",
+ "malaysia",
+ "maldives",
+ "mali",
+ "malta",
+ "mauritania",
+ "mauritius",
+ "mexico",
+ "moldova",
+ "monaco",
+ "mongolia",
+ "montenegro",
+ "morocco",
+ "mozambique",
+ "myanmar",
+ "namibia",
+ "nepal",
+ "netherlands",
+ "new zealand",
+ "nicaragua",
+ "niger",
+ "nigeria",
+ "north korea",
+ "north macedonia",
+ "norway",
+ "oman",
+ "pakistan",
+ "panama",
+ "papua new guinea",
+ "paraguay",
+ "peru",
+ "philippines",
+ "poland",
+ "portugal",
+ "qatar",
+ "romania",
+ "russia",
+ "rwanda",
+ "saudi arabia",
+ "senegal",
+ "serbia",
+ "singapore",
+ "slovakia",
+ "slovenia",
+ "somalia",
+ "south africa",
+ "south korea",
+ "south sudan",
+ "spain",
+ "sri lanka",
+ "sudan",
+ "sweden",
+ "switzerland",
+ "syria",
+ "taiwan",
+ "tajikistan",
+ "tanzania",
+ "thailand",
+ "togo",
+ "trinidad and tobago",
+ "tunisia",
+ "turkey",
+ "turkmenistan",
+ "uganda",
+ "ukraine",
+ "united arab emirates",
+ "united kingdom",
+ "united states",
+ "uruguay",
+ "uzbekistan",
+ "venezuela",
+ "vietnam",
+ "yemen",
+ "zambia",
+ "zimbabwe",
+ ]
+ | NotGiven = NOT_GIVEN,
+ days: int | NotGiven = NOT_GIVEN,
+ exclude_domains: List[str] | NotGiven = NOT_GIVEN,
+ include_answer: bool | NotGiven = NOT_GIVEN,
+ include_domains: List[str] | NotGiven = NOT_GIVEN,
+ include_raw_content: Union[Literal["text", "markdown"], bool] | NotGiven = NOT_GIVEN,
+ max_results: int | NotGiven = NOT_GIVEN,
+ query: str | NotGiven = NOT_GIVEN,
+ search_depth: Literal["basic", "advanced"] | NotGiven = NOT_GIVEN,
+ stream: bool | NotGiven = NOT_GIVEN,
+ time_range: Literal["day", "week", "month", "year", "d", "w", "m", "y"] | NotGiven = NOT_GIVEN,
+ topic: Literal["general", "news"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ToolWebSearchResponse:
+ """
+ Search the web for information about a given query and return relevant results
+ with source URLs.
+
+ Args:
+ chunks_per_source: Only applies when `search_depth` is `advanced`. Specifies how many text segments
+ to extract from each source. Limited to 3 chunks maximum.
+
+ country: Localizes search results to a specific country. Only applies to general topic
+ searches.
+
+ days: For news topic searches, specifies how many days of news coverage to include.
+
+ exclude_domains: Domains to exclude from the search. If unset, the search includes all domains.
+
+ include_answer: Whether to include a generated answer to the query in the response. If `false`,
+ only search results are returned.
+
+ include_domains: Domains to include in the search. If unset, the search includes all domains.
+
+ include_raw_content:
+ Controls how raw content is included in search results:
+
+ - `text`: Returns plain text without formatting markup
+ - `markdown`: Returns structured content with markdown formatting (headers,
+ links, bold text)
+ - `true`: Same as `markdown`
+ - `false`: Raw content is not included (default if unset)
+
+ max_results: Limits the number of search results returned. Cannot exceed 20 sources.
+
+ query: The search query.
+
+ search_depth:
+ Controls search comprehensiveness:
+
+ - `basic`: Returns fewer but highly relevant results
+ - `advanced`: Performs a deeper search with more results
+
+ stream: Enables streaming of search results as they become available.
+
+ time_range: Filters results to content published within the specified time range back from
+ the current date. For example, `week` or `w` returns results from the past 7
+ days.
+
+ topic: The search topic category. Use `news` for current events and news articles, or
+ `general` for broader web search.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return self._post(
+ "/v1/tools/web-search",
+ body=maybe_transform(
+ {
+ "chunks_per_source": chunks_per_source,
+ "country": country,
+ "days": days,
+ "exclude_domains": exclude_domains,
+ "include_answer": include_answer,
+ "include_domains": include_domains,
+ "include_raw_content": include_raw_content,
+ "max_results": max_results,
+ "query": query,
+ "search_depth": search_depth,
+ "stream": stream,
+ "time_range": time_range,
+ "topic": topic,
+ },
+ tool_web_search_params.ToolWebSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ToolWebSearchResponse,
+ )
+
class AsyncToolsResource(AsyncAPIResource):
@cached_property
@@ -322,6 +602,279 @@ async def parse_pdf(
cast_to=ToolParsePdfResponse,
)
+ async def web_search(
+ self,
+ *,
+ chunks_per_source: int | NotGiven = NOT_GIVEN,
+ country: Literal[
+ "afghanistan",
+ "albania",
+ "algeria",
+ "andorra",
+ "angola",
+ "argentina",
+ "armenia",
+ "australia",
+ "austria",
+ "azerbaijan",
+ "bahamas",
+ "bahrain",
+ "bangladesh",
+ "barbados",
+ "belarus",
+ "belgium",
+ "belize",
+ "benin",
+ "bhutan",
+ "bolivia",
+ "bosnia and herzegovina",
+ "botswana",
+ "brazil",
+ "brunei",
+ "bulgaria",
+ "burkina faso",
+ "burundi",
+ "cambodia",
+ "cameroon",
+ "canada",
+ "cape verde",
+ "central african republic",
+ "chad",
+ "chile",
+ "china",
+ "colombia",
+ "comoros",
+ "congo",
+ "costa rica",
+ "croatia",
+ "cuba",
+ "cyprus",
+ "czech republic",
+ "denmark",
+ "djibouti",
+ "dominican republic",
+ "ecuador",
+ "egypt",
+ "el salvador",
+ "equatorial guinea",
+ "eritrea",
+ "estonia",
+ "ethiopia",
+ "fiji",
+ "finland",
+ "france",
+ "gabon",
+ "gambia",
+ "georgia",
+ "germany",
+ "ghana",
+ "greece",
+ "guatemala",
+ "guinea",
+ "haiti",
+ "honduras",
+ "hungary",
+ "iceland",
+ "india",
+ "indonesia",
+ "iran",
+ "iraq",
+ "ireland",
+ "israel",
+ "italy",
+ "jamaica",
+ "japan",
+ "jordan",
+ "kazakhstan",
+ "kenya",
+ "kuwait",
+ "kyrgyzstan",
+ "latvia",
+ "lebanon",
+ "lesotho",
+ "liberia",
+ "libya",
+ "liechtenstein",
+ "lithuania",
+ "luxembourg",
+ "madagascar",
+ "malawi",
+ "malaysia",
+ "maldives",
+ "mali",
+ "malta",
+ "mauritania",
+ "mauritius",
+ "mexico",
+ "moldova",
+ "monaco",
+ "mongolia",
+ "montenegro",
+ "morocco",
+ "mozambique",
+ "myanmar",
+ "namibia",
+ "nepal",
+ "netherlands",
+ "new zealand",
+ "nicaragua",
+ "niger",
+ "nigeria",
+ "north korea",
+ "north macedonia",
+ "norway",
+ "oman",
+ "pakistan",
+ "panama",
+ "papua new guinea",
+ "paraguay",
+ "peru",
+ "philippines",
+ "poland",
+ "portugal",
+ "qatar",
+ "romania",
+ "russia",
+ "rwanda",
+ "saudi arabia",
+ "senegal",
+ "serbia",
+ "singapore",
+ "slovakia",
+ "slovenia",
+ "somalia",
+ "south africa",
+ "south korea",
+ "south sudan",
+ "spain",
+ "sri lanka",
+ "sudan",
+ "sweden",
+ "switzerland",
+ "syria",
+ "taiwan",
+ "tajikistan",
+ "tanzania",
+ "thailand",
+ "togo",
+ "trinidad and tobago",
+ "tunisia",
+ "turkey",
+ "turkmenistan",
+ "uganda",
+ "ukraine",
+ "united arab emirates",
+ "united kingdom",
+ "united states",
+ "uruguay",
+ "uzbekistan",
+ "venezuela",
+ "vietnam",
+ "yemen",
+ "zambia",
+ "zimbabwe",
+ ]
+ | NotGiven = NOT_GIVEN,
+ days: int | NotGiven = NOT_GIVEN,
+ exclude_domains: List[str] | NotGiven = NOT_GIVEN,
+ include_answer: bool | NotGiven = NOT_GIVEN,
+ include_domains: List[str] | NotGiven = NOT_GIVEN,
+ include_raw_content: Union[Literal["text", "markdown"], bool] | NotGiven = NOT_GIVEN,
+ max_results: int | NotGiven = NOT_GIVEN,
+ query: str | NotGiven = NOT_GIVEN,
+ search_depth: Literal["basic", "advanced"] | NotGiven = NOT_GIVEN,
+ stream: bool | NotGiven = NOT_GIVEN,
+ time_range: Literal["day", "week", "month", "year", "d", "w", "m", "y"] | NotGiven = NOT_GIVEN,
+ topic: Literal["general", "news"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ToolWebSearchResponse:
+ """
+ Search the web for information about a given query and return relevant results
+ with source URLs.
+
+ Args:
+ chunks_per_source: Only applies when `search_depth` is `advanced`. Specifies how many text segments
+ to extract from each source. Limited to 3 chunks maximum.
+
+ country: Localizes search results to a specific country. Only applies to general topic
+ searches.
+
+ days: For news topic searches, specifies how many days of news coverage to include.
+
+ exclude_domains: Domains to exclude from the search. If unset, the search includes all domains.
+
+ include_answer: Whether to include a generated answer to the query in the response. If `false`,
+ only search results are returned.
+
+ include_domains: Domains to include in the search. If unset, the search includes all domains.
+
+ include_raw_content:
+ Controls how raw content is included in search results:
+
+ - `text`: Returns plain text without formatting markup
+ - `markdown`: Returns structured content with markdown formatting (headers,
+ links, bold text)
+ - `true`: Same as `markdown`
+ - `false`: Raw content is not included (default if unset)
+
+ max_results: Limits the number of search results returned. Cannot exceed 20 sources.
+
+ query: The search query.
+
+ search_depth:
+ Controls search comprehensiveness:
+
+ - `basic`: Returns fewer but highly relevant results
+ - `advanced`: Performs a deeper search with more results
+
+ stream: Enables streaming of search results as they become available.
+
+ time_range: Filters results to content published within the specified time range back from
+ the current date. For example, `week` or `w` returns results from the past 7
+ days.
+
+ topic: The search topic category. Use `news` for current events and news articles, or
+ `general` for broader web search.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ return await self._post(
+ "/v1/tools/web-search",
+ body=await async_maybe_transform(
+ {
+ "chunks_per_source": chunks_per_source,
+ "country": country,
+ "days": days,
+ "exclude_domains": exclude_domains,
+ "include_answer": include_answer,
+ "include_domains": include_domains,
+ "include_raw_content": include_raw_content,
+ "max_results": max_results,
+ "query": query,
+ "search_depth": search_depth,
+ "stream": stream,
+ "time_range": time_range,
+ "topic": topic,
+ },
+ tool_web_search_params.ToolWebSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ToolWebSearchResponse,
+ )
+
class ToolsResourceWithRawResponse:
def __init__(self, tools: ToolsResource) -> None:
@@ -336,6 +889,9 @@ def __init__(self, tools: ToolsResource) -> None:
self.parse_pdf = to_raw_response_wrapper(
tools.parse_pdf,
)
+ self.web_search = to_raw_response_wrapper(
+ tools.web_search,
+ )
@cached_property
def comprehend(self) -> ComprehendResourceWithRawResponse:
@@ -355,6 +911,9 @@ def __init__(self, tools: AsyncToolsResource) -> None:
self.parse_pdf = async_to_raw_response_wrapper(
tools.parse_pdf,
)
+ self.web_search = async_to_raw_response_wrapper(
+ tools.web_search,
+ )
@cached_property
def comprehend(self) -> AsyncComprehendResourceWithRawResponse:
@@ -374,6 +933,9 @@ def __init__(self, tools: ToolsResource) -> None:
self.parse_pdf = to_streamed_response_wrapper(
tools.parse_pdf,
)
+ self.web_search = to_streamed_response_wrapper(
+ tools.web_search,
+ )
@cached_property
def comprehend(self) -> ComprehendResourceWithStreamingResponse:
@@ -393,6 +955,9 @@ def __init__(self, tools: AsyncToolsResource) -> None:
self.parse_pdf = async_to_streamed_response_wrapper(
tools.parse_pdf,
)
+ self.web_search = async_to_streamed_response_wrapper(
+ tools.web_search,
+ )
@cached_property
def comprehend(self) -> AsyncComprehendResourceWithStreamingResponse:
diff --git a/src/writerai/resources/translation.py b/src/writerai/resources/translation.py
index f342bb6c..235ce8d8 100644
--- a/src/writerai/resources/translation.py
+++ b/src/writerai/resources/translation.py
@@ -65,15 +65,15 @@ def translate(
Args:
formality: Whether to use formal or informal language in the translation. See the
- [list of languages that support formality](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#formality).
+ [list of languages that support formality](https://dev.writer.com/api-reference/translation-api/language-support#formality).
If the language does not support formality, this parameter is ignored.
length_control: Whether to control the length of the translated text. See the
- [list of languages that support length control](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#length-control).
+ [list of languages that support length control](https://dev.writer.com/api-reference/translation-api/language-support#length-control).
If the language does not support length control, this parameter is ignored.
mask_profanity: Whether to mask profane words in the translated text. See the
- [list of languages that do not support profanity masking](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#profanity-masking).
+ [list of languages that do not support profanity masking](https://dev.writer.com/api-reference/translation-api/language-support#profanity-masking).
If the language does not support profanity masking, this parameter is ignored.
model: The model to use for translation.
@@ -84,7 +84,7 @@ def translate(
variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
target_language_code: The [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes)
language code of the target language for the translation. For example, `en` for
@@ -92,7 +92,7 @@ def translate(
has a variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
text: The text to translate. Maximum of 100,000 words.
@@ -167,15 +167,15 @@ async def translate(
Args:
formality: Whether to use formal or informal language in the translation. See the
- [list of languages that support formality](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#formality).
+ [list of languages that support formality](https://dev.writer.com/api-reference/translation-api/language-support#formality).
If the language does not support formality, this parameter is ignored.
length_control: Whether to control the length of the translated text. See the
- [list of languages that support length control](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#length-control).
+ [list of languages that support length control](https://dev.writer.com/api-reference/translation-api/language-support#length-control).
If the language does not support length control, this parameter is ignored.
mask_profanity: Whether to mask profane words in the translated text. See the
- [list of languages that do not support profanity masking](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#profanity-masking).
+ [list of languages that do not support profanity masking](https://dev.writer.com/api-reference/translation-api/language-support#profanity-masking).
If the language does not support profanity masking, this parameter is ignored.
model: The model to use for translation.
@@ -186,7 +186,7 @@ async def translate(
variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
target_language_code: The [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes)
language code of the target language for the translation. For example, `en` for
@@ -194,7 +194,7 @@ async def translate(
has a variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
text: The text to translate. Maximum of 100,000 words.
diff --git a/src/writerai/types/__init__.py b/src/writerai/types/__init__.py
index 16b8f6ef..84a63a36 100644
--- a/src/writerai/types/__init__.py
+++ b/src/writerai/types/__init__.py
@@ -3,7 +3,6 @@
from __future__ import annotations
from .file import File as File
-from .graph import Graph as Graph
from .shared import (
Source as Source,
Logprobs as Logprobs,
@@ -31,6 +30,7 @@
from .file_upload_params import FileUploadParams as FileUploadParams
from .file_retry_response import FileRetryResponse as FileRetryResponse
from .graph_create_params import GraphCreateParams as GraphCreateParams
+from .graph_list_response import GraphListResponse as GraphListResponse
from .graph_update_params import GraphUpdateParams as GraphUpdateParams
from .model_list_response import ModelListResponse as ModelListResponse
from .file_delete_response import FileDeleteResponse as FileDeleteResponse
@@ -45,12 +45,15 @@
from .tool_parse_pdf_params import ToolParsePdfParams as ToolParsePdfParams
from .vision_analyze_params import VisionAnalyzeParams as VisionAnalyzeParams
from .chat_completion_choice import ChatCompletionChoice as ChatCompletionChoice
+from .tool_web_search_params import ToolWebSearchParams as ToolWebSearchParams
from .application_list_params import ApplicationListParams as ApplicationListParams
from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage
+from .graph_retrieve_response import GraphRetrieveResponse as GraphRetrieveResponse
from .question_response_chunk import QuestionResponseChunk as QuestionResponseChunk
from .tool_ai_detect_response import ToolAIDetectResponse as ToolAIDetectResponse
from .tool_parse_pdf_response import ToolParsePdfResponse as ToolParsePdfResponse
from .completion_create_params import CompletionCreateParams as CompletionCreateParams
+from .tool_web_search_response import ToolWebSearchResponse as ToolWebSearchResponse
from .application_list_response import ApplicationListResponse as ApplicationListResponse
from .translation_translate_params import TranslationTranslateParams as TranslationTranslateParams
from .application_retrieve_response import ApplicationRetrieveResponse as ApplicationRetrieveResponse
diff --git a/src/writerai/types/application_generate_content_params.py b/src/writerai/types/application_generate_content_params.py
index 56b36867..bfd7011c 100644
--- a/src/writerai/types/application_generate_content_params.py
+++ b/src/writerai/types/application_generate_content_params.py
@@ -31,9 +31,9 @@ class Input(TypedDict, total=False):
If the input type is "File upload", you must pass the `file_id` of an uploaded
file. You cannot pass a file object directly. See the
- [file upload endpoint](https://dev.writer.com/api-guides/api-reference/file-api/upload-files)
+ [file upload endpoint](https://dev.writer.com/api-reference/file-api/upload-files)
for instructions on uploading files or the
- [list files endpoint](https://dev.writer.com/api-guides/api-reference/file-api/get-all-files)
+ [list files endpoint](https://dev.writer.com/api-reference/file-api/get-all-files)
for how to see a list of uploaded files and their IDs.
"""
diff --git a/src/writerai/types/applications/job_create_params.py b/src/writerai/types/applications/job_create_params.py
index f0a908cd..56c84c46 100644
--- a/src/writerai/types/applications/job_create_params.py
+++ b/src/writerai/types/applications/job_create_params.py
@@ -27,8 +27,8 @@ class Input(TypedDict, total=False):
If the input type is "File upload", you must pass the `file_id` of an uploaded
file. You cannot pass a file object directly. See the
- [file upload endpoint](https://dev.writer.com/api-guides/api-reference/file-api/upload-files)
+ [file upload endpoint](https://dev.writer.com/api-reference/file-api/upload-files)
for instructions on uploading files or the
- [list files endpoint](https://dev.writer.com/api-guides/api-reference/file-api/get-all-files)
+ [list files endpoint](https://dev.writer.com/api-reference/file-api/get-all-files)
for how to see a list of uploaded files and their IDs.
"""
diff --git a/src/writerai/types/chat_chat_params.py b/src/writerai/types/chat_chat_params.py
index 66b9a544..e15fb942 100644
--- a/src/writerai/types/chat_chat_params.py
+++ b/src/writerai/types/chat_chat_params.py
@@ -14,6 +14,10 @@
__all__ = [
"ChatChatParamsBase",
"Message",
+ "MessageContentMixedContent",
+ "MessageContentMixedContentTextFragment",
+ "MessageContentMixedContentImageFragment",
+ "MessageContentMixedContentImageFragmentImageURL",
"ResponseFormat",
"StreamOptions",
"ToolChoice",
@@ -42,8 +46,10 @@ class ChatChatParamsBase(TypedDict, total=False):
max_tokens: int
"""
Defines the maximum number of tokens (words and characters) that the model can
- generate in the response. The default value is set to 16, but it can be adjusted
- to allow for longer or shorter responses as needed.
+ generate in the response. This can be adjusted to allow for longer or shorter
+ responses as needed. The maximum value varies by model. See the
+ [models overview](/home/models) for more information about the maximum number of
+ tokens for each model.
"""
n: int
@@ -81,10 +87,17 @@ class ChatChatParamsBase(TypedDict, total=False):
"""
tool_choice: ToolChoice
- """
- Configure how the model will call functions: `auto` will allow the model to
- automatically choose the best tool, `none` disables tool calling. You can also
- pass a specific previously defined function.
+ """Configure how the model will call functions:
+
+ - `auto`: allows the model to automatically choose the tool to use, or not call
+ a tool
+ - `none`: disables tool calling; the model will instead generate a message
+ - `required`: requires the model to call one or more tools
+
+ You can also use a JSON object to force the model to call a specific tool. For
+ example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ requires the model to call the `get_current_weather` function, regardless of the
+ prompt.
"""
tools: Iterable[ToolParam]
@@ -94,8 +107,8 @@ class ChatChatParamsBase(TypedDict, total=False):
own functions or use one of the built-in `graph`, `llm`, `translation`, or
`vision` tools. Note that you can only use one built-in tool type in the array
(only one of `graph`, `llm`, `translation`, or `vision`). You can pass multiple
- [custom tools](https://dev.writer.com/api-guides/tool-calling) of type
- `function` in the same request.
+ [custom tools](https://dev.writer.com/home/tool-calling) of type `function` in
+ the same request.
"""
top_p: float
@@ -107,21 +120,60 @@ class ChatChatParamsBase(TypedDict, total=False):
"""
+class MessageContentMixedContentTextFragment(TypedDict, total=False):
+ text: Required[str]
+ """The actual text content of the message fragment."""
+
+ type: Required[Literal["text"]]
+ """The type of content fragment. Must be `text` for text fragments."""
+
+
+class MessageContentMixedContentImageFragmentImageURL(TypedDict, total=False):
+ url: Required[str]
+ """The URL pointing to the image file.
+
+ Supports common image formats like JPEG, PNG, GIF, etc.
+ """
+
+
+class MessageContentMixedContentImageFragment(TypedDict, total=False):
+ image_url: Required[MessageContentMixedContentImageFragmentImageURL]
+ """The image URL object containing the location of the image."""
+
+ type: Required[Literal["image_url"]]
+ """The type of content fragment. Must be `image_url` for image fragments."""
+
+
+MessageContentMixedContent: TypeAlias = Union[
+ MessageContentMixedContentTextFragment, MessageContentMixedContentImageFragment
+]
+
+
class Message(TypedDict, total=False):
role: Required[Literal["user", "assistant", "system", "tool"]]
"""The role of the chat message.
You can provide a system prompt by setting the role to `system`, or specify that
a message is the result of a
- [tool call](https://dev.writer.com/api-guides/tool-calling) by setting the role
- to `tool`.
+ [tool call](https://dev.writer.com/home/tool-calling) by setting the role to
+ `tool`.
"""
- content: Optional[str]
+ content: Union[str, Iterable[MessageContentMixedContent], None]
+ """The content of the message.
+
+ Can be either a string (for text-only messages) or an array of content fragments
+ (for mixed text and image messages).
+ """
graph_data: Optional[GraphData]
name: Optional[str]
+ """An optional name for the message sender.
+
+ Useful for identifying different users, personas, or tools in multi-participant
+ conversations.
+ """
refusal: Optional[str]
diff --git a/src/writerai/types/chat_completion_message.py b/src/writerai/types/chat_completion_message.py
index acf5649d..5b4cb30b 100644
--- a/src/writerai/types/chat_completion_message.py
+++ b/src/writerai/types/chat_completion_message.py
@@ -7,7 +7,7 @@
from .shared.tool_call import ToolCall
from .shared.graph_data import GraphData
-__all__ = ["ChatCompletionMessage", "LlmData", "TranslationData"]
+__all__ = ["ChatCompletionMessage", "LlmData", "TranslationData", "WebSearchData", "WebSearchDataSource"]
class LlmData(BaseModel):
@@ -29,6 +29,16 @@ class TranslationData(BaseModel):
"""The language code of the target text."""
+class WebSearchDataSource(BaseModel):
+ raw_content: Optional[str] = None
+
+ url: Optional[str] = None
+
+
+class WebSearchData(BaseModel):
+ sources: List[WebSearchDataSource]
+
+
class ChatCompletionMessage(BaseModel):
content: str
"""The text content produced by the model.
@@ -49,3 +59,5 @@ class ChatCompletionMessage(BaseModel):
tool_calls: Optional[List[ToolCall]] = None
translation_data: Optional[TranslationData] = None
+
+ web_search_data: Optional[WebSearchData] = None
diff --git a/src/writerai/types/graph.py b/src/writerai/types/graph.py
deleted file mode 100644
index 02dcf94e..00000000
--- a/src/writerai/types/graph.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Optional
-from datetime import datetime
-from typing_extensions import Literal
-
-from .._models import BaseModel
-
-__all__ = ["Graph", "FileStatus"]
-
-
-class FileStatus(BaseModel):
- completed: int
- """The number of files that have been successfully processed."""
-
- failed: int
- """The number of files that failed to process."""
-
- in_progress: int
- """The number of files currently being processed."""
-
- total: int
- """The total number of files associated with the Knowledge Graph."""
-
-
-class Graph(BaseModel):
- id: str
- """The unique identifier of the Knowledge Graph."""
-
- created_at: datetime
- """The timestamp when the Knowledge Graph was created."""
-
- file_status: FileStatus
-
- name: str
- """The name of the Knowledge Graph."""
-
- type: Literal["manual", "connector"]
- """
- The type of Knowledge Graph, either `manual` (files are uploaded via UI or API)
- or `connector` (files are uploaded via a connector).
- """
-
- description: Optional[str] = None
- """A description of the Knowledge Graph."""
diff --git a/src/writerai/types/graph_create_response.py b/src/writerai/types/graph_create_response.py
index 7564f4f8..11dcb958 100644
--- a/src/writerai/types/graph_create_response.py
+++ b/src/writerai/types/graph_create_response.py
@@ -1,11 +1,36 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from datetime import datetime
+from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["GraphCreateResponse"]
+__all__ = ["GraphCreateResponse", "URL", "URLStatus"]
+
+
+class URLStatus(BaseModel):
+ status: Literal["validating", "success", "error"]
+ """The current status of the URL processing."""
+
+ error_type: Optional[
+ Literal["invalid_url", "not_searchable", "not_found", "paywall_or_login_page", "unexpected_error"]
+ ] = None
+ """The type of error that occurred during processing, if any."""
+
+
+class URL(BaseModel):
+ status: URLStatus
+ """The current status of the URL processing."""
+
+ type: Literal["single_page", "sub_pages"]
+ """The type of web connector processing for this URL."""
+
+ url: str
+ """The URL to be processed by the web connector."""
+
+ exclude_urls: Optional[List[str]] = None
+ """An array of URLs to exclude from processing within this web connector."""
class GraphCreateResponse(BaseModel):
@@ -20,3 +45,6 @@ class GraphCreateResponse(BaseModel):
description: Optional[str] = None
"""A description of the Knowledge Graph (max 255 characters)."""
+
+ urls: Optional[List[URL]] = None
+ """An array of web connector URLs associated with this Knowledge Graph."""
diff --git a/src/writerai/types/graph_list_response.py b/src/writerai/types/graph_list_response.py
new file mode 100644
index 00000000..babb4060
--- /dev/null
+++ b/src/writerai/types/graph_list_response.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["GraphListResponse", "FileStatus", "URL", "URLStatus"]
+
+
+class FileStatus(BaseModel):
+ completed: int
+ """The number of files that have been successfully processed."""
+
+ failed: int
+ """The number of files that failed to process."""
+
+ in_progress: int
+ """The number of files currently being processed."""
+
+ total: int
+ """The total number of files associated with the Knowledge Graph."""
+
+
+class URLStatus(BaseModel):
+ status: Literal["validating", "success", "error"]
+ """The current status of the URL processing."""
+
+ error_type: Optional[
+ Literal["invalid_url", "not_searchable", "not_found", "paywall_or_login_page", "unexpected_error"]
+ ] = None
+ """The type of error that occurred during processing, if any."""
+
+
+class URL(BaseModel):
+ status: URLStatus
+ """The current status of the URL processing."""
+
+ type: Literal["single_page", "sub_pages"]
+ """The type of web connector processing for this URL."""
+
+ url: str
+ """The URL to be processed by the web connector."""
+
+ exclude_urls: Optional[List[str]] = None
+ """An array of URLs to exclude from processing within this web connector."""
+
+
+class GraphListResponse(BaseModel):
+ id: str
+ """The unique identifier of the Knowledge Graph."""
+
+ created_at: datetime
+ """The timestamp when the Knowledge Graph was created."""
+
+ file_status: FileStatus
+ """The processing status of files in the Knowledge Graph."""
+
+ name: str
+ """The name of the Knowledge Graph."""
+
+ type: Literal["manual", "connector", "web"]
+ """The type of Knowledge Graph.
+
+ - `manual`: files are uploaded via UI or API
+ - `connector`: files are uploaded via a data connector such as Google Drive or
+ Confluence
+ - `web`: URLs are connected to the Knowledge Graph
+ """
+
+ description: Optional[str] = None
+ """A description of the Knowledge Graph."""
+
+ urls: Optional[List[URL]] = None
+ """An array of web connector URLs associated with this Knowledge Graph."""
diff --git a/src/writerai/types/graph_retrieve_response.py b/src/writerai/types/graph_retrieve_response.py
new file mode 100644
index 00000000..f93f7149
--- /dev/null
+++ b/src/writerai/types/graph_retrieve_response.py
@@ -0,0 +1,76 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["GraphRetrieveResponse", "FileStatus", "URL", "URLStatus"]
+
+
+class FileStatus(BaseModel):
+ completed: int
+ """The number of files that have been successfully processed."""
+
+ failed: int
+ """The number of files that failed to process."""
+
+ in_progress: int
+ """The number of files currently being processed."""
+
+ total: int
+ """The total number of files associated with the Knowledge Graph."""
+
+
+class URLStatus(BaseModel):
+ status: Literal["validating", "success", "error"]
+ """The current status of the URL processing."""
+
+ error_type: Optional[
+ Literal["invalid_url", "not_searchable", "not_found", "paywall_or_login_page", "unexpected_error"]
+ ] = None
+ """The type of error that occurred during processing, if any."""
+
+
+class URL(BaseModel):
+ status: URLStatus
+ """The current status of the URL processing."""
+
+ type: Literal["single_page", "sub_pages"]
+ """The type of web connector processing for this URL."""
+
+ url: str
+ """The URL to be processed by the web connector."""
+
+ exclude_urls: Optional[List[str]] = None
+ """An array of URLs to exclude from processing within this web connector."""
+
+
+class GraphRetrieveResponse(BaseModel):
+ id: str
+ """The unique identifier of the Knowledge Graph."""
+
+ created_at: datetime
+ """The timestamp when the Knowledge Graph was created."""
+
+ file_status: FileStatus
+ """The processing status of files in the Knowledge Graph."""
+
+ name: str
+ """The name of the Knowledge Graph."""
+
+ type: Literal["manual", "connector", "web"]
+ """The type of Knowledge Graph.
+
+ - `manual`: files are uploaded via UI or API
+ - `connector`: files are uploaded via a data connector such as Google Drive or
+ Confluence
+ - `web`: URLs are connected to the Knowledge Graph
+ """
+
+ description: Optional[str] = None
+ """A description of the Knowledge Graph."""
+
+ urls: Optional[List[URL]] = None
+ """An array of web connector URLs associated with this Knowledge Graph."""
diff --git a/src/writerai/types/graph_update_params.py b/src/writerai/types/graph_update_params.py
index 03d36d52..7be9324d 100644
--- a/src/writerai/types/graph_update_params.py
+++ b/src/writerai/types/graph_update_params.py
@@ -2,9 +2,10 @@
from __future__ import annotations
-from typing_extensions import TypedDict
+from typing import List, Iterable
+from typing_extensions import Literal, Required, TypedDict
-__all__ = ["GraphUpdateParams"]
+__all__ = ["GraphUpdateParams", "URL"]
class GraphUpdateParams(TypedDict, total=False):
@@ -19,3 +20,21 @@ class GraphUpdateParams(TypedDict, total=False):
Omitting this field leaves the name unchanged.
"""
+
+ urls: Iterable[URL]
+ """An array of web connector URLs to update for this Knowledge Graph.
+
+ You can only connect URLs to Knowledge Graphs with the type `web`. To clear the
+ list of URLs, set this field to an empty array.
+ """
+
+
+class URL(TypedDict, total=False):
+ type: Required[Literal["single_page", "sub_pages"]]
+ """The type of web connector processing for this URL."""
+
+ url: Required[str]
+ """The URL to be processed by the web connector."""
+
+ exclude_urls: List[str]
+ """An array of URLs to exclude from processing within this web connector."""
diff --git a/src/writerai/types/graph_update_response.py b/src/writerai/types/graph_update_response.py
index 3e43b2e5..bc17ebf3 100644
--- a/src/writerai/types/graph_update_response.py
+++ b/src/writerai/types/graph_update_response.py
@@ -1,11 +1,36 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import Optional
+from typing import List, Optional
from datetime import datetime
+from typing_extensions import Literal
from .._models import BaseModel
-__all__ = ["GraphUpdateResponse"]
+__all__ = ["GraphUpdateResponse", "URL", "URLStatus"]
+
+
+class URLStatus(BaseModel):
+ status: Literal["validating", "success", "error"]
+ """The current status of the URL processing."""
+
+ error_type: Optional[
+ Literal["invalid_url", "not_searchable", "not_found", "paywall_or_login_page", "unexpected_error"]
+ ] = None
+ """The type of error that occurred during processing, if any."""
+
+
+class URL(BaseModel):
+ status: URLStatus
+ """The current status of the URL processing."""
+
+ type: Literal["single_page", "sub_pages"]
+ """The type of web connector processing for this URL."""
+
+ url: str
+ """The URL to be processed by the web connector."""
+
+ exclude_urls: Optional[List[str]] = None
+ """An array of URLs to exclude from processing within this web connector."""
class GraphUpdateResponse(BaseModel):
@@ -20,3 +45,6 @@ class GraphUpdateResponse(BaseModel):
description: Optional[str] = None
"""A description of the Knowledge Graph (max 255 characters)."""
+
+ urls: Optional[List[URL]] = None
+ """An array of web connector URLs associated with this Knowledge Graph."""
diff --git a/src/writerai/types/shared/tool_choice_json_object.py b/src/writerai/types/shared/tool_choice_json_object.py
index bc12acf5..499b6d24 100644
--- a/src/writerai/types/shared/tool_choice_json_object.py
+++ b/src/writerai/types/shared/tool_choice_json_object.py
@@ -9,3 +9,7 @@
class ToolChoiceJsonObject(BaseModel):
value: Dict[str, object]
+ """A JSON object that specifies the tool to call.
+
+ For example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ """
diff --git a/src/writerai/types/shared/tool_param.py b/src/writerai/types/shared/tool_param.py
index aa76ed45..f5186ef1 100644
--- a/src/writerai/types/shared/tool_param.py
+++ b/src/writerai/types/shared/tool_param.py
@@ -19,6 +19,8 @@
"VisionTool",
"VisionToolFunction",
"VisionToolFunctionVariable",
+ "WebSearchTool",
+ "WebSearchToolFunction",
]
@@ -70,7 +72,7 @@ class TranslationToolFunction(BaseModel):
"""Whether to use formal or informal language in the translation.
See the
- [list of languages that support formality](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#formality).
+ [list of languages that support formality](https://dev.writer.com/api-reference/translation-api/language-support#formality).
If the language does not support formality, this parameter is ignored.
"""
@@ -78,7 +80,7 @@ class TranslationToolFunction(BaseModel):
"""Whether to control the length of the translated text.
See the
- [list of languages that support length control](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#length-control).
+ [list of languages that support length control](https://dev.writer.com/api-reference/translation-api/language-support#length-control).
If the language does not support length control, this parameter is ignored.
"""
@@ -86,7 +88,7 @@ class TranslationToolFunction(BaseModel):
"""Whether to mask profane words in the translated text.
See the
- [list of languages that do not support profanity masking](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#profanity-masking).
+ [list of languages that do not support profanity masking](https://dev.writer.com/api-reference/translation-api/language-support#profanity-masking).
If the language does not support profanity masking, this parameter is ignored.
"""
@@ -130,7 +132,7 @@ class VisionToolFunctionVariable(BaseModel):
"""The File ID of the image to analyze.
The file must be uploaded to the Writer platform before you use it with the
- Vision tool.
+ Vision tool. The maximum allowed file size is 7MB.
"""
name: str
@@ -158,6 +160,23 @@ class VisionTool(BaseModel):
"""The type of tool."""
+class WebSearchToolFunction(BaseModel):
+ exclude_domains: List[str]
+ """An array of domains to exclude from the search results."""
+
+ include_domains: List[str]
+ """An array of domains to include in the search results."""
+
+
+class WebSearchTool(BaseModel):
+ function: WebSearchToolFunction
+ """A tool that uses web search to find information."""
+
+ type: Literal["web_search"]
+ """The type of tool."""
+
+
ToolParam: TypeAlias = Annotated[
- Union[FunctionTool, GraphTool, LlmTool, TranslationTool, VisionTool], PropertyInfo(discriminator="type")
+ Union[FunctionTool, GraphTool, LlmTool, TranslationTool, VisionTool, WebSearchTool],
+ PropertyInfo(discriminator="type"),
]
diff --git a/src/writerai/types/shared_params/tool_choice_json_object.py b/src/writerai/types/shared_params/tool_choice_json_object.py
index 4b2cdbdc..30d0f7f6 100644
--- a/src/writerai/types/shared_params/tool_choice_json_object.py
+++ b/src/writerai/types/shared_params/tool_choice_json_object.py
@@ -10,3 +10,7 @@
class ToolChoiceJsonObject(TypedDict, total=False):
value: Required[Dict[str, object]]
+ """A JSON object that specifies the tool to call.
+
+ For example, `{"type": "function", "function": {"name": "get_current_weather"}}`
+ """
diff --git a/src/writerai/types/shared_params/tool_param.py b/src/writerai/types/shared_params/tool_param.py
index c50863e4..25211144 100644
--- a/src/writerai/types/shared_params/tool_param.py
+++ b/src/writerai/types/shared_params/tool_param.py
@@ -19,6 +19,8 @@
"VisionTool",
"VisionToolFunction",
"VisionToolFunctionVariable",
+ "WebSearchTool",
+ "WebSearchToolFunction",
]
@@ -70,7 +72,7 @@ class TranslationToolFunction(TypedDict, total=False):
"""Whether to use formal or informal language in the translation.
See the
- [list of languages that support formality](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#formality).
+ [list of languages that support formality](https://dev.writer.com/api-reference/translation-api/language-support#formality).
If the language does not support formality, this parameter is ignored.
"""
@@ -78,7 +80,7 @@ class TranslationToolFunction(TypedDict, total=False):
"""Whether to control the length of the translated text.
See the
- [list of languages that support length control](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#length-control).
+ [list of languages that support length control](https://dev.writer.com/api-reference/translation-api/language-support#length-control).
If the language does not support length control, this parameter is ignored.
"""
@@ -86,7 +88,7 @@ class TranslationToolFunction(TypedDict, total=False):
"""Whether to mask profane words in the translated text.
See the
- [list of languages that do not support profanity masking](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#profanity-masking).
+ [list of languages that do not support profanity masking](https://dev.writer.com/api-reference/translation-api/language-support#profanity-masking).
If the language does not support profanity masking, this parameter is ignored.
"""
@@ -130,7 +132,7 @@ class VisionToolFunctionVariable(TypedDict, total=False):
"""The File ID of the image to analyze.
The file must be uploaded to the Writer platform before you use it with the
- Vision tool.
+ Vision tool. The maximum allowed file size is 7MB.
"""
name: Required[str]
@@ -158,4 +160,20 @@ class VisionTool(TypedDict, total=False):
"""The type of tool."""
-ToolParam: TypeAlias = Union[FunctionTool, GraphTool, LlmTool, TranslationTool, VisionTool]
+class WebSearchToolFunction(TypedDict, total=False):
+ exclude_domains: Required[List[str]]
+ """An array of domains to exclude from the search results."""
+
+ include_domains: Required[List[str]]
+ """An array of domains to include in the search results."""
+
+
+class WebSearchTool(TypedDict, total=False):
+ function: Required[WebSearchToolFunction]
+ """A tool that uses web search to find information."""
+
+ type: Required[Literal["web_search"]]
+ """The type of tool."""
+
+
+ToolParam: TypeAlias = Union[FunctionTool, GraphTool, LlmTool, TranslationTool, VisionTool, WebSearchTool]
diff --git a/src/writerai/types/tool_web_search_params.py b/src/writerai/types/tool_web_search_params.py
new file mode 100644
index 00000000..800dc033
--- /dev/null
+++ b/src/writerai/types/tool_web_search_params.py
@@ -0,0 +1,245 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ToolWebSearchParams"]
+
+
+class ToolWebSearchParams(TypedDict, total=False):
+ chunks_per_source: int
+ """Only applies when `search_depth` is `advanced`.
+
+ Specifies how many text segments to extract from each source. Limited to 3
+ chunks maximum.
+ """
+
+ country: Literal[
+ "afghanistan",
+ "albania",
+ "algeria",
+ "andorra",
+ "angola",
+ "argentina",
+ "armenia",
+ "australia",
+ "austria",
+ "azerbaijan",
+ "bahamas",
+ "bahrain",
+ "bangladesh",
+ "barbados",
+ "belarus",
+ "belgium",
+ "belize",
+ "benin",
+ "bhutan",
+ "bolivia",
+ "bosnia and herzegovina",
+ "botswana",
+ "brazil",
+ "brunei",
+ "bulgaria",
+ "burkina faso",
+ "burundi",
+ "cambodia",
+ "cameroon",
+ "canada",
+ "cape verde",
+ "central african republic",
+ "chad",
+ "chile",
+ "china",
+ "colombia",
+ "comoros",
+ "congo",
+ "costa rica",
+ "croatia",
+ "cuba",
+ "cyprus",
+ "czech republic",
+ "denmark",
+ "djibouti",
+ "dominican republic",
+ "ecuador",
+ "egypt",
+ "el salvador",
+ "equatorial guinea",
+ "eritrea",
+ "estonia",
+ "ethiopia",
+ "fiji",
+ "finland",
+ "france",
+ "gabon",
+ "gambia",
+ "georgia",
+ "germany",
+ "ghana",
+ "greece",
+ "guatemala",
+ "guinea",
+ "haiti",
+ "honduras",
+ "hungary",
+ "iceland",
+ "india",
+ "indonesia",
+ "iran",
+ "iraq",
+ "ireland",
+ "israel",
+ "italy",
+ "jamaica",
+ "japan",
+ "jordan",
+ "kazakhstan",
+ "kenya",
+ "kuwait",
+ "kyrgyzstan",
+ "latvia",
+ "lebanon",
+ "lesotho",
+ "liberia",
+ "libya",
+ "liechtenstein",
+ "lithuania",
+ "luxembourg",
+ "madagascar",
+ "malawi",
+ "malaysia",
+ "maldives",
+ "mali",
+ "malta",
+ "mauritania",
+ "mauritius",
+ "mexico",
+ "moldova",
+ "monaco",
+ "mongolia",
+ "montenegro",
+ "morocco",
+ "mozambique",
+ "myanmar",
+ "namibia",
+ "nepal",
+ "netherlands",
+ "new zealand",
+ "nicaragua",
+ "niger",
+ "nigeria",
+ "north korea",
+ "north macedonia",
+ "norway",
+ "oman",
+ "pakistan",
+ "panama",
+ "papua new guinea",
+ "paraguay",
+ "peru",
+ "philippines",
+ "poland",
+ "portugal",
+ "qatar",
+ "romania",
+ "russia",
+ "rwanda",
+ "saudi arabia",
+ "senegal",
+ "serbia",
+ "singapore",
+ "slovakia",
+ "slovenia",
+ "somalia",
+ "south africa",
+ "south korea",
+ "south sudan",
+ "spain",
+ "sri lanka",
+ "sudan",
+ "sweden",
+ "switzerland",
+ "syria",
+ "taiwan",
+ "tajikistan",
+ "tanzania",
+ "thailand",
+ "togo",
+ "trinidad and tobago",
+ "tunisia",
+ "turkey",
+ "turkmenistan",
+ "uganda",
+ "ukraine",
+ "united arab emirates",
+ "united kingdom",
+ "united states",
+ "uruguay",
+ "uzbekistan",
+ "venezuela",
+ "vietnam",
+ "yemen",
+ "zambia",
+ "zimbabwe",
+ ]
+ """Localizes search results to a specific country.
+
+ Only applies to general topic searches.
+ """
+
+ days: int
+ """For news topic searches, specifies how many days of news coverage to include."""
+
+ exclude_domains: List[str]
+ """Domains to exclude from the search. If unset, the search includes all domains."""
+
+ include_answer: bool
+ """Whether to include a generated answer to the query in the response.
+
+ If `false`, only search results are returned.
+ """
+
+ include_domains: List[str]
+ """Domains to include in the search. If unset, the search includes all domains."""
+
+ include_raw_content: Union[Literal["text", "markdown"], bool]
+ """Controls how raw content is included in search results:
+
+ - `text`: Returns plain text without formatting markup
+ - `markdown`: Returns structured content with markdown formatting (headers,
+ links, bold text)
+ - `true`: Same as `markdown`
+ - `false`: Raw content is not included (default if unset)
+ """
+
+ max_results: int
+ """Limits the number of search results returned. Cannot exceed 20 sources."""
+
+ query: str
+ """The search query."""
+
+ search_depth: Literal["basic", "advanced"]
+ """Controls search comprehensiveness:
+
+ - `basic`: Returns fewer but highly relevant results
+ - `advanced`: Performs a deeper search with more results
+ """
+
+ stream: bool
+ """Enables streaming of search results as they become available."""
+
+ time_range: Literal["day", "week", "month", "year", "d", "w", "m", "y"]
+ """
+ Filters results to content published within the specified time range back from
+ the current date. For example, `week` or `w` returns results from the past 7
+ days.
+ """
+
+ topic: Literal["general", "news"]
+ """The search topic category.
+
+ Use `news` for current events and news articles, or `general` for broader web
+ search.
+ """
diff --git a/src/writerai/types/tool_web_search_response.py b/src/writerai/types/tool_web_search_response.py
new file mode 100644
index 00000000..2ed9ed71
--- /dev/null
+++ b/src/writerai/types/tool_web_search_response.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from .._models import BaseModel
+
+__all__ = ["ToolWebSearchResponse", "Source"]
+
+
+class Source(BaseModel):
+ raw_content: Optional[str] = None
+ """Raw content from the source URL.
+
+ Not included if `include_raw_content` is `false`.
+ """
+
+ url: Optional[str] = None
+ """URL of the search result."""
+
+
+class ToolWebSearchResponse(BaseModel):
+ query: str
+ """The search query that was submitted."""
+
+ sources: List[Source]
+ """The search results found."""
+
+ answer: Optional[str] = None
+ """Generated answer based on the search results.
+
+ Not included if `include_answer` is `false`.
+ """
diff --git a/src/writerai/types/translation_translate_params.py b/src/writerai/types/translation_translate_params.py
index 636618f1..26de74d7 100644
--- a/src/writerai/types/translation_translate_params.py
+++ b/src/writerai/types/translation_translate_params.py
@@ -12,7 +12,7 @@ class TranslationTranslateParams(TypedDict, total=False):
"""Whether to use formal or informal language in the translation.
See the
- [list of languages that support formality](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#formality).
+ [list of languages that support formality](https://dev.writer.com/api-reference/translation-api/language-support#formality).
If the language does not support formality, this parameter is ignored.
"""
@@ -20,7 +20,7 @@ class TranslationTranslateParams(TypedDict, total=False):
"""Whether to control the length of the translated text.
See the
- [list of languages that support length control](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#length-control).
+ [list of languages that support length control](https://dev.writer.com/api-reference/translation-api/language-support#length-control).
If the language does not support length control, this parameter is ignored.
"""
@@ -28,7 +28,7 @@ class TranslationTranslateParams(TypedDict, total=False):
"""Whether to mask profane words in the translated text.
See the
- [list of languages that do not support profanity masking](https://dev.writer.com/api-guides/api-reference/translation-api/language-support#profanity-masking).
+ [list of languages that do not support profanity masking](https://dev.writer.com/api-reference/translation-api/language-support#profanity-masking).
If the language does not support profanity masking, this parameter is ignored.
"""
@@ -43,7 +43,7 @@ class TranslationTranslateParams(TypedDict, total=False):
variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
"""
target_language_code: Required[str]
@@ -54,7 +54,7 @@ class TranslationTranslateParams(TypedDict, total=False):
has a variant, the code appends the two-digit
[ISO-3166 country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).
For example, Mexican Spanish is `es-MX`. See the
- [list of supported languages and language codes](https://dev.writer.com/api-guides/api-reference/translation-api/language-support).
+ [list of supported languages and language codes](https://dev.writer.com/api-reference/translation-api/language-support).
"""
text: Required[str]
diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py
index f47c2ade..8d072394 100644
--- a/tests/api_resources/test_chat.py
+++ b/tests/api_resources/test_chat.py
@@ -31,7 +31,7 @@ def test_method_chat_with_all_params_overload_1(self, client: Writer) -> None:
messages=[
{
"role": "user",
- "content": "content",
+ "content": "string",
"graph_data": {
"sources": [
{
@@ -137,7 +137,7 @@ def test_method_chat_with_all_params_overload_2(self, client: Writer) -> None:
messages=[
{
"role": "user",
- "content": "content",
+ "content": "string",
"graph_data": {
"sources": [
{
@@ -249,7 +249,7 @@ async def test_method_chat_with_all_params_overload_1(self, async_client: AsyncW
messages=[
{
"role": "user",
- "content": "content",
+ "content": "string",
"graph_data": {
"sources": [
{
@@ -355,7 +355,7 @@ async def test_method_chat_with_all_params_overload_2(self, async_client: AsyncW
messages=[
{
"role": "user",
- "content": "content",
+ "content": "string",
"graph_data": {
"sources": [
{
diff --git a/tests/api_resources/test_graphs.py b/tests/api_resources/test_graphs.py
index 15ae7a62..102cbb9e 100644
--- a/tests/api_resources/test_graphs.py
+++ b/tests/api_resources/test_graphs.py
@@ -11,11 +11,12 @@
from tests.utils import assert_matches_type
from writerai.types import (
File,
- Graph,
Question,
+ GraphListResponse,
GraphCreateResponse,
GraphDeleteResponse,
GraphUpdateResponse,
+ GraphRetrieveResponse,
GraphRemoveFileFromGraphResponse,
)
from writerai.pagination import SyncCursorPage, AsyncCursorPage
@@ -64,7 +65,7 @@ def test_method_retrieve(self, client: Writer) -> None:
graph = client.graphs.retrieve(
"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Writer) -> None:
@@ -75,7 +76,7 @@ def test_raw_response_retrieve(self, client: Writer) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = response.parse()
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
@parametrize
def test_streaming_response_retrieve(self, client: Writer) -> None:
@@ -86,7 +87,7 @@ def test_streaming_response_retrieve(self, client: Writer) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = response.parse()
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -110,6 +111,13 @@ def test_method_update_with_all_params(self, client: Writer) -> None:
graph_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
description="description",
name="name",
+ urls=[
+ {
+ "type": "single_page",
+ "url": "url",
+ "exclude_urls": ["string"],
+ }
+ ],
)
assert_matches_type(GraphUpdateResponse, graph, path=["response"])
@@ -147,7 +155,7 @@ def test_path_params_update(self, client: Writer) -> None:
@parametrize
def test_method_list(self, client: Writer) -> None:
graph = client.graphs.list()
- assert_matches_type(SyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(SyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
def test_method_list_with_all_params(self, client: Writer) -> None:
@@ -157,7 +165,7 @@ def test_method_list_with_all_params(self, client: Writer) -> None:
limit=0,
order="asc",
)
- assert_matches_type(SyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(SyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
def test_raw_response_list(self, client: Writer) -> None:
@@ -166,7 +174,7 @@ def test_raw_response_list(self, client: Writer) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = response.parse()
- assert_matches_type(SyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(SyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
def test_streaming_response_list(self, client: Writer) -> None:
@@ -175,7 +183,7 @@ def test_streaming_response_list(self, client: Writer) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = response.parse()
- assert_matches_type(SyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(SyncCursorPage[GraphListResponse], graph, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -441,7 +449,7 @@ async def test_method_retrieve(self, async_client: AsyncWriter) -> None:
graph = await async_client.graphs.retrieve(
"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncWriter) -> None:
@@ -452,7 +460,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncWriter) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = await response.parse()
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncWriter) -> None:
@@ -463,7 +471,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncWriter) -> N
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = await response.parse()
- assert_matches_type(Graph, graph, path=["response"])
+ assert_matches_type(GraphRetrieveResponse, graph, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -487,6 +495,13 @@ async def test_method_update_with_all_params(self, async_client: AsyncWriter) ->
graph_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
description="description",
name="name",
+ urls=[
+ {
+ "type": "single_page",
+ "url": "url",
+ "exclude_urls": ["string"],
+ }
+ ],
)
assert_matches_type(GraphUpdateResponse, graph, path=["response"])
@@ -524,7 +539,7 @@ async def test_path_params_update(self, async_client: AsyncWriter) -> None:
@parametrize
async def test_method_list(self, async_client: AsyncWriter) -> None:
graph = await async_client.graphs.list()
- assert_matches_type(AsyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(AsyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncWriter) -> None:
@@ -534,7 +549,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncWriter) -> N
limit=0,
order="asc",
)
- assert_matches_type(AsyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(AsyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncWriter) -> None:
@@ -543,7 +558,7 @@ async def test_raw_response_list(self, async_client: AsyncWriter) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = await response.parse()
- assert_matches_type(AsyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(AsyncCursorPage[GraphListResponse], graph, path=["response"])
@parametrize
async def test_streaming_response_list(self, async_client: AsyncWriter) -> None:
@@ -552,7 +567,7 @@ async def test_streaming_response_list(self, async_client: AsyncWriter) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
graph = await response.parse()
- assert_matches_type(AsyncCursorPage[Graph], graph, path=["response"])
+ assert_matches_type(AsyncCursorPage[GraphListResponse], graph, path=["response"])
assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_tools.py b/tests/api_resources/test_tools.py
index bbc2a7db..8e2787aa 100644
--- a/tests/api_resources/test_tools.py
+++ b/tests/api_resources/test_tools.py
@@ -12,6 +12,7 @@
from writerai.types import (
ToolAIDetectResponse,
ToolParsePdfResponse,
+ ToolWebSearchResponse,
ToolContextAwareSplittingResponse,
)
@@ -128,6 +129,50 @@ def test_path_params_parse_pdf(self, client: Writer) -> None:
format="text",
)
+ @parametrize
+ def test_method_web_search(self, client: Writer) -> None:
+ tool = client.tools.web_search()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ def test_method_web_search_with_all_params(self, client: Writer) -> None:
+ tool = client.tools.web_search(
+ chunks_per_source=0,
+ country="afghanistan",
+ days=0,
+ exclude_domains=["string"],
+ include_answer=True,
+ include_domains=["dev.writer.com"],
+ include_raw_content="text",
+ max_results=0,
+ query="How do I get an API key for the Writer API?",
+ search_depth="basic",
+ stream=True,
+ time_range="day",
+ topic="general",
+ )
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ def test_raw_response_web_search(self, client: Writer) -> None:
+ response = client.tools.with_raw_response.web_search()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tool = response.parse()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ def test_streaming_response_web_search(self, client: Writer) -> None:
+ with client.tools.with_streaming_response.web_search() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tool = response.parse()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
class TestAsyncTools:
parametrize = pytest.mark.parametrize(
@@ -240,3 +285,47 @@ async def test_path_params_parse_pdf(self, async_client: AsyncWriter) -> None:
file_id="",
format="text",
)
+
+ @parametrize
+ async def test_method_web_search(self, async_client: AsyncWriter) -> None:
+ tool = await async_client.tools.web_search()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ async def test_method_web_search_with_all_params(self, async_client: AsyncWriter) -> None:
+ tool = await async_client.tools.web_search(
+ chunks_per_source=0,
+ country="afghanistan",
+ days=0,
+ exclude_domains=["string"],
+ include_answer=True,
+ include_domains=["dev.writer.com"],
+ include_raw_content="text",
+ max_results=0,
+ query="How do I get an API key for the Writer API?",
+ search_depth="basic",
+ stream=True,
+ time_range="day",
+ topic="general",
+ )
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ async def test_raw_response_web_search(self, async_client: AsyncWriter) -> None:
+ response = await async_client.tools.with_raw_response.web_search()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ tool = await response.parse()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_web_search(self, async_client: AsyncWriter) -> None:
+ async with async_client.tools.with_streaming_response.web_search() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ tool = await response.parse()
+ assert_matches_type(ToolWebSearchResponse, tool, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/lib/streaming/test_chat_completions_streaming.py b/tests/lib/streaming/test_chat_completions_streaming.py
index a621e7a2..990d1f70 100644
--- a/tests/lib/streaming/test_chat_completions_streaming.py
+++ b/tests/lib/streaming/test_chat_completions_streaming.py
@@ -71,7 +71,8 @@ def test_parse_nothing(client: Writer, respx_mock: MockRouter, monkeypatch: pyte
refusal=None,
role='assistant',
tool_calls=None,
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -494,7 +495,8 @@ def test_content_logprobs_events(client: Writer, respx_mock: MockRouter, monkeyp
refusal=None,
role='assistant',
tool_calls=None,
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -587,7 +589,8 @@ def test_refusal_logprobs_events(client: Writer, respx_mock: MockRouter, monkeyp
refusal="I'm very sorry, but I can't assist with that.",
role='assistant',
tool_calls=None,
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -646,7 +649,8 @@ class GetWeatherArgs(BaseModel):
type='function'
)
],
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -679,7 +683,8 @@ class GetWeatherArgs(BaseModel):
type='function'
)
],
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -762,7 +767,8 @@ class GetStockPrice(BaseModel):
type='function'
)
],
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -906,7 +912,8 @@ def test_non_pydantic_response_format(client: Writer, respx_mock: MockRouter, mo
refusal=None,
role='assistant',
tool_calls=None,
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
@@ -975,7 +982,8 @@ def test_allows_non_strict_tools_but_no_parsing(
type='function'
)
],
- translation_data=None
+ translation_data=None,
+ web_search_data=None
)
)
]
diff --git a/tests/test_models.py b/tests/test_models.py
index 65019f23..0bf7e815 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,5 +1,5 @@
import json
-from typing import Any, Dict, List, Union, Optional, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast
from datetime import datetime, timezone
from typing_extensions import Literal, Annotated, TypeAliasType
@@ -934,3 +934,30 @@ class Type2(BaseModel):
)
assert isinstance(model, Type1)
assert isinstance(model.value, InnerType2)
+
+
+@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now")
+def test_extra_properties() -> None:
+ class Item(BaseModel):
+ prop: int
+
+ class Model(BaseModel):
+ __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ other: str
+
+ if TYPE_CHECKING:
+
+ def __getattr__(self, attr: str) -> Item: ...
+
+ model = construct_type(
+ type_=Model,
+ value={
+ "a": {"prop": 1},
+ "other": "foo",
+ },
+ )
+ assert isinstance(model, Model)
+ assert model.a.prop == 1
+ assert isinstance(model.a, Item)
+ assert model.other == "foo"