forgesight 0.1.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.
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ *.so
9
+
10
+ # venv / tooling
11
+ .venv/
12
+ venv/
13
+ .uv/
14
+ uv.lock
15
+
16
+ # test / type / lint caches
17
+ .pytest_cache/
18
+ .mypy_cache/
19
+ .ruff_cache/
20
+ .coverage
21
+ .coverage.*
22
+ coverage.xml
23
+ htmlcov/
24
+
25
+ # secrets / local env (never commit)
26
+ .env
27
+ .env.*
28
+
29
+ # editor / OS
30
+ .DS_Store
31
+ .idea/
32
+ .vscode/
33
+
34
+ # local-only session working state (per the workspace pipeline)
35
+ .claude/state/
36
+
37
+ # local-only launch planning (not part of the published repo)
38
+ /launch/
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: forgesight
3
+ Version: 0.1.0
4
+ Summary: Vendor-neutral, OpenTelemetry-first telemetry for AI agents: traces, cost, budgets, and a tamper-evident audit trail — ship to any backend, no agent-code changes.
5
+ Project-URL: Homepage, https://github.com/Scaffoldic/forgesight
6
+ Project-URL: Repository, https://github.com/Scaffoldic/forgesight
7
+ Project-URL: Issues, https://github.com/Scaffoldic/forgesight/issues
8
+ Project-URL: Changelog, https://github.com/Scaffoldic/forgesight/blob/main/docs/releases/v0.1.md
9
+ Author: kjoshi
10
+ License-Expression: Apache-2.0
11
+ Keywords: agent-observability,ai-agents,audit,cost,finops,genai,governance,llm,llm-observability,observability,opentelemetry,otel,telemetry,tracing
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: System :: Monitoring
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: forgesight-core
25
+ Provides-Extra: adapters-crewai
26
+ Requires-Dist: forgesight-adapters-crewai[crewai]~=0.1.0; extra == 'adapters-crewai'
27
+ Provides-Extra: adapters-langgraph
28
+ Requires-Dist: forgesight-adapters-langgraph~=0.1.0; extra == 'adapters-langgraph'
29
+ Provides-Extra: all
30
+ Requires-Dist: forgesight-adapters-langgraph~=0.1.0; extra == 'all'
31
+ Requires-Dist: forgesight-audit~=0.1.0; extra == 'all'
32
+ Requires-Dist: forgesight-clickhouse~=0.1.0; extra == 'all'
33
+ Requires-Dist: forgesight-datadog~=0.1.0; extra == 'all'
34
+ Requires-Dist: forgesight-eval~=0.1.0; extra == 'all'
35
+ Requires-Dist: forgesight-fastapi~=0.1.0; extra == 'all'
36
+ Requires-Dist: forgesight-github~=0.1.0; extra == 'all'
37
+ Requires-Dist: forgesight-governance~=0.1.0; extra == 'all'
38
+ Requires-Dist: forgesight-langfuse~=0.1.0; extra == 'all'
39
+ Requires-Dist: forgesight-mcp~=0.1.0; extra == 'all'
40
+ Requires-Dist: forgesight-otel~=0.1.0; extra == 'all'
41
+ Requires-Dist: forgesight-prometheus~=0.1.0; extra == 'all'
42
+ Requires-Dist: forgesight-registry~=0.1.0; extra == 'all'
43
+ Provides-Extra: audit
44
+ Requires-Dist: forgesight-audit~=0.1.0; extra == 'audit'
45
+ Provides-Extra: clickhouse
46
+ Requires-Dist: forgesight-clickhouse~=0.1.0; extra == 'clickhouse'
47
+ Provides-Extra: datadog
48
+ Requires-Dist: forgesight-datadog~=0.1.0; extra == 'datadog'
49
+ Provides-Extra: eval
50
+ Requires-Dist: forgesight-eval~=0.1.0; extra == 'eval'
51
+ Provides-Extra: fastapi
52
+ Requires-Dist: forgesight-fastapi~=0.1.0; extra == 'fastapi'
53
+ Provides-Extra: github
54
+ Requires-Dist: forgesight-github~=0.1.0; extra == 'github'
55
+ Provides-Extra: governance
56
+ Requires-Dist: forgesight-governance~=0.1.0; extra == 'governance'
57
+ Provides-Extra: langfuse
58
+ Requires-Dist: forgesight-langfuse~=0.1.0; extra == 'langfuse'
59
+ Provides-Extra: mcp
60
+ Requires-Dist: forgesight-mcp~=0.1.0; extra == 'mcp'
61
+ Provides-Extra: otel
62
+ Requires-Dist: forgesight-otel~=0.1.0; extra == 'otel'
63
+ Provides-Extra: prometheus
64
+ Requires-Dist: forgesight-prometheus~=0.1.0; extra == 'prometheus'
65
+ Provides-Extra: registry
66
+ Requires-Dist: forgesight-registry~=0.1.0; extra == 'registry'
67
+ Description-Content-Type: text/markdown
68
+
69
+ # ForgeSight
70
+
71
+ **Instrument any AI agent in a few lines — then ship traces, cost, metrics, evals, budgets,
72
+ and a tamper-evident audit trail to any backend by changing one line of config.
73
+ OpenTelemetry-first. Vendor-neutral. Never an agent-code change.**
74
+
75
+ [![License](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/Scaffoldic/forgesight/blob/main/LICENSE)
76
+ [![Python](https://img.shields.io/badge/python-3.11_|_3.12_|_3.13-blue.svg)](https://pypi.org/project/forgesight/)
77
+
78
+ ▶️ **[See the 30-second demo](https://github.com/Scaffoldic/forgesight#forgesight)** — instrument
79
+ an agent and watch the trace, cost, and a verified tamper-evident audit trail appear.
80
+
81
+ `forgesight` is the batteries-included facade — the package most people install.
82
+
83
+ ```bash
84
+ pip install "forgesight[otel]"
85
+ ```
86
+
87
+ ```python
88
+ import forgesight
89
+ from forgesight import telemetry
90
+
91
+ forgesight.configure(exporters=["otel"]) # pick a backend by name — that's it
92
+
93
+ with telemetry.agent_run("pr-reviewer", version="2.1.0", metadata={"team": "platform"}) as run:
94
+ with run.llm_call("anthropic", "claude-sonnet-4-5") as call:
95
+ resp = await client.messages.create(...)
96
+ call.record_usage(input=resp.usage.input_tokens, output=resp.usage.output_tokens)
97
+ with run.tool_call("github_get_diff"):
98
+ diff = gh.get_diff(pr)
99
+ ```
100
+
101
+ Cost, token usage, trace nesting, metrics, and multi-backend fan-out come for free. Swap
102
+ `otel` → `langfuse`, `datadog`, `clickhouse`, `prometheus` — the agent code never moves.
103
+
104
+ ## What you get
105
+
106
+ - **🔌 Vendor-neutral.** The core depends on *no* backend or model-provider SDK. Backends are
107
+ packages you install and select by config.
108
+ - **📐 OpenTelemetry-first.** GenAI semantic conventions, so any OTLP backend works with no
109
+ dedicated package.
110
+ - **💰 Cost built in.** Token usage → USD via a pluggable pricing table — the same number
111
+ everywhere — plus live attribution by team and pre-call budget projection.
112
+ - **🚦 Non-blocking & fault-isolated.** Export runs on a background worker; a backend outage
113
+ never breaks a run.
114
+ - **🔒 Secure by default.** Content capture is opt-in; redaction runs before export.
115
+ - **🧾 Accountability.** A tamper-evident, hash-chained **audit trail** with a compliance
116
+ query/export surface.
117
+
118
+ ## Install backends & integrations as extras
119
+
120
+ ```bash
121
+ pip install "forgesight[otel]" # one backend (OTLP → any OTel platform)
122
+ pip install "forgesight[otel,langfuse,datadog]" # several
123
+ pip install "forgesight[all]" # everything except the heavy CrewAI tree
124
+ ```
125
+
126
+ Extras: `otel` · `prometheus` · `langfuse` · `clickhouse` · `datadog` · `mcp` · `fastapi` ·
127
+ `github` · `governance` · `eval` · `registry` · `audit` · `adapters-langgraph` ·
128
+ `adapters-crewai`. Installing a package *enables* a backend; config *selects* it.
129
+
130
+ ## Docs
131
+
132
+ - **[README & guides](https://github.com/Scaffoldic/forgesight)** — full docs, playbooks, runbooks
133
+ - **[Quick start](https://github.com/Scaffoldic/forgesight/blob/main/docs/playbooks/01-install.md)** ·
134
+ **[Run locally with Docker](https://github.com/Scaffoldic/forgesight/blob/main/docs/playbooks/03-run-locally-with-docker.md)** ·
135
+ **[Examples](https://github.com/Scaffoldic/forgesight/tree/main/examples)**
136
+
137
+ ## License
138
+
139
+ [Apache-2.0](https://github.com/Scaffoldic/forgesight/blob/main/LICENSE)
@@ -0,0 +1,71 @@
1
+ # ForgeSight
2
+
3
+ **Instrument any AI agent in a few lines — then ship traces, cost, metrics, evals, budgets,
4
+ and a tamper-evident audit trail to any backend by changing one line of config.
5
+ OpenTelemetry-first. Vendor-neutral. Never an agent-code change.**
6
+
7
+ [![License](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/Scaffoldic/forgesight/blob/main/LICENSE)
8
+ [![Python](https://img.shields.io/badge/python-3.11_|_3.12_|_3.13-blue.svg)](https://pypi.org/project/forgesight/)
9
+
10
+ ▶️ **[See the 30-second demo](https://github.com/Scaffoldic/forgesight#forgesight)** — instrument
11
+ an agent and watch the trace, cost, and a verified tamper-evident audit trail appear.
12
+
13
+ `forgesight` is the batteries-included facade — the package most people install.
14
+
15
+ ```bash
16
+ pip install "forgesight[otel]"
17
+ ```
18
+
19
+ ```python
20
+ import forgesight
21
+ from forgesight import telemetry
22
+
23
+ forgesight.configure(exporters=["otel"]) # pick a backend by name — that's it
24
+
25
+ with telemetry.agent_run("pr-reviewer", version="2.1.0", metadata={"team": "platform"}) as run:
26
+ with run.llm_call("anthropic", "claude-sonnet-4-5") as call:
27
+ resp = await client.messages.create(...)
28
+ call.record_usage(input=resp.usage.input_tokens, output=resp.usage.output_tokens)
29
+ with run.tool_call("github_get_diff"):
30
+ diff = gh.get_diff(pr)
31
+ ```
32
+
33
+ Cost, token usage, trace nesting, metrics, and multi-backend fan-out come for free. Swap
34
+ `otel` → `langfuse`, `datadog`, `clickhouse`, `prometheus` — the agent code never moves.
35
+
36
+ ## What you get
37
+
38
+ - **🔌 Vendor-neutral.** The core depends on *no* backend or model-provider SDK. Backends are
39
+ packages you install and select by config.
40
+ - **📐 OpenTelemetry-first.** GenAI semantic conventions, so any OTLP backend works with no
41
+ dedicated package.
42
+ - **💰 Cost built in.** Token usage → USD via a pluggable pricing table — the same number
43
+ everywhere — plus live attribution by team and pre-call budget projection.
44
+ - **🚦 Non-blocking & fault-isolated.** Export runs on a background worker; a backend outage
45
+ never breaks a run.
46
+ - **🔒 Secure by default.** Content capture is opt-in; redaction runs before export.
47
+ - **🧾 Accountability.** A tamper-evident, hash-chained **audit trail** with a compliance
48
+ query/export surface.
49
+
50
+ ## Install backends & integrations as extras
51
+
52
+ ```bash
53
+ pip install "forgesight[otel]" # one backend (OTLP → any OTel platform)
54
+ pip install "forgesight[otel,langfuse,datadog]" # several
55
+ pip install "forgesight[all]" # everything except the heavy CrewAI tree
56
+ ```
57
+
58
+ Extras: `otel` · `prometheus` · `langfuse` · `clickhouse` · `datadog` · `mcp` · `fastapi` ·
59
+ `github` · `governance` · `eval` · `registry` · `audit` · `adapters-langgraph` ·
60
+ `adapters-crewai`. Installing a package *enables* a backend; config *selects* it.
61
+
62
+ ## Docs
63
+
64
+ - **[README & guides](https://github.com/Scaffoldic/forgesight)** — full docs, playbooks, runbooks
65
+ - **[Quick start](https://github.com/Scaffoldic/forgesight/blob/main/docs/playbooks/01-install.md)** ·
66
+ **[Run locally with Docker](https://github.com/Scaffoldic/forgesight/blob/main/docs/playbooks/03-run-locally-with-docker.md)** ·
67
+ **[Examples](https://github.com/Scaffoldic/forgesight/tree/main/examples)**
68
+
69
+ ## License
70
+
71
+ [Apache-2.0](https://github.com/Scaffoldic/forgesight/blob/main/LICENSE)
@@ -0,0 +1,107 @@
1
+ [project]
2
+ name = "forgesight"
3
+ version = "0.1.0"
4
+ description = "Vendor-neutral, OpenTelemetry-first telemetry for AI agents: traces, cost, budgets, and a tamper-evident audit trail — ship to any backend, no agent-code changes."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "Apache-2.0"
8
+ authors = [{ name = "kjoshi" }]
9
+ keywords = [
10
+ "observability",
11
+ "telemetry",
12
+ "ai-agents",
13
+ "agent-observability",
14
+ "llm-observability",
15
+ "opentelemetry",
16
+ "otel",
17
+ "genai",
18
+ "llm",
19
+ "tracing",
20
+ "cost",
21
+ "finops",
22
+ "audit",
23
+ "governance",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 2 - Pre-Alpha",
27
+ "Intended Audience :: Developers",
28
+ "Intended Audience :: Information Technology",
29
+ "License :: OSI Approved :: Apache Software License",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Topic :: System :: Monitoring",
34
+ "Topic :: Software Development :: Libraries :: Python Modules",
35
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
36
+ "Typing :: Typed",
37
+ ]
38
+ dependencies = ["forgesight-core"]
39
+
40
+ # Backends & integrations are separate distributions; each extra pulls one at the matching
41
+ # coordinated version. `pip install "forgesight[otel,langfuse]"` installs the facade + those.
42
+ # Bump the `~= 0.1.0` pins together every release train (see launch/launch-checklist.md).
43
+ [project.optional-dependencies]
44
+ otel = ["forgesight-otel ~= 0.1.0"]
45
+ prometheus = ["forgesight-prometheus ~= 0.1.0"]
46
+ langfuse = ["forgesight-langfuse ~= 0.1.0"]
47
+ clickhouse = ["forgesight-clickhouse ~= 0.1.0"]
48
+ datadog = ["forgesight-datadog ~= 0.1.0"]
49
+ mcp = ["forgesight-mcp ~= 0.1.0"]
50
+ fastapi = ["forgesight-fastapi ~= 0.1.0"]
51
+ github = ["forgesight-github ~= 0.1.0"]
52
+ governance = ["forgesight-governance ~= 0.1.0"]
53
+ eval = ["forgesight-eval ~= 0.1.0"]
54
+ registry = ["forgesight-registry ~= 0.1.0"]
55
+ audit = ["forgesight-audit ~= 0.1.0"]
56
+ adapters-langgraph = ["forgesight-adapters-langgraph ~= 0.1.0"]
57
+ # crewai keeps its heavy framework behind an optional extra on the adapter — chain it so the
58
+ # framework is actually installed (otherwise the wrapper imports but the first call fails).
59
+ adapters-crewai = ["forgesight-adapters-crewai[crewai] ~= 0.1.0"]
60
+ # Everything except the heavy CrewAI tree (opt into that explicitly with [adapters-crewai]).
61
+ all = [
62
+ "forgesight-otel ~= 0.1.0",
63
+ "forgesight-prometheus ~= 0.1.0",
64
+ "forgesight-langfuse ~= 0.1.0",
65
+ "forgesight-clickhouse ~= 0.1.0",
66
+ "forgesight-datadog ~= 0.1.0",
67
+ "forgesight-mcp ~= 0.1.0",
68
+ "forgesight-fastapi ~= 0.1.0",
69
+ "forgesight-github ~= 0.1.0",
70
+ "forgesight-governance ~= 0.1.0",
71
+ "forgesight-eval ~= 0.1.0",
72
+ "forgesight-registry ~= 0.1.0",
73
+ "forgesight-audit ~= 0.1.0",
74
+ "forgesight-adapters-langgraph ~= 0.1.0",
75
+ ]
76
+
77
+ [project.urls]
78
+ Homepage = "https://github.com/Scaffoldic/forgesight"
79
+ Repository = "https://github.com/Scaffoldic/forgesight"
80
+ Issues = "https://github.com/Scaffoldic/forgesight/issues"
81
+ Changelog = "https://github.com/Scaffoldic/forgesight/blob/main/docs/releases/v0.1.md"
82
+
83
+ [build-system]
84
+ requires = ["hatchling"]
85
+ build-backend = "hatchling.build"
86
+
87
+ [tool.hatch.build.targets.wheel]
88
+ packages = ["src/forgesight"]
89
+
90
+ # Dev-time: resolve every ForgeSight package from the workspace (the version pins above are
91
+ # for PyPI installs). uv prefers these over PyPI when working in the workspace.
92
+ [tool.uv.sources]
93
+ forgesight-core = { workspace = true }
94
+ forgesight-otel = { workspace = true }
95
+ forgesight-prometheus = { workspace = true }
96
+ forgesight-langfuse = { workspace = true }
97
+ forgesight-clickhouse = { workspace = true }
98
+ forgesight-datadog = { workspace = true }
99
+ forgesight-mcp = { workspace = true }
100
+ forgesight-fastapi = { workspace = true }
101
+ forgesight-github = { workspace = true }
102
+ forgesight-governance = { workspace = true }
103
+ forgesight-eval = { workspace = true }
104
+ forgesight-registry = { workspace = true }
105
+ forgesight-audit = { workspace = true }
106
+ forgesight-adapters-langgraph = { workspace = true }
107
+ forgesight-adapters-crewai = { workspace = true }
@@ -0,0 +1,41 @@
1
+ """ForgeSight — the batteries-included facade.
2
+
3
+ ``import forgesight`` gives you ``configure()``, the ``telemetry`` instrumentation
4
+ facade, and the ``@instrument`` decorator. Backends are added by installing their
5
+ package (``forgesight-otel`` etc.) — never a code change here.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from forgesight_core import (
11
+ ConsoleExporter,
12
+ InMemoryExporter,
13
+ Telemetry,
14
+ configure,
15
+ current_run_scope,
16
+ get_runtime,
17
+ instrument,
18
+ register,
19
+ telemetry,
20
+ )
21
+
22
+ __version__ = "0.1.0"
23
+
24
+
25
+ def current_run() -> object | None:
26
+ """The active run scope, or ``None`` outside any run (alias of ``telemetry.current_run``)."""
27
+ return current_run_scope()
28
+
29
+
30
+ __all__ = [
31
+ "ConsoleExporter",
32
+ "InMemoryExporter",
33
+ "Telemetry",
34
+ "__version__",
35
+ "configure",
36
+ "current_run",
37
+ "get_runtime",
38
+ "instrument",
39
+ "register",
40
+ "telemetry",
41
+ ]
File without changes
@@ -0,0 +1,27 @@
1
+ """ForgeSight testing surface — re-exported from ``forgesight_core.testing``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from forgesight_core.testing import (
6
+ InMemoryExporter,
7
+ SpanData,
8
+ assert_span_tree,
9
+ build_spans,
10
+ find_span,
11
+ find_spans,
12
+ llm_call_factory,
13
+ token_usage_factory,
14
+ tool_call_factory,
15
+ )
16
+
17
+ __all__ = [
18
+ "InMemoryExporter",
19
+ "SpanData",
20
+ "assert_span_tree",
21
+ "build_spans",
22
+ "find_span",
23
+ "find_spans",
24
+ "llm_call_factory",
25
+ "token_usage_factory",
26
+ "tool_call_factory",
27
+ ]
@@ -0,0 +1,17 @@
1
+ """Per-SPI conformance suites — re-exported from ``forgesight_core.testing.conformance``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from forgesight_core.testing.conformance import (
6
+ run_event_listener_conformance,
7
+ run_exporter_conformance,
8
+ run_interceptor_conformance,
9
+ run_pricing_conformance,
10
+ )
11
+
12
+ __all__ = [
13
+ "run_event_listener_conformance",
14
+ "run_exporter_conformance",
15
+ "run_interceptor_conformance",
16
+ "run_pricing_conformance",
17
+ ]
@@ -0,0 +1,33 @@
1
+ """The `forgesight` facade re-exports the runtime and works end-to-end."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import forgesight
6
+ from forgesight_api import Kind
7
+
8
+
9
+ def test_facade_reexports() -> None:
10
+ assert callable(forgesight.configure)
11
+ assert callable(forgesight.instrument)
12
+ assert forgesight.telemetry is not None
13
+ assert forgesight.__version__
14
+
15
+
16
+ def test_end_to_end_via_facade() -> None:
17
+ mem = forgesight.InMemoryExporter()
18
+ forgesight.configure(exporters=[mem], service_name="my-agent")
19
+ assert forgesight.current_run() is None
20
+ with forgesight.telemetry.agent_run("issue-classifier", version="1.2.0") as run:
21
+ assert forgesight.current_run() is run
22
+ with run.llm_call(provider="anthropic", model="claude-sonnet-4-5") as call:
23
+ call.record_usage(input=1200, output=350, cache_read=800)
24
+ forgesight.get_runtime().force_flush() # drain the async pipeline
25
+ kinds = sorted({r.kind for r in mem.records})
26
+ assert kinds == [Kind.AGENT, Kind.LLM]
27
+ forgesight.get_runtime().shutdown()
28
+
29
+
30
+ def test_configure_default_console_exporter() -> None:
31
+ rt = forgesight.configure()
32
+ assert len(rt.exporters) == 1
33
+ assert isinstance(rt.exporters[0], forgesight.ConsoleExporter)
@@ -0,0 +1,24 @@
1
+ """The `forgesight.testing` facade re-exports the core testing surface."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import forgesight
6
+ from forgesight.testing import InMemoryExporter, assert_span_tree, find_span
7
+ from forgesight.testing.conformance import run_exporter_conformance
8
+
9
+
10
+ def test_testing_reexports_are_callable() -> None:
11
+ assert callable(assert_span_tree)
12
+ assert callable(find_span)
13
+ run_exporter_conformance(InMemoryExporter) # the re-exported suite runs
14
+
15
+
16
+ def test_end_to_end_via_facade_testing() -> None:
17
+ sink = InMemoryExporter()
18
+ forgesight.configure(exporters=[sink], sync_export=True)
19
+ try:
20
+ with forgesight.telemetry.agent_run("classifier"):
21
+ pass
22
+ assert_span_tree(sink, {"op": "invoke_agent", "name": "classifier"})
23
+ finally:
24
+ forgesight.get_runtime().shutdown()