outsail-sdk 0.28.1__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.
- outsail_sdk-0.28.1/LICENSE +21 -0
- outsail_sdk-0.28.1/PKG-INFO +87 -0
- outsail_sdk-0.28.1/README.md +61 -0
- outsail_sdk-0.28.1/pyproject.toml +41 -0
- outsail_sdk-0.28.1/setup.cfg +4 -0
- outsail_sdk-0.28.1/src/outsail/__init__.py +30 -0
- outsail_sdk-0.28.1/src/outsail/agent_tools.py +212 -0
- outsail_sdk-0.28.1/src/outsail/client.py +985 -0
- outsail_sdk-0.28.1/src/outsail/errors.py +23 -0
- outsail_sdk-0.28.1/src/outsail/py.typed +1 -0
- outsail_sdk-0.28.1/src/outsail/streaming.py +91 -0
- outsail_sdk-0.28.1/src/outsail/types.py +374 -0
- outsail_sdk-0.28.1/src/outsail_sdk.egg-info/PKG-INFO +87 -0
- outsail_sdk-0.28.1/src/outsail_sdk.egg-info/SOURCES.txt +18 -0
- outsail_sdk-0.28.1/src/outsail_sdk.egg-info/dependency_links.txt +1 -0
- outsail_sdk-0.28.1/src/outsail_sdk.egg-info/requires.txt +7 -0
- outsail_sdk-0.28.1/src/outsail_sdk.egg-info/top_level.txt +1 -0
- outsail_sdk-0.28.1/tests/test_client.py +942 -0
- outsail_sdk-0.28.1/tests/test_release_parity.py +66 -0
- outsail_sdk-0.28.1/tests/test_streaming.py +324 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The Atomic Shift
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: outsail-sdk
|
|
3
|
+
Version: 0.28.1
|
|
4
|
+
Summary: Python SDK for the Outsail Context Intelligence Platform
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/theatomicshift/outsail
|
|
7
|
+
Project-URL: Repository, https://github.com/theatomicshift/outsail
|
|
8
|
+
Keywords: outsail,context-intelligence,ai,research,agents
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: httpx>=0.27.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
24
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Outsail Python SDK
|
|
28
|
+
|
|
29
|
+
`outsail-sdk` is the official async Python SDK for Outsail. It supports Python 3.10–3.13.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install outsail-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import asyncio
|
|
37
|
+
from outsail import OutsailClient, OutsailApiError
|
|
38
|
+
|
|
39
|
+
async def main():
|
|
40
|
+
async with OutsailClient(api_key="osk_...") as client:
|
|
41
|
+
pack = await client.research("Research current OAuth migration guidance", profile="developer", discover=True)
|
|
42
|
+
print(pack.answer)
|
|
43
|
+
|
|
44
|
+
asyncio.run(main())
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For account-session authentication, call `await client.login(email, password)`, then construct a client with the returned session/API token. Production services should create scoped keys with `create_api_key()` and pass them as `api_key`; do not put keys in browser code.
|
|
48
|
+
|
|
49
|
+
## Research and streaming
|
|
50
|
+
|
|
51
|
+
Use `extract`, `map`, `discover`, `research`, `start_research`, `verify`, `compare`, `report`, and `decision_report` to build agent-ready workflows. `start_research` accepts the same full options as `research`, including provider, budget, cache, site, and Context Memory settings.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
job = await client.start_research("Compare database migration guides", discover=True, max_sources=12)
|
|
55
|
+
async for event in client.stream_research(job):
|
|
56
|
+
print(event.event_type, event.data)
|
|
57
|
+
|
|
58
|
+
report = await client.report(pack)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The SDK includes Context Packs, monitors (including RSS feeds), knowledge-base creation and ingestion, connectors, graph, memory, trust, workflows, API keys, organizations, notifications, billing, and self-service account controls.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
monitor = await client.create_monitor("Track release notes", rss_feeds=["https://example.com/feed.xml"])
|
|
65
|
+
base = await client.create_knowledge_base(name="Engineering docs")
|
|
66
|
+
await client.ingest_knowledge_base_documents(base["id"], [{"title": "Runbook", "content": "# Recovery"}])
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Connectors and platform controls
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
github = await client.create_github_connector("Engineering docs", token=os.environ["GITHUB_TOKEN"])
|
|
73
|
+
await client.sync_github("owner/repository", connector_id=github.id)
|
|
74
|
+
print(await client.operations())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`create_notion_connector`, `create_google_drive_connector`, and `create_slack_connector` return connector records. Call the matching `begin_*_authorization` method and open its URL in a browser, then run `sync_notion`, `sync_google_drive`, or `sync_slack`. The client also exposes graph, memory, trust-policy, workflow, Context Pack feedback, notifications, and usage operations for durable agent workflows.
|
|
78
|
+
|
|
79
|
+
## Errors and compatibility
|
|
80
|
+
|
|
81
|
+
Non-2xx API responses raise `OutsailApiError` (`OutsailError` remains a compatible alias), with `status` and parsed `body`. Supply `idempotency_key` when retrying research or ingestion. The SDK is version-locked to Outsail `0.28.0`; consult the served [OpenAPI reference](https://outsail.theatomicshift.com/openapi.json) for request and response details.
|
|
82
|
+
|
|
83
|
+
## Compatibility and deprecations
|
|
84
|
+
|
|
85
|
+
Use the SDK release that matches the platform’s `0.28.x` release. Breaking changes require a new major release; deprecated operations are announced in release notes before removal. Store API keys only in server-side environment variables and revoke keys promptly if they may have been exposed.
|
|
86
|
+
|
|
87
|
+
OAuth callbacks and inbound GitHub/Slack webhooks are browser/server integration flows, not SDK calls. Create a connector, open its returned authorization URL, then call its sync method.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Outsail Python SDK
|
|
2
|
+
|
|
3
|
+
`outsail-sdk` is the official async Python SDK for Outsail. It supports Python 3.10–3.13.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install outsail-sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import asyncio
|
|
11
|
+
from outsail import OutsailClient, OutsailApiError
|
|
12
|
+
|
|
13
|
+
async def main():
|
|
14
|
+
async with OutsailClient(api_key="osk_...") as client:
|
|
15
|
+
pack = await client.research("Research current OAuth migration guidance", profile="developer", discover=True)
|
|
16
|
+
print(pack.answer)
|
|
17
|
+
|
|
18
|
+
asyncio.run(main())
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For account-session authentication, call `await client.login(email, password)`, then construct a client with the returned session/API token. Production services should create scoped keys with `create_api_key()` and pass them as `api_key`; do not put keys in browser code.
|
|
22
|
+
|
|
23
|
+
## Research and streaming
|
|
24
|
+
|
|
25
|
+
Use `extract`, `map`, `discover`, `research`, `start_research`, `verify`, `compare`, `report`, and `decision_report` to build agent-ready workflows. `start_research` accepts the same full options as `research`, including provider, budget, cache, site, and Context Memory settings.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
job = await client.start_research("Compare database migration guides", discover=True, max_sources=12)
|
|
29
|
+
async for event in client.stream_research(job):
|
|
30
|
+
print(event.event_type, event.data)
|
|
31
|
+
|
|
32
|
+
report = await client.report(pack)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The SDK includes Context Packs, monitors (including RSS feeds), knowledge-base creation and ingestion, connectors, graph, memory, trust, workflows, API keys, organizations, notifications, billing, and self-service account controls.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
monitor = await client.create_monitor("Track release notes", rss_feeds=["https://example.com/feed.xml"])
|
|
39
|
+
base = await client.create_knowledge_base(name="Engineering docs")
|
|
40
|
+
await client.ingest_knowledge_base_documents(base["id"], [{"title": "Runbook", "content": "# Recovery"}])
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Connectors and platform controls
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
github = await client.create_github_connector("Engineering docs", token=os.environ["GITHUB_TOKEN"])
|
|
47
|
+
await client.sync_github("owner/repository", connector_id=github.id)
|
|
48
|
+
print(await client.operations())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`create_notion_connector`, `create_google_drive_connector`, and `create_slack_connector` return connector records. Call the matching `begin_*_authorization` method and open its URL in a browser, then run `sync_notion`, `sync_google_drive`, or `sync_slack`. The client also exposes graph, memory, trust-policy, workflow, Context Pack feedback, notifications, and usage operations for durable agent workflows.
|
|
52
|
+
|
|
53
|
+
## Errors and compatibility
|
|
54
|
+
|
|
55
|
+
Non-2xx API responses raise `OutsailApiError` (`OutsailError` remains a compatible alias), with `status` and parsed `body`. Supply `idempotency_key` when retrying research or ingestion. The SDK is version-locked to Outsail `0.28.0`; consult the served [OpenAPI reference](https://outsail.theatomicshift.com/openapi.json) for request and response details.
|
|
56
|
+
|
|
57
|
+
## Compatibility and deprecations
|
|
58
|
+
|
|
59
|
+
Use the SDK release that matches the platform’s `0.28.x` release. Breaking changes require a new major release; deprecated operations are announced in release notes before removal. Store API keys only in server-side environment variables and revoke keys promptly if they may have been exposed.
|
|
60
|
+
|
|
61
|
+
OAuth callbacks and inbound GitHub/Slack webhooks are browser/server integration flows, not SDK calls. Create a connector, open its returned authorization URL, then call its sync method.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "outsail-sdk"
|
|
7
|
+
version = "0.28.1"
|
|
8
|
+
description = "Python SDK for the Outsail Context Intelligence Platform"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
keywords = ["outsail", "context-intelligence", "ai", "research", "agents"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["httpx>=0.27.0"]
|
|
24
|
+
optional-dependencies = {dev = ["build>=1.2", "pytest>=8.0", "pytest-asyncio>=0.24.0", "twine>=5.0"]}
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
asyncio_mode = "auto"
|
|
28
|
+
filterwarnings = ["error"]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.package-data]
|
|
31
|
+
outsail = ["py.typed"]
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.package-dir]
|
|
34
|
+
"" = "src"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/theatomicshift/outsail"
|
|
41
|
+
Repository = "https://github.com/theatomicshift/outsail"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Outsail Python SDK — Context Intelligence for AI agents.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from outsail import OutsailClient
|
|
7
|
+
|
|
8
|
+
async def main():
|
|
9
|
+
async with OutsailClient(api_key="osk_...") as client:
|
|
10
|
+
pack = await client.research(objective="Assess OAuth guidance", discover=True)
|
|
11
|
+
print(pack.answer)
|
|
12
|
+
|
|
13
|
+
asyncio.run(main())
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from .client import OutsailClient
|
|
17
|
+
from .errors import OutsailApiError, OutsailError
|
|
18
|
+
from .streaming import SseEvent, stream_events
|
|
19
|
+
from . import agent_tools
|
|
20
|
+
from . import types
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"OutsailClient",
|
|
24
|
+
"OutsailError",
|
|
25
|
+
"OutsailApiError",
|
|
26
|
+
"SseEvent",
|
|
27
|
+
"stream_events",
|
|
28
|
+
"agent_tools",
|
|
29
|
+
"types",
|
|
30
|
+
]
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI and Claude tool definitions generated from the Outsail Capability
|
|
3
|
+
Registry. These functions let you register Outsail capabilities as tools
|
|
4
|
+
for OpenAI Responses / Chat Completions API and Claude Messages API.
|
|
5
|
+
|
|
6
|
+
Usage (OpenAI)::
|
|
7
|
+
|
|
8
|
+
from outsail import OutsailClient
|
|
9
|
+
from outsail.agent_tools import create_openai_tools, execute_tool_call
|
|
10
|
+
|
|
11
|
+
async with OutsailClient(base_url="...", api_key="...") as client:
|
|
12
|
+
tools = await create_openai_tools(client)
|
|
13
|
+
# Pass tools to OpenAI: tools parameter
|
|
14
|
+
# When the model returns a tool call:
|
|
15
|
+
result = await execute_tool_call(client, tool_call.function.name, json.loads(tool_call.function.arguments))
|
|
16
|
+
|
|
17
|
+
Usage (Claude)::
|
|
18
|
+
|
|
19
|
+
from outsail import OutsailClient
|
|
20
|
+
from outsail.agent_tools import create_claude_tools, execute_tool_call
|
|
21
|
+
|
|
22
|
+
async with OutsailClient(base_url="...", api_key="...") as client:
|
|
23
|
+
tools = await create_claude_tools(client)
|
|
24
|
+
# Pass tools to Claude: tools parameter
|
|
25
|
+
# When Claude uses a tool:
|
|
26
|
+
result = await execute_tool_call(client, tool_call.name, tool_call.input)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import asyncio
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from outsail.client import OutsailClient
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Tool definition generators
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
async def create_openai_tools(client: OutsailClient) -> list[dict]:
|
|
42
|
+
"""Fetch OpenAI-compatible tool definitions from the Outsail server.
|
|
43
|
+
|
|
44
|
+
Tools are generated from the curated Capability Registry — only
|
|
45
|
+
agent-safe operations are included.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
client: Authenticated OutsailClient instance.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of OpenAI function tool definitions.
|
|
52
|
+
"""
|
|
53
|
+
response = await client._request("GET", "/developer/tools/openai")
|
|
54
|
+
return response.get("tools", [])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def create_claude_tools(client: OutsailClient) -> list[dict]:
|
|
58
|
+
"""Fetch Claude-compatible tool definitions from the Outsail server.
|
|
59
|
+
|
|
60
|
+
Tools are generated from the curated Capability Registry — only
|
|
61
|
+
agent-safe operations are included.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
client: Authenticated OutsailClient instance.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of Claude tool definitions.
|
|
68
|
+
"""
|
|
69
|
+
response = await client._request("GET", "/developer/tools/claude")
|
|
70
|
+
return response.get("tools", [])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Tool call executor
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
TOOL_HANDLERS: dict[str, callable] = {
|
|
78
|
+
# ── Research ────────────────────────────────────────────────────
|
|
79
|
+
"outsail_research_start": lambda client, args: client.start_research(
|
|
80
|
+
objective=args.get("objective", ""),
|
|
81
|
+
urls=args.get("urls"),
|
|
82
|
+
profile=args.get("profile"),
|
|
83
|
+
depth=args.get("depth"),
|
|
84
|
+
discover=args.get("discover"),
|
|
85
|
+
max_sources=args.get("max_sources"),
|
|
86
|
+
),
|
|
87
|
+
"outsail_research_status": lambda client, args: client.get_research_job(args["id"]),
|
|
88
|
+
|
|
89
|
+
# ── Context Packs ───────────────────────────────────────────────
|
|
90
|
+
"outsail_context_pack_get": lambda client, args: client.get_context_pack(args["id"]),
|
|
91
|
+
"outsail_agent_context_get": lambda client, args: client.agent_context(
|
|
92
|
+
args["id"], profile=args.get("profile"),
|
|
93
|
+
),
|
|
94
|
+
|
|
95
|
+
# ── Core Tools ──────────────────────────────────────────────────
|
|
96
|
+
"outsail_verify": lambda client, args: client.verify(
|
|
97
|
+
args["claim"],
|
|
98
|
+
urls=args.get("urls"),
|
|
99
|
+
discover=args.get("discover"),
|
|
100
|
+
profile=args.get("profile"),
|
|
101
|
+
),
|
|
102
|
+
"outsail_extract": lambda client, args: client.extract(args["url"]),
|
|
103
|
+
"outsail_map": lambda client, args: client.map(
|
|
104
|
+
args["url"],
|
|
105
|
+
max_pages=args.get("max_pages"),
|
|
106
|
+
max_depth=args.get("max_depth"),
|
|
107
|
+
),
|
|
108
|
+
"outsail_compare": lambda client, args: client.compare(
|
|
109
|
+
args["items"],
|
|
110
|
+
objective=args.get("objective"),
|
|
111
|
+
profile=args.get("profile"),
|
|
112
|
+
depth=args.get("depth"),
|
|
113
|
+
discover=args.get("discover"),
|
|
114
|
+
),
|
|
115
|
+
"outsail_report": lambda client, args: client.report(args["context_pack"]),
|
|
116
|
+
"outsail_decision_report": lambda client, args: client.report(args["context_pack"]),
|
|
117
|
+
"outsail_discover": lambda client, args: client.discover(
|
|
118
|
+
args["query"],
|
|
119
|
+
profile=args.get("profile"),
|
|
120
|
+
providers=args.get("providers"),
|
|
121
|
+
limit=args.get("limit"),
|
|
122
|
+
),
|
|
123
|
+
|
|
124
|
+
# ── Monitors ────────────────────────────────────────────────────
|
|
125
|
+
"outsail_monitor_list": lambda client, _args: client.list_monitors(),
|
|
126
|
+
"outsail_monitor_get": lambda client, args: client.get_monitor(args["id"]),
|
|
127
|
+
"outsail_monitor_create": lambda client, args: client.create_monitor(
|
|
128
|
+
objective=args.get("objective", ""),
|
|
129
|
+
urls=args.get("urls"),
|
|
130
|
+
rss_feeds=args.get("rss_feeds"),
|
|
131
|
+
profile=args.get("profile"),
|
|
132
|
+
interval_minutes=args.get("interval_minutes"),
|
|
133
|
+
materiality_threshold=args.get("materiality_threshold"),
|
|
134
|
+
),
|
|
135
|
+
"outsail_monitor_run": lambda client, args: client.check_monitor(args["id"]),
|
|
136
|
+
"outsail_monitor_pause": lambda client, args: client.set_monitor_status(args["id"], "paused"),
|
|
137
|
+
"outsail_monitor_resume": lambda client, args: client.set_monitor_status(args["id"], "active"),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
async def execute_tool_call(
|
|
142
|
+
client: OutsailClient,
|
|
143
|
+
tool_name: str,
|
|
144
|
+
args: dict[str, Any],
|
|
145
|
+
) -> Any:
|
|
146
|
+
"""Execute a tool call by name with validated arguments.
|
|
147
|
+
|
|
148
|
+
Determines the correct SDK method from the tool name, calls it,
|
|
149
|
+
and returns the result. For long-running research tools, returns
|
|
150
|
+
a job handle — use ``poll_tool_result`` to wait for completion.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
client: Authenticated OutsailClient instance.
|
|
154
|
+
tool_name: Tool name (e.g. ``"outsail_research_start"``).
|
|
155
|
+
args: Tool arguments (validated against registry schema).
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Tool execution result.
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
ValueError: If the tool name is unknown.
|
|
162
|
+
OutsailApiError: If the API call fails.
|
|
163
|
+
"""
|
|
164
|
+
handler = TOOL_HANDLERS.get(tool_name)
|
|
165
|
+
if handler is None:
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"Unknown tool: {tool_name}. "
|
|
168
|
+
"Available tools can be fetched via create_openai_tools() or create_claude_tools()."
|
|
169
|
+
)
|
|
170
|
+
return await handler(client, args)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def poll_tool_result(
|
|
174
|
+
client: OutsailClient,
|
|
175
|
+
job_id: str,
|
|
176
|
+
*,
|
|
177
|
+
poll_interval_ms: int = 2000,
|
|
178
|
+
timeout_ms: int = 300_000,
|
|
179
|
+
) -> Any:
|
|
180
|
+
"""Poll a research job until completion.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
client: Authenticated OutsailClient instance.
|
|
184
|
+
job_id: Research job ID from :meth:`start_research` /
|
|
185
|
+
``outsail_research_start``.
|
|
186
|
+
poll_interval_ms: Polling interval in milliseconds (default 2000).
|
|
187
|
+
timeout_ms: Maximum wait time in milliseconds (default 300000 = 5 min).
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Completed job result with ``context_pack`` if successful.
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
TimeoutError: If the job does not complete within ``timeout_ms``.
|
|
194
|
+
RuntimeError: If the job fails.
|
|
195
|
+
"""
|
|
196
|
+
start_time = asyncio.get_event_loop().time()
|
|
197
|
+
deadline = start_time + (timeout_ms / 1000)
|
|
198
|
+
|
|
199
|
+
while True:
|
|
200
|
+
now = asyncio.get_event_loop().time()
|
|
201
|
+
if now >= deadline:
|
|
202
|
+
raise TimeoutError(f"Research job {job_id} timed out after {timeout_ms}ms")
|
|
203
|
+
|
|
204
|
+
await asyncio.sleep(poll_interval_ms / 1000)
|
|
205
|
+
|
|
206
|
+
job = await client.get_research_job(job_id)
|
|
207
|
+
|
|
208
|
+
if job.status == "completed":
|
|
209
|
+
return job
|
|
210
|
+
if job.status == "failed":
|
|
211
|
+
error_msg = getattr(job, "error", None) or "Unknown error"
|
|
212
|
+
raise RuntimeError(f"Research job {job_id} failed: {error_msg}")
|