interloper-toolkit 0.89.0__tar.gz → 0.91.0__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.
- interloper_toolkit-0.91.0/PKG-INFO +49 -0
- interloper_toolkit-0.91.0/README.md +38 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/pyproject.toml +2 -2
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/pyproject.toml.orig +2 -2
- interloper_toolkit-0.91.0/src/interloper_toolkit/__init__.py +34 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/collection.py +4 -4
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/errors.py +3 -3
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/models.py +27 -1
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/scheduling.py +4 -1
- interloper_toolkit-0.91.0/src/interloper_toolkit/tools.py +158 -0
- interloper_toolkit-0.89.0/PKG-INFO +0 -40
- interloper_toolkit-0.89.0/README.md +0 -29
- interloper_toolkit-0.89.0/src/interloper_toolkit/__init__.py +0 -22
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/analytics.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/authz.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/catalog.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/context.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/jobs.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/lineage.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/sources.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/stats.py +0 -0
- {interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/utils.py +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: interloper-toolkit
|
|
3
|
+
Version: 0.91.0
|
|
4
|
+
Summary: Interloper tool functions shared by the AI surfaces (agent, MCP)
|
|
5
|
+
Author: Guillaume Onfroy
|
|
6
|
+
Author-email: Guillaume Onfroy <guillaume@digitlcloud.com>
|
|
7
|
+
Requires-Dist: interloper-core
|
|
8
|
+
Requires-Dist: interloper-db
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# interloper-toolkit
|
|
13
|
+
|
|
14
|
+
The tool functions shared by interloper's AI surfaces: the chat agent
|
|
15
|
+
(`interloper-agent`) and the MCP server (`interloper-mcp`).
|
|
16
|
+
|
|
17
|
+
Every function takes a frozen `ToolkitContext(store, catalog, org_id, role)`
|
|
18
|
+
as its first argument and returns `<SuccessModel> | ToolError`: typed
|
|
19
|
+
pydantic results (see `models.py`) discriminated by the literal `status`
|
|
20
|
+
field, never raising. The models are the tool contract: MCP derives per-tool
|
|
21
|
+
output schemas from them, row-projecting models act as an allowlist of what
|
|
22
|
+
leaves the platform, and full-row payloads embed the interloper-db models to
|
|
23
|
+
keep that coupling visible rather than duplicated. The docstrings are
|
|
24
|
+
LLM-facing: both surfaces adopt them verbatim as tool descriptions.
|
|
25
|
+
|
|
26
|
+
Reads take no role; writes declare the role they need with `requires_role`
|
|
27
|
+
and refuse below it, as a structured `ToolError`.
|
|
28
|
+
|
|
29
|
+
`tools.TOOLS` is the table both surfaces register: every tool with its
|
|
30
|
+
`Effect` (read, read through a provider, edit, create, launch, cancel). A
|
|
31
|
+
surface derives its behaviour from the effect (MCP annotations, the agent's
|
|
32
|
+
approvals) and `Tool.bind` adapts a function to the way the surface supplies
|
|
33
|
+
the context. A tool that `carries_secrets` in its arguments is left off any
|
|
34
|
+
surface that transports arguments through a third party.
|
|
35
|
+
|
|
36
|
+
Modules:
|
|
37
|
+
|
|
38
|
+
- `catalog`: the component definitions the platform ships (list, detail,
|
|
39
|
+
asset schemas, field search, schema comparison)
|
|
40
|
+
- `collection`: the org's component instances (listing, edits, relations,
|
|
41
|
+
connection checks and setup; sensitive kinds project identity only)
|
|
42
|
+
- `sources` and `jobs`: creating sources, resolving their fields, creating jobs
|
|
43
|
+
- `lineage`: dependency analysis, impact assessment, DAG traversal
|
|
44
|
+
- `scheduling`: jobs, runs and backfills, monitoring and control
|
|
45
|
+
- `analytics`: run statistics, partition coverage, data freshness
|
|
46
|
+
- `tools`: the table above
|
|
47
|
+
|
|
48
|
+
Depends only on `interloper-core` and `interloper-db`; no LLM-framework
|
|
49
|
+
dependencies (no pydantic-ai, no mcp).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# interloper-toolkit
|
|
2
|
+
|
|
3
|
+
The tool functions shared by interloper's AI surfaces: the chat agent
|
|
4
|
+
(`interloper-agent`) and the MCP server (`interloper-mcp`).
|
|
5
|
+
|
|
6
|
+
Every function takes a frozen `ToolkitContext(store, catalog, org_id, role)`
|
|
7
|
+
as its first argument and returns `<SuccessModel> | ToolError`: typed
|
|
8
|
+
pydantic results (see `models.py`) discriminated by the literal `status`
|
|
9
|
+
field, never raising. The models are the tool contract: MCP derives per-tool
|
|
10
|
+
output schemas from them, row-projecting models act as an allowlist of what
|
|
11
|
+
leaves the platform, and full-row payloads embed the interloper-db models to
|
|
12
|
+
keep that coupling visible rather than duplicated. The docstrings are
|
|
13
|
+
LLM-facing: both surfaces adopt them verbatim as tool descriptions.
|
|
14
|
+
|
|
15
|
+
Reads take no role; writes declare the role they need with `requires_role`
|
|
16
|
+
and refuse below it, as a structured `ToolError`.
|
|
17
|
+
|
|
18
|
+
`tools.TOOLS` is the table both surfaces register: every tool with its
|
|
19
|
+
`Effect` (read, read through a provider, edit, create, launch, cancel). A
|
|
20
|
+
surface derives its behaviour from the effect (MCP annotations, the agent's
|
|
21
|
+
approvals) and `Tool.bind` adapts a function to the way the surface supplies
|
|
22
|
+
the context. A tool that `carries_secrets` in its arguments is left off any
|
|
23
|
+
surface that transports arguments through a third party.
|
|
24
|
+
|
|
25
|
+
Modules:
|
|
26
|
+
|
|
27
|
+
- `catalog`: the component definitions the platform ships (list, detail,
|
|
28
|
+
asset schemas, field search, schema comparison)
|
|
29
|
+
- `collection`: the org's component instances (listing, edits, relations,
|
|
30
|
+
connection checks and setup; sensitive kinds project identity only)
|
|
31
|
+
- `sources` and `jobs`: creating sources, resolving their fields, creating jobs
|
|
32
|
+
- `lineage`: dependency analysis, impact assessment, DAG traversal
|
|
33
|
+
- `scheduling`: jobs, runs and backfills, monitoring and control
|
|
34
|
+
- `analytics`: run statistics, partition coverage, data freshness
|
|
35
|
+
- `tools`: the table above
|
|
36
|
+
|
|
37
|
+
Depends only on `interloper-core` and `interloper-db`; no LLM-framework
|
|
38
|
+
dependencies (no pydantic-ai, no mcp).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "interloper-toolkit"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "Interloper
|
|
3
|
+
version = "0.91.0"
|
|
4
|
+
description = "Interloper tool functions shared by the AI surfaces (agent, MCP)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
# ###############
|
|
4
4
|
[project]
|
|
5
5
|
name = "interloper-toolkit"
|
|
6
|
-
version = "0.
|
|
7
|
-
description = "Interloper
|
|
6
|
+
version = "0.91.0"
|
|
7
|
+
description = "Interloper tool functions shared by the AI surfaces (agent, MCP)"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
authors = [{ name = "Guillaume Onfroy", email = "guillaume@digitlcloud.com" }]
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Tool functions shared by the AI surfaces (agent, MCP server).
|
|
2
|
+
|
|
3
|
+
Every function takes a :class:`~interloper_toolkit.context.ToolkitContext`
|
|
4
|
+
as its first argument and returns ``<SuccessModel> | ToolError``: typed
|
|
5
|
+
pydantic results (see :mod:`interloper_toolkit.models`) discriminated by
|
|
6
|
+
the literal ``status`` field, never raising. The docstrings are LLM-facing:
|
|
7
|
+
the surfaces adopt them verbatim as tool descriptions.
|
|
8
|
+
|
|
9
|
+
Reads take no role; writes declare the role they need with
|
|
10
|
+
:func:`~interloper_toolkit.authz.requires_role` and refuse below it.
|
|
11
|
+
:data:`~interloper_toolkit.tools.TOOLS` is the table the surfaces register,
|
|
12
|
+
each tool with its :class:`~interloper_toolkit.tools.Effect`; a surface
|
|
13
|
+
derives its behaviour from that and leaves out what it cannot carry (the
|
|
14
|
+
MCP server never registers a tool whose arguments carry credentials).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from interloper_toolkit.authz import requires_role
|
|
18
|
+
from interloper_toolkit.collection import bind_relation, unbind_relation
|
|
19
|
+
from interloper_toolkit.context import ToolkitContext, serialize
|
|
20
|
+
from interloper_toolkit.models import ToolError, UserRequest
|
|
21
|
+
from interloper_toolkit.tools import TOOLS, Effect, Tool
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"TOOLS",
|
|
25
|
+
"Effect",
|
|
26
|
+
"Tool",
|
|
27
|
+
"ToolError",
|
|
28
|
+
"ToolkitContext",
|
|
29
|
+
"UserRequest",
|
|
30
|
+
"bind_relation",
|
|
31
|
+
"requires_role",
|
|
32
|
+
"serialize",
|
|
33
|
+
"unbind_relation",
|
|
34
|
+
]
|
{interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/collection.py
RENAMED
|
@@ -17,7 +17,7 @@ from typing import Any
|
|
|
17
17
|
from urllib.parse import urlencode
|
|
18
18
|
from uuid import UUID
|
|
19
19
|
|
|
20
|
-
import
|
|
20
|
+
import httpx2
|
|
21
21
|
from interloper.component import KINDS
|
|
22
22
|
from interloper.connection.base import Connection
|
|
23
23
|
from interloper.errors import (
|
|
@@ -466,12 +466,12 @@ def categorise(exc: Exception) -> tuple[str, str]:
|
|
|
466
466
|
"""
|
|
467
467
|
if isinstance(exc, ConnectionCheckError):
|
|
468
468
|
return "error", str(exc)
|
|
469
|
-
if isinstance(exc,
|
|
469
|
+
if isinstance(exc, httpx2.HTTPStatusError):
|
|
470
470
|
if exc.response.status_code in (401, 403):
|
|
471
471
|
return "auth", "The provider rejected the credentials."
|
|
472
472
|
return "error", f"The provider responded with HTTP {exc.response.status_code}."
|
|
473
|
-
if isinstance(exc, (TimeoutError,
|
|
473
|
+
if isinstance(exc, (TimeoutError, httpx2.TimeoutException)):
|
|
474
474
|
return "network", "The provider did not respond in time."
|
|
475
|
-
if isinstance(exc,
|
|
475
|
+
if isinstance(exc, httpx2.TransportError):
|
|
476
476
|
return "network", "The provider could not be reached."
|
|
477
477
|
return "error", "The connection check failed unexpectedly."
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Every error the platform records went through
|
|
4
4
|
:func:`interloper.errors.format_exception`, so it opens with the exception's
|
|
5
5
|
type name; what follows is the library's own message. Three families carry
|
|
6
|
-
an HTTP request in a recognisable shape:
|
|
6
|
+
an HTTP request in a recognisable shape: httpx2 (``Client error '429 …' for
|
|
7
7
|
url '…'``), google-api-core (``403 POST https://…: message``) and the
|
|
8
8
|
Facebook SDK (``Method:`` / ``Path:`` / ``Status:`` lines over a JSON body
|
|
9
9
|
with ``code`` and ``error_subcode``). Everything else is grouped by its
|
|
@@ -49,8 +49,8 @@ def classify(text: str) -> ErrorCause:
|
|
|
49
49
|
|
|
50
50
|
status: int | None = None
|
|
51
51
|
method: str | None = None
|
|
52
|
-
if
|
|
53
|
-
status = int(
|
|
52
|
+
if httpx2 := _HTTPX_STATUS.search(message):
|
|
53
|
+
status = int(httpx2.group(1))
|
|
54
54
|
elif google := _GOOGLE_STATUS.match(message.strip()):
|
|
55
55
|
status = int(google.group(1))
|
|
56
56
|
method = google.group(2)
|
|
@@ -228,7 +228,24 @@ class ConnectionsCreated(BaseModel):
|
|
|
228
228
|
failed: list[FailedInstance]
|
|
229
229
|
|
|
230
230
|
|
|
231
|
-
class
|
|
231
|
+
class UserRequest(BaseModel):
|
|
232
|
+
"""A result that asks the user for something the model cannot supply.
|
|
233
|
+
|
|
234
|
+
An interactive surface takes it to the user and answers the call with
|
|
235
|
+
their input; elsewhere it is an ordinary result the model relays.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def awaits_user(self) -> bool:
|
|
240
|
+
"""Whether the user is being asked.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
True unless the request resolved in place after all.
|
|
244
|
+
"""
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class ConnectionSetup(UserRequest):
|
|
232
249
|
"""The hand-off ``request_connection_setup`` makes.
|
|
233
250
|
|
|
234
251
|
``setup_url`` opens the app's connection form for this definition, when
|
|
@@ -246,6 +263,15 @@ class ConnectionSetup(BaseModel):
|
|
|
246
263
|
setup_url: str | None = None
|
|
247
264
|
existing: list[ComponentRef] = []
|
|
248
265
|
|
|
266
|
+
@property
|
|
267
|
+
def awaits_user(self) -> bool:
|
|
268
|
+
"""Whether the form is presented.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
False when the existing connections were listed instead.
|
|
272
|
+
"""
|
|
273
|
+
return not self.existing
|
|
274
|
+
|
|
249
275
|
|
|
250
276
|
class ConnectionCheck(BaseModel):
|
|
251
277
|
"""The outcome of a connection's health check.
|
{interloper_toolkit-0.89.0 → interloper_toolkit-0.91.0}/src/interloper_toolkit/scheduling.py
RENAMED
|
@@ -229,7 +229,10 @@ def retry_run(ctx: ToolkitContext, run_id: str, scope: str = "all") -> RunRetrie
|
|
|
229
229
|
the operations that failed or were canceled.
|
|
230
230
|
|
|
231
231
|
Returns the queued attempt; its ``attempt`` number and ``root_run_id``
|
|
232
|
-
tie it to the run it retries.
|
|
232
|
+
tie it to the run it retries. The retry continues from the stack's latest
|
|
233
|
+
attempt, so retrying an earlier attempt retries the stack. A run that is
|
|
234
|
+
not failed, or a stack whose latest attempt is pending or succeeded,
|
|
235
|
+
cannot be retried.
|
|
233
236
|
"""
|
|
234
237
|
try:
|
|
235
238
|
ctx.store.runs.get(UUID(run_id), org_id=ctx.org_id)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""The tool table: every tool the AI surfaces expose, with its effect on the platform.
|
|
2
|
+
|
|
3
|
+
A surface (the agent, the MCP server) registers this table rather than a
|
|
4
|
+
list of its own. The effect is the one fact a surface derives its behaviour
|
|
5
|
+
from: MCP maps it to the annotations a client keys on, the agent to which
|
|
6
|
+
calls wait for the user's approval. ``carries_secrets`` marks a tool whose
|
|
7
|
+
arguments would carry credentials; a surface that transports arguments
|
|
8
|
+
through a third party leaves it out.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import inspect
|
|
14
|
+
import types
|
|
15
|
+
import typing
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from interloper_toolkit import analytics, catalog, collection, jobs, lineage, scheduling, sources
|
|
22
|
+
from interloper_toolkit.context import ToolkitContext
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Effect(str, Enum):
|
|
26
|
+
"""What a tool does to the platform."""
|
|
27
|
+
|
|
28
|
+
READ = "read"
|
|
29
|
+
READ_PROVIDER = "read_provider"
|
|
30
|
+
EDIT = "edit"
|
|
31
|
+
CREATE = "create"
|
|
32
|
+
LAUNCH = "launch"
|
|
33
|
+
CANCEL = "cancel"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Tool:
|
|
38
|
+
"""A toolkit function as the surfaces expose it.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
fn: The toolkit function, sync or async, taking the context first.
|
|
42
|
+
effect: What it does to the platform.
|
|
43
|
+
carries_secrets: Whether its arguments carry credentials.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
fn: types.FunctionType
|
|
47
|
+
effect: Effect
|
|
48
|
+
carries_secrets: bool = False
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def name(self) -> str:
|
|
52
|
+
"""The tool's name, which is the function's.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The function name.
|
|
56
|
+
"""
|
|
57
|
+
return self.fn.__name__
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def needs_approval(self) -> bool:
|
|
61
|
+
"""Whether an interactive surface waits for the user before the call runs.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
True for creates and cancels, the calls a user wants to confirm.
|
|
65
|
+
"""
|
|
66
|
+
return self.effect in (Effect.CREATE, Effect.CANCEL)
|
|
67
|
+
|
|
68
|
+
def bind(self, context: Callable[..., ToolkitContext], *leading: inspect.Parameter) -> types.FunctionType:
|
|
69
|
+
"""Adapt the function to a surface that supplies the context itself.
|
|
70
|
+
|
|
71
|
+
The bound function keeps the name, docstring and every parameter but
|
|
72
|
+
the context, so the schema a model sees is exactly the toolkit's.
|
|
73
|
+
``leading`` are the surface's own parameters in the context's place,
|
|
74
|
+
if any; ``context`` receives their values and returns the context to
|
|
75
|
+
call with.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
context: Derives the toolkit context from the leading arguments.
|
|
79
|
+
*leading: Parameters the bound function takes before the toolkit's own.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The bound function, with an explicit signature and annotations.
|
|
83
|
+
"""
|
|
84
|
+
signature = inspect.signature(self.fn)
|
|
85
|
+
hints = typing.get_type_hints(self.fn)
|
|
86
|
+
_, *rest = signature.parameters
|
|
87
|
+
parameters = [*leading, *(signature.parameters[name].replace(annotation=hints[name]) for name in rest)]
|
|
88
|
+
returns = hints.get("return", Any)
|
|
89
|
+
split = len(leading)
|
|
90
|
+
|
|
91
|
+
if inspect.iscoroutinefunction(self.fn):
|
|
92
|
+
|
|
93
|
+
async def tool(*args: Any, **kwargs: Any) -> Any:
|
|
94
|
+
return await self.fn(context(*args[:split]), *args[split:], **kwargs)
|
|
95
|
+
else:
|
|
96
|
+
|
|
97
|
+
def tool(*args: Any, **kwargs: Any) -> Any:
|
|
98
|
+
return self.fn(context(*args[:split]), *args[split:], **kwargs)
|
|
99
|
+
|
|
100
|
+
tool.__name__ = tool.__qualname__ = self.name
|
|
101
|
+
tool.__module__ = self.fn.__module__
|
|
102
|
+
tool.__doc__ = self.fn.__doc__
|
|
103
|
+
tool.__signature__ = signature.replace(parameters=parameters, return_annotation=returns) # ty: ignore[invalid-assignment]
|
|
104
|
+
tool.__annotations__ = {p.name: p.annotation for p in parameters} | {"return": returns}
|
|
105
|
+
return tool
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
TOOLS: tuple[Tool, ...] = (
|
|
109
|
+
# Catalog
|
|
110
|
+
Tool(catalog.list_definitions, Effect.READ),
|
|
111
|
+
Tool(catalog.get_definition, Effect.READ),
|
|
112
|
+
Tool(catalog.get_asset_schema, Effect.READ),
|
|
113
|
+
Tool(catalog.search_fields, Effect.READ),
|
|
114
|
+
Tool(catalog.compare_schemas, Effect.READ),
|
|
115
|
+
# Collection
|
|
116
|
+
Tool(collection.list_components, Effect.READ),
|
|
117
|
+
Tool(collection.update_component, Effect.EDIT),
|
|
118
|
+
Tool(collection.bind_relation, Effect.EDIT),
|
|
119
|
+
Tool(collection.unbind_relation, Effect.EDIT),
|
|
120
|
+
Tool(collection.request_connection_setup, Effect.READ),
|
|
121
|
+
Tool(collection.check_connection, Effect.READ_PROVIDER),
|
|
122
|
+
Tool(collection.create_connections, Effect.CREATE, carries_secrets=True),
|
|
123
|
+
# Sources and jobs
|
|
124
|
+
Tool(sources.resolve_source_field_options, Effect.READ_PROVIDER),
|
|
125
|
+
Tool(sources.create_source, Effect.CREATE),
|
|
126
|
+
Tool(sources.create_sources, Effect.CREATE),
|
|
127
|
+
Tool(jobs.create_job, Effect.CREATE),
|
|
128
|
+
# Lineage
|
|
129
|
+
Tool(lineage.get_upstream, Effect.READ),
|
|
130
|
+
Tool(lineage.get_downstream, Effect.READ),
|
|
131
|
+
Tool(lineage.get_full_lineage, Effect.READ),
|
|
132
|
+
Tool(lineage.impact_analysis, Effect.READ),
|
|
133
|
+
Tool(lineage.cross_source_dependencies, Effect.READ),
|
|
134
|
+
# Scheduling
|
|
135
|
+
Tool(scheduling.list_jobs, Effect.READ),
|
|
136
|
+
Tool(scheduling.get_job_health, Effect.READ),
|
|
137
|
+
Tool(scheduling.toggle_job, Effect.EDIT),
|
|
138
|
+
Tool(scheduling.toggle_asset, Effect.EDIT),
|
|
139
|
+
Tool(scheduling.list_recent_runs, Effect.READ),
|
|
140
|
+
Tool(scheduling.get_run_detail, Effect.READ),
|
|
141
|
+
Tool(scheduling.list_run_events, Effect.READ),
|
|
142
|
+
Tool(scheduling.get_event, Effect.READ),
|
|
143
|
+
Tool(scheduling.list_failures, Effect.READ),
|
|
144
|
+
Tool(scheduling.error_breakdown, Effect.READ),
|
|
145
|
+
Tool(scheduling.trigger_run, Effect.LAUNCH),
|
|
146
|
+
Tool(scheduling.retry_run, Effect.LAUNCH),
|
|
147
|
+
Tool(scheduling.list_backfills, Effect.READ),
|
|
148
|
+
Tool(scheduling.backfill_timeline, Effect.READ),
|
|
149
|
+
Tool(scheduling.trigger_backfill, Effect.LAUNCH),
|
|
150
|
+
Tool(scheduling.cancel_backfill, Effect.CANCEL),
|
|
151
|
+
# Analytics
|
|
152
|
+
Tool(analytics.run_history_summary, Effect.READ),
|
|
153
|
+
Tool(analytics.partition_coverage, Effect.READ),
|
|
154
|
+
Tool(analytics.freshness_check, Effect.READ),
|
|
155
|
+
Tool(analytics.run_stats, Effect.READ),
|
|
156
|
+
Tool(analytics.asset_coverage, Effect.READ),
|
|
157
|
+
)
|
|
158
|
+
"""Every tool the surfaces expose."""
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: interloper-toolkit
|
|
3
|
-
Version: 0.89.0
|
|
4
|
-
Summary: Interloper shared read-only tool functions for AI surfaces (agent, MCP)
|
|
5
|
-
Author: Guillaume Onfroy
|
|
6
|
-
Author-email: Guillaume Onfroy <guillaume@digitlcloud.com>
|
|
7
|
-
Requires-Dist: interloper-core
|
|
8
|
-
Requires-Dist: interloper-db
|
|
9
|
-
Requires-Python: >=3.10
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
|
|
12
|
-
# interloper-toolkit
|
|
13
|
-
|
|
14
|
-
The read-only tool functions shared by interloper's AI surfaces — the ADK
|
|
15
|
-
chat agent (`interloper-agent`) and the MCP server (`interloper-mcp`).
|
|
16
|
-
|
|
17
|
-
Every function takes a frozen `ToolkitContext(store, catalog, org_id)` as its
|
|
18
|
-
first argument and returns `<SuccessModel> | ToolError` — typed pydantic
|
|
19
|
-
results (see `models.py`) discriminated by the literal `status` field, never
|
|
20
|
-
raising. The models are the tool contract: MCP derives per-tool output
|
|
21
|
-
schemas from them, row-projecting models act as an allowlist of what leaves
|
|
22
|
-
the platform, and full-row payloads embed the interloper-db models to keep
|
|
23
|
-
that coupling visible rather than duplicated. The docstrings are LLM-facing:
|
|
24
|
-
both consumers surface them verbatim as tool descriptions.
|
|
25
|
-
|
|
26
|
-
Modules:
|
|
27
|
-
|
|
28
|
-
- `catalog` — the component definitions the platform ships (list, detail,
|
|
29
|
-
asset schemas, field search, schema comparison)
|
|
30
|
-
- `collection` — the org's component instances (read-only listing; sensitive
|
|
31
|
-
kinds project identity/metadata only)
|
|
32
|
-
- `lineage` — dependency analysis, impact assessment, DAG traversal
|
|
33
|
-
- `scheduling` — jobs, runs, and backfills: read-only monitoring
|
|
34
|
-
- `analytics` — run statistics, partition coverage, data freshness
|
|
35
|
-
|
|
36
|
-
Mutating operations (trigger/toggle/create) deliberately live with the agent,
|
|
37
|
-
not here — this package is safe to expose on read-only surfaces.
|
|
38
|
-
|
|
39
|
-
Depends only on `interloper-core` and `interloper-db`; no LLM-framework
|
|
40
|
-
dependencies (no google-adk, no mcp).
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# interloper-toolkit
|
|
2
|
-
|
|
3
|
-
The read-only tool functions shared by interloper's AI surfaces — the ADK
|
|
4
|
-
chat agent (`interloper-agent`) and the MCP server (`interloper-mcp`).
|
|
5
|
-
|
|
6
|
-
Every function takes a frozen `ToolkitContext(store, catalog, org_id)` as its
|
|
7
|
-
first argument and returns `<SuccessModel> | ToolError` — typed pydantic
|
|
8
|
-
results (see `models.py`) discriminated by the literal `status` field, never
|
|
9
|
-
raising. The models are the tool contract: MCP derives per-tool output
|
|
10
|
-
schemas from them, row-projecting models act as an allowlist of what leaves
|
|
11
|
-
the platform, and full-row payloads embed the interloper-db models to keep
|
|
12
|
-
that coupling visible rather than duplicated. The docstrings are LLM-facing:
|
|
13
|
-
both consumers surface them verbatim as tool descriptions.
|
|
14
|
-
|
|
15
|
-
Modules:
|
|
16
|
-
|
|
17
|
-
- `catalog` — the component definitions the platform ships (list, detail,
|
|
18
|
-
asset schemas, field search, schema comparison)
|
|
19
|
-
- `collection` — the org's component instances (read-only listing; sensitive
|
|
20
|
-
kinds project identity/metadata only)
|
|
21
|
-
- `lineage` — dependency analysis, impact assessment, DAG traversal
|
|
22
|
-
- `scheduling` — jobs, runs, and backfills: read-only monitoring
|
|
23
|
-
- `analytics` — run statistics, partition coverage, data freshness
|
|
24
|
-
|
|
25
|
-
Mutating operations (trigger/toggle/create) deliberately live with the agent,
|
|
26
|
-
not here — this package is safe to expose on read-only surfaces.
|
|
27
|
-
|
|
28
|
-
Depends only on `interloper-core` and `interloper-db`; no LLM-framework
|
|
29
|
-
dependencies (no google-adk, no mcp).
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""Tool functions shared by AI surfaces (agent, MCP server).
|
|
2
|
-
|
|
3
|
-
Every function takes a :class:`~interloper_toolkit.context.ToolkitContext`
|
|
4
|
-
as its first argument and returns ``<SuccessModel> | ToolError`` — typed
|
|
5
|
-
pydantic results (see :mod:`interloper_toolkit.models`) discriminated by
|
|
6
|
-
the literal ``status`` field, never raising. The docstrings are LLM-facing:
|
|
7
|
-
both the ADK agent and the MCP server surface them verbatim as tool
|
|
8
|
-
descriptions.
|
|
9
|
-
|
|
10
|
-
Reads take no role; writes declare the role they need with
|
|
11
|
-
:func:`~interloper_toolkit.authz.requires_role` and refuse below it. Which
|
|
12
|
-
functions a surface exposes is that surface's registration list (the MCP
|
|
13
|
-
server, for one, never registers ``create_connections``, whose arguments
|
|
14
|
-
would carry credentials through the client).
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
from interloper_toolkit.authz import requires_role
|
|
18
|
-
from interloper_toolkit.collection import bind_relation, unbind_relation
|
|
19
|
-
from interloper_toolkit.context import ToolkitContext, serialize
|
|
20
|
-
from interloper_toolkit.models import ToolError
|
|
21
|
-
|
|
22
|
-
__all__ = ["ToolError", "ToolkitContext", "bind_relation", "requires_role", "serialize", "unbind_relation"]
|
|
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
|