unique-sdk 2026.44.0.dev4__tar.gz → 2026.44.0.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/PKG-INFO +1 -1
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/pyproject.toml +1 -1
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/cli.py +28 -13
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/web_search.py +174 -20
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +52 -2
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/README.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/__init__.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_api_requestor.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_api_resource.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_api_version.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_error.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_http_client.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_list_object.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_object_classes.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_request_options.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_unique_object.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_unique_ql.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_unique_response.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_util.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_version.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/_webhook.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/__init__.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_acronyms.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_agentic_table.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_analytics_order.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_benchmarking.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_briefing.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_chat_completion.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_content.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_context_memory.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_elicitation.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_embedding.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_event.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_folder.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_group.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_integrated.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_llm_models.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_mcp.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_assessment.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_execution.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_log.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_tool.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_module.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_scheduled_task.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_search.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_search_string.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_short_term_memory.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_space.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_user.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_web_search.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/__init__.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/__main__.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/__init__.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/agentic_table.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/agentic_table_write.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/browser.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/cite_file.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/elicitation.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/files.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/folders.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/mcp.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/navigation.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/read.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/search.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/subagent.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/web_search_config.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/config.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/formatting.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/identity.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/metadata_filter.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/paths.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/shell.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-agentic-table/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/state.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/workspace.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/analytics_order_run.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/benchmarking_run.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/chat_history.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/chat_in_space.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/file_io.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/sources.py +0 -0
- {unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/token.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-sdk
|
|
3
|
-
Version: 2026.44.0.
|
|
3
|
+
Version: 2026.44.0.dev5
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
|
|
6
6
|
Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
|
|
@@ -105,6 +105,7 @@ from unique_sdk.cli.commands.subagent import (
|
|
|
105
105
|
from unique_sdk.cli.commands.web_search import (
|
|
106
106
|
cmd_web_crawl,
|
|
107
107
|
cmd_web_search,
|
|
108
|
+
cmd_web_search_many,
|
|
108
109
|
)
|
|
109
110
|
from unique_sdk.cli.commands.web_search import (
|
|
110
111
|
is_error_output as _is_web_search_error_output,
|
|
@@ -2127,6 +2128,7 @@ Examples:
|
|
|
2127
2128
|
unique-cli web-search search "quarterly earnings 2026"
|
|
2128
2129
|
unique-cli web-search search "AI regulation" -n 10
|
|
2129
2130
|
unique-cli web-search search "python tutorial" --json
|
|
2131
|
+
unique-cli web-search search "git history" "redis history" "docker history"
|
|
2130
2132
|
unique-cli web-search search "sustainability" --include-content --json
|
|
2131
2133
|
unique-cli web-search search "tax reform" \\
|
|
2132
2134
|
--engine-config '{"searchEngineName":"Google","fetchSize":3}'
|
|
@@ -2135,7 +2137,7 @@ Examples:
|
|
|
2135
2137
|
|
|
2136
2138
|
|
|
2137
2139
|
@web_search_group.command("search", help=_SEARCH_HELP)
|
|
2138
|
-
@click.argument("
|
|
2140
|
+
@click.argument("queries", nargs=-1, required=True)
|
|
2139
2141
|
@click.option(
|
|
2140
2142
|
"--fetch-size",
|
|
2141
2143
|
"-n",
|
|
@@ -2191,7 +2193,7 @@ Examples:
|
|
|
2191
2193
|
@click.pass_context
|
|
2192
2194
|
def web_search_search_cmd(
|
|
2193
2195
|
ctx: click.Context,
|
|
2194
|
-
|
|
2196
|
+
queries: tuple[str, ...],
|
|
2195
2197
|
fetch_size: int | None,
|
|
2196
2198
|
include_content: bool,
|
|
2197
2199
|
engine_config_raw: str | None,
|
|
@@ -2202,17 +2204,30 @@ def web_search_search_cmd(
|
|
|
2202
2204
|
) -> None:
|
|
2203
2205
|
"""Search the web via the Unique public API."""
|
|
2204
2206
|
resolved_config = _resolve_web_search_config_path(ctx, config_path)
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2207
|
+
if len(queries) == 1:
|
|
2208
|
+
output = cmd_web_search(
|
|
2209
|
+
LazyState.get(ctx),
|
|
2210
|
+
queries[0],
|
|
2211
|
+
fetch_size=fetch_size,
|
|
2212
|
+
include_content=include_content,
|
|
2213
|
+
engine_config_raw=engine_config_raw,
|
|
2214
|
+
crawler_config_raw=crawler_config_raw,
|
|
2215
|
+
output_json=output_json,
|
|
2216
|
+
config_path=resolved_config,
|
|
2217
|
+
chat_id=_resolve_cli_chat_id(ctx, chat_id),
|
|
2218
|
+
)
|
|
2219
|
+
else:
|
|
2220
|
+
output = cmd_web_search_many(
|
|
2221
|
+
LazyState.get(ctx),
|
|
2222
|
+
list(queries),
|
|
2223
|
+
fetch_size=fetch_size,
|
|
2224
|
+
include_content=include_content,
|
|
2225
|
+
engine_config_raw=engine_config_raw,
|
|
2226
|
+
crawler_config_raw=crawler_config_raw,
|
|
2227
|
+
output_json=output_json,
|
|
2228
|
+
config_path=resolved_config,
|
|
2229
|
+
chat_id=_resolve_cli_chat_id(ctx, chat_id),
|
|
2230
|
+
)
|
|
2216
2231
|
_emit_web_search(ctx, output)
|
|
2217
2232
|
|
|
2218
2233
|
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/web_search.py
RENAMED
|
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import json
|
|
21
21
|
import logging
|
|
22
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
from typing import Any, cast
|
|
24
25
|
|
|
@@ -40,6 +41,8 @@ from unique_sdk.cli.workspace import manifest_path as workspace_manifest_path
|
|
|
40
41
|
_LOGGER = logging.getLogger(__name__)
|
|
41
42
|
|
|
42
43
|
DEFAULT_PARALLEL = 10
|
|
44
|
+
MAX_SEARCH_QUERIES = 8
|
|
45
|
+
SEARCH_CONCURRENCY = 4
|
|
43
46
|
_SNIPPET_PREVIEW_LIMIT = 200
|
|
44
47
|
_CONTENT_PREVIEW_LIMIT = 500
|
|
45
48
|
_WEB_REFS_LOG_RELATIVE_PATH = Path(".unique") / "web-refs.jsonl"
|
|
@@ -231,14 +234,18 @@ def _format_search_results(payload: dict[str, Any]) -> str:
|
|
|
231
234
|
return "\n".join(lines).rstrip()
|
|
232
235
|
|
|
233
236
|
|
|
237
|
+
def _search_results_projection(payload: dict[str, Any]) -> dict[str, Any]:
|
|
238
|
+
"""Stable projection — drops the SDK envelope ``object`` discriminator."""
|
|
239
|
+
return {
|
|
240
|
+
"engine": payload.get("engine"),
|
|
241
|
+
"query": payload.get("query"),
|
|
242
|
+
"results": payload.get("results", []),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
234
246
|
def _format_search_results_json(payload: dict[str, Any]) -> str:
|
|
235
|
-
"""Stable JSON projection — drops the SDK envelope ``object`` discriminator."""
|
|
236
247
|
return json.dumps(
|
|
237
|
-
|
|
238
|
-
"engine": payload.get("engine"),
|
|
239
|
-
"query": payload.get("query"),
|
|
240
|
-
"results": payload.get("results", []),
|
|
241
|
-
},
|
|
248
|
+
_search_results_projection(payload),
|
|
242
249
|
indent=2,
|
|
243
250
|
ensure_ascii=False,
|
|
244
251
|
)
|
|
@@ -385,6 +392,40 @@ def cmd_web_search(
|
|
|
385
392
|
chat_id: Optional chat id this call is made on behalf of. When
|
|
386
393
|
set, the space's Web Search toggle is enforced server-side.
|
|
387
394
|
"""
|
|
395
|
+
options = _search_request_options(
|
|
396
|
+
fetch_size=fetch_size,
|
|
397
|
+
include_content=include_content,
|
|
398
|
+
engine_config_raw=engine_config_raw,
|
|
399
|
+
crawler_config_raw=crawler_config_raw,
|
|
400
|
+
config_path=config_path,
|
|
401
|
+
chat_id=chat_id,
|
|
402
|
+
)
|
|
403
|
+
if isinstance(options, str):
|
|
404
|
+
return options
|
|
405
|
+
|
|
406
|
+
payload = _fetch_search_payload(state, query, options)
|
|
407
|
+
if isinstance(payload, str):
|
|
408
|
+
return payload
|
|
409
|
+
|
|
410
|
+
try:
|
|
411
|
+
payload = _annotate_web_results_for_citations(payload)
|
|
412
|
+
except UnsafeRefsLogPathError as exc:
|
|
413
|
+
return f"{WEB_SEARCH_ERROR_PREFIX} {exc}"
|
|
414
|
+
if output_json:
|
|
415
|
+
return _format_search_results_json(payload)
|
|
416
|
+
return _format_search_results(payload)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _search_request_options(
|
|
420
|
+
*,
|
|
421
|
+
fetch_size: int | None,
|
|
422
|
+
include_content: bool,
|
|
423
|
+
engine_config_raw: str | None,
|
|
424
|
+
crawler_config_raw: str | None,
|
|
425
|
+
config_path: str | None,
|
|
426
|
+
chat_id: str | None,
|
|
427
|
+
) -> dict[str, Any] | str:
|
|
428
|
+
"""Build the request fields shared by every query, or return an error string."""
|
|
388
429
|
file_overrides = _load_file_overrides(config_path)
|
|
389
430
|
if isinstance(file_overrides, str):
|
|
390
431
|
return file_overrides
|
|
@@ -402,34 +443,147 @@ def cmd_web_search(
|
|
|
402
443
|
if fetch_size is None:
|
|
403
444
|
fetch_size = file_overrides.fetch_size
|
|
404
445
|
|
|
405
|
-
|
|
446
|
+
options: dict[str, Any] = {}
|
|
406
447
|
if fetch_size is not None:
|
|
407
|
-
|
|
448
|
+
options["fetchSize"] = fetch_size
|
|
408
449
|
if include_content:
|
|
409
|
-
|
|
450
|
+
options["includeContent"] = True
|
|
410
451
|
if engine_override is not None:
|
|
411
|
-
|
|
452
|
+
options["searchEngineConfig"] = engine_override
|
|
412
453
|
if crawler_override is not None:
|
|
413
|
-
|
|
454
|
+
options["crawlerConfig"] = crawler_override
|
|
414
455
|
if chat_id:
|
|
415
|
-
|
|
456
|
+
options["chatId"] = chat_id
|
|
457
|
+
return options
|
|
416
458
|
|
|
459
|
+
|
|
460
|
+
def _fetch_search_payload(
|
|
461
|
+
state: ShellState,
|
|
462
|
+
query: str,
|
|
463
|
+
options: dict[str, Any],
|
|
464
|
+
) -> dict[str, Any] | str:
|
|
465
|
+
"""Run one search request; return the payload, or an error string."""
|
|
417
466
|
try:
|
|
418
467
|
resource = unique_sdk.WebSearch.search(
|
|
419
468
|
user_id=state.config.user_id,
|
|
420
469
|
company_id=state.config.company_id,
|
|
421
|
-
|
|
470
|
+
query=query,
|
|
471
|
+
**options,
|
|
422
472
|
)
|
|
423
|
-
except (ValueError, unique_sdk.
|
|
473
|
+
except (ValueError, unique_sdk.UniqueError) as exc:
|
|
424
474
|
return f"{WEB_SEARCH_ERROR_PREFIX} {exc}"
|
|
475
|
+
return _payload_from_resource(resource)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def cmd_web_search_many(
|
|
479
|
+
state: ShellState,
|
|
480
|
+
queries: list[str],
|
|
481
|
+
fetch_size: int | None = None,
|
|
482
|
+
include_content: bool = False,
|
|
483
|
+
engine_config_raw: str | None = None,
|
|
484
|
+
crawler_config_raw: str | None = None,
|
|
485
|
+
output_json: bool = False,
|
|
486
|
+
config_path: str | None = None,
|
|
487
|
+
chat_id: str | None = None,
|
|
488
|
+
) -> str:
|
|
489
|
+
"""Run several web searches in one call and return one block per query.
|
|
490
|
+
|
|
491
|
+
Requests run concurrently, at most ``SEARCH_CONCURRENCY`` at a time.
|
|
492
|
+
Citation numbers are assigned afterwards, one query at a time in the
|
|
493
|
+
order given, so ``[websourceN]`` is the same on every run. A URL found
|
|
494
|
+
by several queries keeps one number; the manifest gets one row per search.
|
|
495
|
+
|
|
496
|
+
A failed query shows its own error and does not drop the others. The
|
|
497
|
+
output is a ``web-search:`` error only when every query failed.
|
|
498
|
+
``output_json`` returns a list with one entry per query: the usual
|
|
499
|
+
``{engine, query, results}`` object, or ``{query, error}`` on failure.
|
|
500
|
+
"""
|
|
501
|
+
if not queries:
|
|
502
|
+
return f"{WEB_SEARCH_ERROR_PREFIX} no queries provided."
|
|
503
|
+
if len(queries) > MAX_SEARCH_QUERIES:
|
|
504
|
+
return (
|
|
505
|
+
f"{WEB_SEARCH_ERROR_PREFIX} too many queries ({len(queries)}); "
|
|
506
|
+
f"the limit is {MAX_SEARCH_QUERIES} per call. "
|
|
507
|
+
"Split them across several calls."
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
options = _search_request_options(
|
|
511
|
+
fetch_size=fetch_size,
|
|
512
|
+
include_content=include_content,
|
|
513
|
+
engine_config_raw=engine_config_raw,
|
|
514
|
+
crawler_config_raw=crawler_config_raw,
|
|
515
|
+
config_path=config_path,
|
|
516
|
+
chat_id=chat_id,
|
|
517
|
+
)
|
|
518
|
+
if isinstance(options, str):
|
|
519
|
+
return options
|
|
520
|
+
|
|
521
|
+
with ThreadPoolExecutor(
|
|
522
|
+
max_workers=min(SEARCH_CONCURRENCY, len(queries))
|
|
523
|
+
) as executor:
|
|
524
|
+
fetched = list(
|
|
525
|
+
executor.map(
|
|
526
|
+
lambda query: _fetch_search_payload(state, query, options), queries
|
|
527
|
+
)
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
outcomes: list[dict[str, Any] | str] = []
|
|
531
|
+
for item in fetched:
|
|
532
|
+
if isinstance(item, str):
|
|
533
|
+
outcomes.append(item)
|
|
534
|
+
continue
|
|
535
|
+
try:
|
|
536
|
+
outcomes.append(_annotate_web_results_for_citations(item))
|
|
537
|
+
except UnsafeRefsLogPathError as exc:
|
|
538
|
+
return f"{WEB_SEARCH_ERROR_PREFIX} {exc}"
|
|
539
|
+
|
|
540
|
+
failures = [
|
|
541
|
+
f"{query!r}: {outcome}"
|
|
542
|
+
for query, outcome in zip(queries, outcomes, strict=True)
|
|
543
|
+
if isinstance(outcome, str)
|
|
544
|
+
]
|
|
545
|
+
if len(failures) == len(queries):
|
|
546
|
+
return (
|
|
547
|
+
f"{WEB_SEARCH_ERROR_PREFIX} all {len(queries)} queries failed.\n"
|
|
548
|
+
+ "\n".join(failures)
|
|
549
|
+
)
|
|
425
550
|
|
|
426
|
-
try:
|
|
427
|
-
payload = _annotate_web_results_for_citations(_payload_from_resource(resource))
|
|
428
|
-
except UnsafeRefsLogPathError as exc:
|
|
429
|
-
return f"{WEB_SEARCH_ERROR_PREFIX} {exc}"
|
|
430
551
|
if output_json:
|
|
431
|
-
return
|
|
432
|
-
return
|
|
552
|
+
return _format_many_search_results_json(queries, outcomes)
|
|
553
|
+
return _format_many_search_results(queries, outcomes)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _format_many_search_results(
|
|
557
|
+
queries: list[str],
|
|
558
|
+
outcomes: list[dict[str, Any] | str],
|
|
559
|
+
) -> str:
|
|
560
|
+
blocks: list[str] = []
|
|
561
|
+
failed = 0
|
|
562
|
+
for position, (query, outcome) in enumerate(
|
|
563
|
+
zip(queries, outcomes, strict=True), start=1
|
|
564
|
+
):
|
|
565
|
+
header = f"=== query {position}/{len(queries)} ==="
|
|
566
|
+
if isinstance(outcome, str):
|
|
567
|
+
failed += 1
|
|
568
|
+
blocks.append(f"{header}\nquery: {query!r}\nERROR: {outcome}")
|
|
569
|
+
continue
|
|
570
|
+
blocks.append(f"{header}\n{_format_search_results(outcome)}")
|
|
571
|
+
if failed:
|
|
572
|
+
blocks.append(f"{failed} of {len(queries)} queries failed.")
|
|
573
|
+
return "\n\n".join(blocks)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _format_many_search_results_json(
|
|
577
|
+
queries: list[str],
|
|
578
|
+
outcomes: list[dict[str, Any] | str],
|
|
579
|
+
) -> str:
|
|
580
|
+
entries = [
|
|
581
|
+
{"query": query, "error": outcome}
|
|
582
|
+
if isinstance(outcome, str)
|
|
583
|
+
else _search_results_projection(outcome)
|
|
584
|
+
for query, outcome in zip(queries, outcomes, strict=True)
|
|
585
|
+
]
|
|
586
|
+
return json.dumps(entries, indent=2, ensure_ascii=False)
|
|
433
587
|
|
|
434
588
|
|
|
435
589
|
def cmd_web_crawl(
|
|
@@ -58,6 +58,31 @@ unique-cli web-search search "python tutorial" --json
|
|
|
58
58
|
unique-cli web-search search "sustainability reports" --include-content --json
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
### Several searches: pass every query in ONE call
|
|
62
|
+
|
|
63
|
+
When you need more than one search (several topics, companies or
|
|
64
|
+
questions), pass all queries as separate arguments of a single `search`
|
|
65
|
+
command. Do **not** run one `unique-cli web-search search` call per query,
|
|
66
|
+
and do not start several in the background: the queries run concurrently
|
|
67
|
+
inside the one call and the answer comes back in one round.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
unique-cli web-search search "Git first release" "Redis first release" "Docker first release" --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Up to 8 queries per call (4 run at the same time). More than 8 is an
|
|
74
|
+
error: split them across calls.
|
|
75
|
+
- Options such as `-n`, `--include-content` and `--json` apply to every
|
|
76
|
+
query. With `--include-content`, prefer fewer queries per call: the output
|
|
77
|
+
grows with every query.
|
|
78
|
+
- Output has one block per query, in the order you gave them, each with
|
|
79
|
+
its query. `--json` returns a list with one entry per query.
|
|
80
|
+
- `[websourceN]` numbers follow the query order. A URL found by two
|
|
81
|
+
queries has one number.
|
|
82
|
+
- A query that fails shows its own error and the other queries still
|
|
83
|
+
return. The command exits `1` only when every query failed.
|
|
84
|
+
- A single query behaves exactly as before.
|
|
85
|
+
|
|
61
86
|
### Per-call engine override
|
|
62
87
|
|
|
63
88
|
Force a specific engine (or any engine-specific knob) without changing server config:
|
|
@@ -141,7 +166,7 @@ discriminator:
|
|
|
141
166
|
|
|
142
167
|
## Command Reference
|
|
143
168
|
|
|
144
|
-
### `unique-cli web-search search <query
|
|
169
|
+
### `unique-cli web-search search <query> [<query>...]`
|
|
145
170
|
|
|
146
171
|
| Option | Short | Default | Description |
|
|
147
172
|
|--------|-------|---------|-------------|
|
|
@@ -217,6 +242,28 @@ Found 3 result(s):
|
|
|
217
242
|
> | unique-cli web-search crawl --stdin
|
|
218
243
|
> ```
|
|
219
244
|
|
|
245
|
+
### `search` with several queries (text)
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
=== query 1/2 ===
|
|
249
|
+
engine: Google query: 'Git first release'
|
|
250
|
+
Found 1 result(s):
|
|
251
|
+
|
|
252
|
+
1. Git history [websource1]
|
|
253
|
+
https://example.com/git
|
|
254
|
+
...
|
|
255
|
+
|
|
256
|
+
=== query 2/2 ===
|
|
257
|
+
query: 'Redis first release'
|
|
258
|
+
ERROR: web-search: upstream failed
|
|
259
|
+
|
|
260
|
+
1 of 2 queries failed.
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
With `--json` the output is a list. Each entry is the usual
|
|
264
|
+
`{engine, query, results}` object, or `{"query": "...", "error": "..."}`
|
|
265
|
+
for a query that failed.
|
|
266
|
+
|
|
220
267
|
### `crawl` (text)
|
|
221
268
|
|
|
222
269
|
```
|
|
@@ -261,7 +308,10 @@ In one-shot mode, the CLI exits **`0`** on success and **`1`** on
|
|
|
261
308
|
failures (config-file errors, invalid JSON overrides, API errors,
|
|
262
309
|
missing URLs for `crawl`, and citation-manifest I/O failures — e.g.
|
|
263
310
|
when `.unique/web-refs.jsonl` cannot be written or its parent
|
|
264
|
-
directory is unsafe).
|
|
311
|
+
directory is unsafe). With several queries in one `search` call, a failed
|
|
312
|
+
query alone does not change the exit code: it exits `1` only when every
|
|
313
|
+
query failed, or when the call is invalid (more than 8 queries, bad
|
|
314
|
+
override JSON). Error messages are prefixed with
|
|
265
315
|
`web-search:` or `web-crawl:` so they're easy to grep.
|
|
266
316
|
|
|
267
317
|
```bash
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/__init__.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_acronyms.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_agentic_table.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_analytics_order.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_benchmarking.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_briefing.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_chat_completion.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_content.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_context_memory.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_elicitation.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_embedding.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_integrated.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_llm_models.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_log.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_message_tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_scheduled_task.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_search_string.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/api_resources/_web_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/agentic_table.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/cite_file.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/elicitation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/navigation.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/scheduled_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/cli/commands/web_search_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/analytics_order_run.py
RENAMED
|
File without changes
|
{unique_sdk-2026.44.0.dev4 → unique_sdk-2026.44.0.dev5}/unique_sdk/utils/benchmarking_run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|