interloper-toolkit 0.43.0__py3-none-any.whl
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/__init__.py +14 -0
- interloper_toolkit/analytics.py +153 -0
- interloper_toolkit/catalog.py +303 -0
- interloper_toolkit/collection.py +60 -0
- interloper_toolkit/context.py +55 -0
- interloper_toolkit/lineage.py +241 -0
- interloper_toolkit/models.py +386 -0
- interloper_toolkit/scheduling.py +172 -0
- interloper_toolkit-0.43.0.dist-info/METADATA +40 -0
- interloper_toolkit-0.43.0.dist-info/RECORD +11 -0
- interloper_toolkit-0.43.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Scheduling tools — jobs, runs, and backfills: read-only monitoring.
|
|
2
|
+
|
|
3
|
+
Mutating operations (toggling jobs, triggering runs and backfills) live with
|
|
4
|
+
the agent — this module is shared with surfaces that must stay read-only.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from interloper_toolkit.context import ToolkitContext
|
|
12
|
+
from interloper_toolkit.models import (
|
|
13
|
+
BackfillList,
|
|
14
|
+
FailureList,
|
|
15
|
+
JobHealth,
|
|
16
|
+
JobHealthStats,
|
|
17
|
+
JobList,
|
|
18
|
+
RunDetail,
|
|
19
|
+
RunErrorEvent,
|
|
20
|
+
RunFailure,
|
|
21
|
+
RunList,
|
|
22
|
+
ToolError,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# --- Jobs ---
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def list_jobs(ctx: ToolkitContext) -> JobList | ToolError:
|
|
29
|
+
"""List all scheduled jobs in the organisation.
|
|
30
|
+
|
|
31
|
+
Returns each job with its name, cron expression, enabled status,
|
|
32
|
+
last_run_at, and next_run_at.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
jobs = ctx.store.list_components(ctx.org_id, kinds=["job"])
|
|
36
|
+
return JobList(count=len(jobs), jobs=jobs)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return ToolError(error=str(e))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_job_health(ctx: ToolkitContext, component_id: str) -> JobHealth | ToolError:
|
|
42
|
+
"""Get health summary for a job: metadata, recent success/failure rate.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
component_id: UUID of the job to inspect.
|
|
46
|
+
|
|
47
|
+
Returns job metadata plus success rate computed from the last 20 runs.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
jid = UUID(component_id)
|
|
51
|
+
job = ctx.store.get_component(jid, kind="job")
|
|
52
|
+
runs = ctx.store.list_runs(ctx.org_id, component_id=jid, limit=20)
|
|
53
|
+
|
|
54
|
+
total = len(runs)
|
|
55
|
+
success = sum(1 for r in runs if r.status == "success")
|
|
56
|
+
failed = sum(1 for r in runs if r.status == "failed")
|
|
57
|
+
|
|
58
|
+
# Compute average duration for completed runs
|
|
59
|
+
durations = []
|
|
60
|
+
for r in runs:
|
|
61
|
+
if r.started_at and r.completed_at:
|
|
62
|
+
delta = r.completed_at - r.started_at
|
|
63
|
+
durations.append(delta.total_seconds())
|
|
64
|
+
avg_duration_seconds = sum(durations) / len(durations) if durations else None
|
|
65
|
+
|
|
66
|
+
return JobHealth(
|
|
67
|
+
job=job,
|
|
68
|
+
health=JobHealthStats(
|
|
69
|
+
total_recent_runs=total,
|
|
70
|
+
success_count=success,
|
|
71
|
+
failed_count=failed,
|
|
72
|
+
success_rate=round(success / total, 2) if total > 0 else None,
|
|
73
|
+
avg_duration_seconds=round(avg_duration_seconds, 1) if avg_duration_seconds else None,
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
return ToolError(error=str(e))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# --- Runs ---
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def list_recent_runs(
|
|
84
|
+
ctx: ToolkitContext,
|
|
85
|
+
component_id: str | None = None,
|
|
86
|
+
status: str | None = None,
|
|
87
|
+
limit: int = 20,
|
|
88
|
+
) -> RunList | ToolError:
|
|
89
|
+
"""List recent runs with optional filters.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
component_id: Filter by job UUID (optional).
|
|
93
|
+
status: Filter by status: 'queued', 'running', 'success', 'failed', 'canceled' (optional).
|
|
94
|
+
limit: Maximum number of runs to return (default 20).
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
runs = ctx.store.list_runs(
|
|
98
|
+
ctx.org_id,
|
|
99
|
+
component_id=UUID(component_id) if component_id else None,
|
|
100
|
+
status=status,
|
|
101
|
+
limit=limit,
|
|
102
|
+
)
|
|
103
|
+
return RunList(count=len(runs), runs=runs)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
return ToolError(error=str(e))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_run_detail(ctx: ToolkitContext, run_id: str) -> RunDetail | ToolError:
|
|
109
|
+
"""Get full detail for a single run including events and per-asset execution status.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
run_id: UUID of the run.
|
|
113
|
+
|
|
114
|
+
Returns the run metadata, event timeline, and per-asset execution summary.
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
rid = UUID(run_id)
|
|
118
|
+
run = ctx.store.get_run(rid)
|
|
119
|
+
events = ctx.store.list_events(run_id=rid)
|
|
120
|
+
asset_execs = ctx.store.list_asset_executions(rid)
|
|
121
|
+
|
|
122
|
+
return RunDetail(run=run, events=events, asset_executions=asset_execs)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
return ToolError(error=str(e))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def list_failures(ctx: ToolkitContext, limit: int = 20) -> FailureList | ToolError:
|
|
128
|
+
"""List recent failed runs with their error events.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
limit: Maximum number of failed runs to return (default 20).
|
|
132
|
+
|
|
133
|
+
Returns failed runs along with the error messages from their events.
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
failed_runs = ctx.store.list_runs(ctx.org_id, status="failed", limit=limit)
|
|
137
|
+
|
|
138
|
+
results = []
|
|
139
|
+
for run in failed_runs:
|
|
140
|
+
run_id = run.id
|
|
141
|
+
events = ctx.store.list_events(run_id=run_id)
|
|
142
|
+
errors = [
|
|
143
|
+
RunErrorEvent(asset_key=e.asset_key, error=e.error, timestamp=e.timestamp)
|
|
144
|
+
for e in events
|
|
145
|
+
if e.error
|
|
146
|
+
]
|
|
147
|
+
results.append(RunFailure(run=run, errors=errors))
|
|
148
|
+
|
|
149
|
+
return FailureList(count=len(results), failures=results)
|
|
150
|
+
except Exception as e:
|
|
151
|
+
return ToolError(error=str(e))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# --- Backfills ---
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def list_backfills(ctx: ToolkitContext, active_only: bool = True) -> BackfillList | ToolError:
|
|
158
|
+
"""List backfills, optionally filtered to active ones only.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
active_only: If true, only return running/queued backfills (default true).
|
|
162
|
+
|
|
163
|
+
Returns backfills with their status, date range, and partition progress.
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
if active_only:
|
|
167
|
+
backfills = ctx.store.list_active_backfills(ctx.org_id)
|
|
168
|
+
else:
|
|
169
|
+
backfills = ctx.store.list_backfills(ctx.org_id)
|
|
170
|
+
return BackfillList(count=len(backfills), backfills=backfills)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
return ToolError(error=str(e))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: interloper-toolkit
|
|
3
|
+
Version: 0.43.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).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interloper_toolkit/__init__.py,sha256=DMqH8CcEPWezTUTEhN4sQlMyBrBgj8kO0fg1GYlMU58,627
|
|
2
|
+
interloper_toolkit/analytics.py,sha256=QHzPmcp9i3iNS1bWXSU8rwCPsUs_hBVy0-H7ZkIdMlA,5178
|
|
3
|
+
interloper_toolkit/catalog.py,sha256=YwFEPMsY-7cehLIZjBT3GV9X1aZx3MFjQRkOYopPbXk,12084
|
|
4
|
+
interloper_toolkit/collection.py,sha256=akurHQcQ2gYPIRNR4QchpPiHxeOyO2lQI921YP_YnrM,2322
|
|
5
|
+
interloper_toolkit/context.py,sha256=Euc7r0dOzSr0zMhfHWLrw8iV1wJkdT9P84S-Ml7Yj5M,1583
|
|
6
|
+
interloper_toolkit/lineage.py,sha256=6ZkKlmzon9CTn3IM9do6qu3Sq6--RqcclObD3FTtcPI,8235
|
|
7
|
+
interloper_toolkit/models.py,sha256=IVKTu8EKZrk6w0RApO1U6yM5X6rNT0hi37S1iULcncU,9956
|
|
8
|
+
interloper_toolkit/scheduling.py,sha256=iSujke74Mj8SoeQn0qSonVY_RNvAAOmXS8mLMrAksbs,5440
|
|
9
|
+
interloper_toolkit-0.43.0.dist-info/WHEEL,sha256=s49dN1sxqzkgPplo4QuUaKomil-_cbDzeLK4-pZKD-A,81
|
|
10
|
+
interloper_toolkit-0.43.0.dist-info/METADATA,sha256=WhBZU3EN7cL7woYRI--yWAz5Bx2B7phDJtCGImsthAM,1830
|
|
11
|
+
interloper_toolkit-0.43.0.dist-info/RECORD,,
|