ioa-observe-sdk 1.0.6__tar.gz → 1.0.8__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.
- {ioa_observe_sdk-1.0.6/ioa_observe_sdk.egg-info → ioa_observe_sdk-1.0.8}/PKG-INFO +99 -47
- ioa_observe_sdk-1.0.8/README.md +143 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/decorators/__init__.py +22 -10
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/decorators/base.py +82 -23
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/decorators/util.py +17 -10
- ioa_observe_sdk-1.0.8/ioa_observe/sdk/instrumentations/a2a.py +111 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/instrumentations/slim.py +18 -18
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/__init__.py +2 -2
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/context_utils.py +19 -19
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/tracing.py +48 -28
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8/ioa_observe_sdk.egg-info}/PKG-INFO +99 -47
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe_sdk.egg-info/SOURCES.txt +1 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe_sdk.egg-info/requires.txt +46 -46
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/pyproject.toml +47 -47
- ioa_observe_sdk-1.0.6/README.md +0 -91
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/LICENSE.md +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/client/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/client/client.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/client/http.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/config/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/connectors/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/connectors/slim.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/instrumentations/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/instruments.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/logging/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/logging/logging.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agent.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/agent_connections.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/availability.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/heuristics.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/recovery_tracker.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/tool_call_tracker.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/agents/tracker.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/metrics/metrics.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/telemetry.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/content_allow_list.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/context_manager.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/tracing/manual.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/utils/__init__.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/utils/const.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/utils/in_memory_span_exporter.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/utils/json_encoder.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/utils/package_check.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe/sdk/version.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe_sdk.egg-info/dependency_links.txt +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/ioa_observe_sdk.egg-info/top_level.txt +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/setup.cfg +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/tests/test_client.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/tests/test_instrumentor.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/tests/test_manual_instrumentation.py +0 -0
- {ioa_observe_sdk-1.0.6 → ioa_observe_sdk-1.0.8}/tests/test_version.py +0 -0
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ioa-observe-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: IOA Observability SDK
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE.md
|
|
8
|
-
Requires-Dist: aiohappyeyeballs
|
|
9
|
-
Requires-Dist: aiohttp
|
|
10
|
-
Requires-Dist: aiosignal
|
|
11
|
-
Requires-Dist: annotated-types
|
|
12
|
-
Requires-Dist: anyio
|
|
13
|
-
Requires-Dist: async-timeout
|
|
14
|
-
Requires-Dist: attrs
|
|
15
|
-
Requires-Dist: backoff
|
|
16
|
-
Requires-Dist: certifi
|
|
17
|
-
Requires-Dist: charset-normalizer
|
|
8
|
+
Requires-Dist: aiohappyeyeballs>=2.4.8
|
|
9
|
+
Requires-Dist: aiohttp>=3.11.18
|
|
10
|
+
Requires-Dist: aiosignal>=1.3.2
|
|
11
|
+
Requires-Dist: annotated-types>=0.7.0
|
|
12
|
+
Requires-Dist: anyio>=4.8.0
|
|
13
|
+
Requires-Dist: async-timeout>=4.0.3
|
|
14
|
+
Requires-Dist: attrs>=25.1.0
|
|
15
|
+
Requires-Dist: backoff>=2.2.1
|
|
16
|
+
Requires-Dist: certifi>=2025.1.31
|
|
17
|
+
Requires-Dist: charset-normalizer>=3.4.1
|
|
18
18
|
Requires-Dist: colorama==0.4.6
|
|
19
|
-
Requires-Dist: Deprecated
|
|
20
|
-
Requires-Dist: distro
|
|
21
|
-
Requires-Dist: exceptiongroup
|
|
22
|
-
Requires-Dist: frozenlist
|
|
23
|
-
Requires-Dist: googleapis-common-protos
|
|
24
|
-
Requires-Dist: grpcio
|
|
25
|
-
Requires-Dist: h11
|
|
26
|
-
Requires-Dist: httpcore
|
|
27
|
-
Requires-Dist: httpx
|
|
28
|
-
Requires-Dist: idna
|
|
29
|
-
Requires-Dist: importlib_metadata
|
|
30
|
-
Requires-Dist: Jinja2
|
|
31
|
-
Requires-Dist: jiter
|
|
32
|
-
Requires-Dist: MarkupSafe
|
|
33
|
-
Requires-Dist: monotonic
|
|
34
|
-
Requires-Dist: multidict
|
|
35
|
-
Requires-Dist: openai
|
|
19
|
+
Requires-Dist: Deprecated>=1.2.18
|
|
20
|
+
Requires-Dist: distro>=1.9.0
|
|
21
|
+
Requires-Dist: exceptiongroup>=1.2.2
|
|
22
|
+
Requires-Dist: frozenlist>=1.5.0
|
|
23
|
+
Requires-Dist: googleapis-common-protos>=1.69.0
|
|
24
|
+
Requires-Dist: grpcio>=1.70.0
|
|
25
|
+
Requires-Dist: h11>=0.16.0
|
|
26
|
+
Requires-Dist: httpcore>=1.0.9
|
|
27
|
+
Requires-Dist: httpx>=0.28.1
|
|
28
|
+
Requires-Dist: idna>=3.10
|
|
29
|
+
Requires-Dist: importlib_metadata>=8.5.0
|
|
30
|
+
Requires-Dist: Jinja2>=3.1.6
|
|
31
|
+
Requires-Dist: jiter>=0.8.2
|
|
32
|
+
Requires-Dist: MarkupSafe>=3.0.2
|
|
33
|
+
Requires-Dist: monotonic>=1.6
|
|
34
|
+
Requires-Dist: multidict>=6.1.0
|
|
35
|
+
Requires-Dist: openai>=1.75.0
|
|
36
36
|
Requires-Dist: opentelemetry-api==1.33.1
|
|
37
37
|
Requires-Dist: opentelemetry-distro
|
|
38
38
|
Requires-Dist: opentelemetry-exporter-otlp==1.33.1
|
|
@@ -52,32 +52,32 @@ Requires-Dist: opentelemetry-sdk==1.33.1
|
|
|
52
52
|
Requires-Dist: opentelemetry-semantic-conventions==0.54b1
|
|
53
53
|
Requires-Dist: opentelemetry-semantic-conventions-ai==0.4.9
|
|
54
54
|
Requires-Dist: opentelemetry-util-http==0.54b1
|
|
55
|
-
Requires-Dist: packaging
|
|
56
|
-
Requires-Dist: propcache
|
|
57
|
-
Requires-Dist: protobuf
|
|
58
|
-
Requires-Dist: pydantic
|
|
59
|
-
Requires-Dist: pydantic_core
|
|
60
|
-
Requires-Dist: python-dateutil
|
|
55
|
+
Requires-Dist: packaging>=24.2
|
|
56
|
+
Requires-Dist: propcache>=0.3.0
|
|
57
|
+
Requires-Dist: protobuf>=5.29.3
|
|
58
|
+
Requires-Dist: pydantic>=2.10.6
|
|
59
|
+
Requires-Dist: pydantic_core>=2.27.2
|
|
60
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
61
61
|
Requires-Dist: regex==2024.11.6
|
|
62
|
-
Requires-Dist: requests
|
|
63
|
-
Requires-Dist: six
|
|
64
|
-
Requires-Dist: sniffio
|
|
65
|
-
Requires-Dist: tenacity
|
|
66
|
-
Requires-Dist: tiktoken
|
|
67
|
-
Requires-Dist: tqdm
|
|
68
|
-
Requires-Dist: typing_extensions
|
|
69
|
-
Requires-Dist: urllib3
|
|
70
|
-
Requires-Dist: wrapt
|
|
71
|
-
Requires-Dist: yarl
|
|
72
|
-
Requires-Dist: zipp
|
|
62
|
+
Requires-Dist: requests>=2.32.3
|
|
63
|
+
Requires-Dist: six>=1.17.0
|
|
64
|
+
Requires-Dist: sniffio>=1.3.1
|
|
65
|
+
Requires-Dist: tenacity>=9.0.0
|
|
66
|
+
Requires-Dist: tiktoken>=0.9.0
|
|
67
|
+
Requires-Dist: tqdm>=4.67.1
|
|
68
|
+
Requires-Dist: typing_extensions>=4.12.2
|
|
69
|
+
Requires-Dist: urllib3>=2.3.0
|
|
70
|
+
Requires-Dist: wrapt>=1.17.2
|
|
71
|
+
Requires-Dist: yarl>=1.18.3
|
|
72
|
+
Requires-Dist: zipp>=3.21.0
|
|
73
73
|
Requires-Dist: langgraph>=0.3.2
|
|
74
74
|
Requires-Dist: langchain>=0.3.19
|
|
75
75
|
Requires-Dist: langchain-openai>=0.3.8
|
|
76
76
|
Requires-Dist: langchain-community>=0.3.25
|
|
77
77
|
Requires-Dist: llama-index>=0.12.34
|
|
78
78
|
Requires-Dist: opentelemetry-instrumentation-requests
|
|
79
|
-
Requires-Dist: opentelemetry-instrumentation-transformers>=0.40.
|
|
80
|
-
Requires-Dist: opentelemetry-instrumentation-crewai>=0.40.
|
|
79
|
+
Requires-Dist: opentelemetry-instrumentation-transformers>=0.40.8
|
|
80
|
+
Requires-Dist: opentelemetry-instrumentation-crewai>=0.40.8
|
|
81
81
|
Requires-Dist: llama-index-utils-workflow>=0.3.1
|
|
82
82
|
Requires-Dist: pytest
|
|
83
83
|
Requires-Dist: pytest-vcr
|
|
@@ -85,6 +85,8 @@ Dynamic: license-file
|
|
|
85
85
|
|
|
86
86
|
# Observe-SDK
|
|
87
87
|
|
|
88
|
+
[](https://pypi.org/project/ioa-observe-sdk/)
|
|
89
|
+
|
|
88
90
|
IOA observability SDK for your multi-agentic application.
|
|
89
91
|
|
|
90
92
|
## Table of Contents
|
|
@@ -119,6 +121,20 @@ Link: [AGNTCY Observability Schema](https://github.com/agntcy/observe/blob/main/
|
|
|
119
121
|
|
|
120
122
|
## Dev
|
|
121
123
|
|
|
124
|
+
Any Opentelemetry compatible backend can be used, but for this guide, we will use ClickhouseDB as the backend database.
|
|
125
|
+
|
|
126
|
+
### Opentelemetry collector
|
|
127
|
+
|
|
128
|
+
The OpenTelemetry Collector offers a vendor-agnostic implementation of how to receive, process and export telemetry data. It removes the need to run, operate, and maintain multiple agents/collectors.
|
|
129
|
+
|
|
130
|
+
### Clickhouse DB
|
|
131
|
+
|
|
132
|
+
ClickhouseDB is used as a backend database to store and query the collected telemetry data efficiently, enabling you to analyze and visualize observability information for your multi-agentic applications.
|
|
133
|
+
|
|
134
|
+
### Grafana (optional)
|
|
135
|
+
|
|
136
|
+
Grafana can be used to visualize the telemetry data collected by the OpenTelemetry Collector and stored in ClickhouseDB.
|
|
137
|
+
|
|
122
138
|
To get started with development, start a Clickhouse DB and an OTel collector container locally using docker-compose like so:
|
|
123
139
|
|
|
124
140
|
```
|
|
@@ -126,6 +142,8 @@ cd deploy/
|
|
|
126
142
|
docker compose up -d
|
|
127
143
|
```
|
|
128
144
|
|
|
145
|
+
Running both locally allows you to test, monitor, and debug your observability setup in a development environment before deploying to production.
|
|
146
|
+
|
|
129
147
|
Ensure the contents of `otel-collector.yaml` is correct.
|
|
130
148
|
|
|
131
149
|
Check the logs of the collector to ensure it is running correctly:
|
|
@@ -134,6 +152,18 @@ Check the logs of the collector to ensure it is running correctly:
|
|
|
134
152
|
docker logs -f otel-collector
|
|
135
153
|
```
|
|
136
154
|
|
|
155
|
+
Viewing data in Clickhouse DB can be done using the Clickhouse client. The collector is configured to export telemetry data to Clickhouse.
|
|
156
|
+
|
|
157
|
+
The clickhouse exporter creates various tables in the Clickhouse DB, including `otel_traces`, which is used to store trace data.
|
|
158
|
+
|
|
159
|
+
For more info, refer to the [OpenTelemetry Clickhouse Exporter documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/clickhouseexporter/README.md)
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
docker exec -it clickhouse-server clickhouse-client
|
|
163
|
+
|
|
164
|
+
select * from otel_traces LIMIT 10;
|
|
165
|
+
```
|
|
166
|
+
|
|
137
167
|
Create a `.env` file with the following content:
|
|
138
168
|
|
|
139
169
|
```bash
|
|
@@ -165,6 +195,28 @@ OPENAI_API_KEY=<KEY> make test
|
|
|
165
195
|
For getting started with the SDK, please refer to the [Getting Started](https://github.com/agntcy/observe/blob/main/GETTING-STARTED.md)
|
|
166
196
|
file. It contains detailed instructions on how to set up and use the SDK effectively.
|
|
167
197
|
|
|
198
|
+
### Grafana
|
|
199
|
+
|
|
200
|
+
To configure Grafana to visualize the telemetry data, follow these steps:
|
|
201
|
+
|
|
202
|
+
1. Spin up Grafana locally using Docker:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
docker run -d -p 3000:3000 --name=grafana grafana/grafana
|
|
206
|
+
```
|
|
207
|
+
2. Access Grafana by navigating to `http://localhost:3000` in your web browser.
|
|
208
|
+
- Default username: `admin`
|
|
209
|
+
- Default password: `admin`
|
|
210
|
+
|
|
211
|
+
3. Add a new data source:
|
|
212
|
+
- Choose "ClickHouse" as the data source type.
|
|
213
|
+
- Set the URL to `http://0.0.0.0:8123`.
|
|
214
|
+
- Configure the authentication settings if necessary.
|
|
215
|
+
- Save and test the connection to ensure it works correctly.
|
|
216
|
+
|
|
217
|
+
Refer to the [Grafana ClickHouse plugin documentation](https://grafana.com/grafana/plugins/grafana-clickhouse-datasource/) for more details on configuring ClickHouse as a data source.
|
|
218
|
+
|
|
219
|
+
|
|
168
220
|
## Contributing
|
|
169
221
|
|
|
170
222
|
Contributions are welcome! Please follow these steps to contribute:
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Observe-SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/ioa-observe-sdk/)
|
|
4
|
+
|
|
5
|
+
IOA observability SDK for your multi-agentic application.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Schema](#schema)
|
|
11
|
+
- [Dev](#dev)
|
|
12
|
+
- [Testing](#testing)
|
|
13
|
+
- [Getting Started](#getting-started)
|
|
14
|
+
- [Contributing](#contributing)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
To install the package via PyPI, simply run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install ioa_observe_sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Alternatively, to download the SDK from git, you could also use the following command. Ensure you have `uv` installed in your environment.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv add "git+https://github.com/agntcy/observe"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Schema
|
|
31
|
+
|
|
32
|
+
The AGNTCY observability schema is an extension of the OTel LLM Semantic Conventions for Generative AI systems.
|
|
33
|
+
This schema is designed to provide comprehensive observability for Multi-Agent Systems (MAS).
|
|
34
|
+
|
|
35
|
+
Link: [AGNTCY Observability Schema](https://github.com/agntcy/observe/blob/main/schema/)
|
|
36
|
+
|
|
37
|
+
## Dev
|
|
38
|
+
|
|
39
|
+
Any Opentelemetry compatible backend can be used, but for this guide, we will use ClickhouseDB as the backend database.
|
|
40
|
+
|
|
41
|
+
### Opentelemetry collector
|
|
42
|
+
|
|
43
|
+
The OpenTelemetry Collector offers a vendor-agnostic implementation of how to receive, process and export telemetry data. It removes the need to run, operate, and maintain multiple agents/collectors.
|
|
44
|
+
|
|
45
|
+
### Clickhouse DB
|
|
46
|
+
|
|
47
|
+
ClickhouseDB is used as a backend database to store and query the collected telemetry data efficiently, enabling you to analyze and visualize observability information for your multi-agentic applications.
|
|
48
|
+
|
|
49
|
+
### Grafana (optional)
|
|
50
|
+
|
|
51
|
+
Grafana can be used to visualize the telemetry data collected by the OpenTelemetry Collector and stored in ClickhouseDB.
|
|
52
|
+
|
|
53
|
+
To get started with development, start a Clickhouse DB and an OTel collector container locally using docker-compose like so:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
cd deploy/
|
|
57
|
+
docker compose up -d
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Running both locally allows you to test, monitor, and debug your observability setup in a development environment before deploying to production.
|
|
61
|
+
|
|
62
|
+
Ensure the contents of `otel-collector.yaml` is correct.
|
|
63
|
+
|
|
64
|
+
Check the logs of the collector to ensure it is running correctly:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
docker logs -f otel-collector
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Viewing data in Clickhouse DB can be done using the Clickhouse client. The collector is configured to export telemetry data to Clickhouse.
|
|
71
|
+
|
|
72
|
+
The clickhouse exporter creates various tables in the Clickhouse DB, including `otel_traces`, which is used to store trace data.
|
|
73
|
+
|
|
74
|
+
For more info, refer to the [OpenTelemetry Clickhouse Exporter documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/clickhouseexporter/README.md)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
docker exec -it clickhouse-server clickhouse-client
|
|
78
|
+
|
|
79
|
+
select * from otel_traces LIMIT 10;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Create a `.env` file with the following content:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
OTLP_HTTP_ENDPOINT=http://localhost:4318
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Install the dependencies and activate the virtual environment:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
set -a
|
|
92
|
+
source .env
|
|
93
|
+
set +a
|
|
94
|
+
|
|
95
|
+
python3 -m venv .venv
|
|
96
|
+
source .venv/bin/activate
|
|
97
|
+
uv sync
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Testing
|
|
101
|
+
|
|
102
|
+
To run the unit tests, ensure you have the `OPENAI_API_KEY` set in your environment. You can run the tests using the following command:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
OPENAI_API_KEY=<KEY> make test
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 🚀 Getting Started
|
|
109
|
+
|
|
110
|
+
For getting started with the SDK, please refer to the [Getting Started](https://github.com/agntcy/observe/blob/main/GETTING-STARTED.md)
|
|
111
|
+
file. It contains detailed instructions on how to set up and use the SDK effectively.
|
|
112
|
+
|
|
113
|
+
### Grafana
|
|
114
|
+
|
|
115
|
+
To configure Grafana to visualize the telemetry data, follow these steps:
|
|
116
|
+
|
|
117
|
+
1. Spin up Grafana locally using Docker:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
docker run -d -p 3000:3000 --name=grafana grafana/grafana
|
|
121
|
+
```
|
|
122
|
+
2. Access Grafana by navigating to `http://localhost:3000` in your web browser.
|
|
123
|
+
- Default username: `admin`
|
|
124
|
+
- Default password: `admin`
|
|
125
|
+
|
|
126
|
+
3. Add a new data source:
|
|
127
|
+
- Choose "ClickHouse" as the data source type.
|
|
128
|
+
- Set the URL to `http://0.0.0.0:8123`.
|
|
129
|
+
- Configure the authentication settings if necessary.
|
|
130
|
+
- Save and test the connection to ensure it works correctly.
|
|
131
|
+
|
|
132
|
+
Refer to the [Grafana ClickHouse plugin documentation](https://grafana.com/grafana/plugins/grafana-clickhouse-datasource/) for more details on configuring ClickHouse as a data source.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## Contributing
|
|
136
|
+
|
|
137
|
+
Contributions are welcome! Please follow these steps to contribute:
|
|
138
|
+
|
|
139
|
+
1. Fork the repository.
|
|
140
|
+
2. Create a new branch (`git checkout -b feature-branch`).
|
|
141
|
+
3. Commit your changes (`git commit -am 'Add new feature'`).
|
|
142
|
+
4. Push to the branch (`git push origin feature-branch`).
|
|
143
|
+
5. Create a new Pull Request.
|
|
@@ -19,15 +19,22 @@ F = TypeVar("F", bound=Callable[P, Union[R, Awaitable[R]]])
|
|
|
19
19
|
|
|
20
20
|
def task(
|
|
21
21
|
name: Optional[str] = None,
|
|
22
|
+
description: Optional[str] = None,
|
|
22
23
|
version: Optional[int] = None,
|
|
23
24
|
method_name: Optional[str] = None,
|
|
24
25
|
tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
|
|
25
26
|
) -> Callable[[F], F]:
|
|
26
27
|
if method_name is None:
|
|
27
|
-
return entity_method(
|
|
28
|
+
return entity_method(
|
|
29
|
+
name=name,
|
|
30
|
+
description=description,
|
|
31
|
+
version=version,
|
|
32
|
+
tlp_span_kind=tlp_span_kind,
|
|
33
|
+
)
|
|
28
34
|
else:
|
|
29
35
|
return entity_class(
|
|
30
36
|
name=name,
|
|
37
|
+
description=description,
|
|
31
38
|
version=version,
|
|
32
39
|
method_name=method_name,
|
|
33
40
|
tlp_span_kind=tlp_span_kind,
|
|
@@ -36,21 +43,21 @@ def task(
|
|
|
36
43
|
|
|
37
44
|
def workflow(
|
|
38
45
|
name: Optional[str] = None,
|
|
46
|
+
description: Optional[str] = None,
|
|
39
47
|
version: Optional[int] = None,
|
|
40
48
|
method_name: Optional[str] = None,
|
|
41
49
|
tlp_span_kind: Optional[
|
|
42
50
|
Union[ObserveSpanKindValues, str]
|
|
43
51
|
] = ObserveSpanKindValues.WORKFLOW,
|
|
44
52
|
) -> Callable[[F], F]:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
53
|
+
# Always use entity_class for class decorators
|
|
54
|
+
return entity_class(
|
|
55
|
+
name=name,
|
|
56
|
+
description=description,
|
|
57
|
+
version=version,
|
|
58
|
+
method_name=method_name,
|
|
59
|
+
tlp_span_kind=tlp_span_kind,
|
|
60
|
+
)
|
|
54
61
|
|
|
55
62
|
|
|
56
63
|
def graph(
|
|
@@ -60,6 +67,7 @@ def graph(
|
|
|
60
67
|
) -> Callable[[F], F]:
|
|
61
68
|
return workflow(
|
|
62
69
|
name=name,
|
|
70
|
+
description=None,
|
|
63
71
|
version=version,
|
|
64
72
|
method_name=method_name,
|
|
65
73
|
tlp_span_kind="graph",
|
|
@@ -68,11 +76,13 @@ def graph(
|
|
|
68
76
|
|
|
69
77
|
def agent(
|
|
70
78
|
name: Optional[str] = None,
|
|
79
|
+
description: Optional[str] = None,
|
|
71
80
|
version: Optional[int] = None,
|
|
72
81
|
method_name: Optional[str] = None,
|
|
73
82
|
) -> Callable[[F], F]:
|
|
74
83
|
return workflow(
|
|
75
84
|
name=name,
|
|
85
|
+
description=description,
|
|
76
86
|
version=version,
|
|
77
87
|
method_name=method_name,
|
|
78
88
|
tlp_span_kind=ObserveSpanKindValues.AGENT,
|
|
@@ -81,11 +91,13 @@ def agent(
|
|
|
81
91
|
|
|
82
92
|
def tool(
|
|
83
93
|
name: Optional[str] = None,
|
|
94
|
+
description: Optional[str] = None,
|
|
84
95
|
version: Optional[int] = None,
|
|
85
96
|
method_name: Optional[str] = None,
|
|
86
97
|
) -> Callable[[F], F]:
|
|
87
98
|
return task(
|
|
88
99
|
name=name,
|
|
100
|
+
description=description,
|
|
89
101
|
version=version,
|
|
90
102
|
method_name=method_name,
|
|
91
103
|
tlp_span_kind=ObserveSpanKindValues.TOOL,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
|
+
import time
|
|
5
6
|
import traceback
|
|
6
7
|
from functools import wraps
|
|
7
8
|
import os
|
|
@@ -91,6 +92,7 @@ def _setup_span(
|
|
|
91
92
|
entity_name,
|
|
92
93
|
tlp_span_kind: Optional[ObserveSpanKindValues] = None,
|
|
93
94
|
version: Optional[int] = None,
|
|
95
|
+
description: Optional[str] = None,
|
|
94
96
|
):
|
|
95
97
|
"""Sets up the OpenTelemetry span and context"""
|
|
96
98
|
if tlp_span_kind in [
|
|
@@ -100,8 +102,8 @@ def _setup_span(
|
|
|
100
102
|
]:
|
|
101
103
|
set_workflow_name(entity_name)
|
|
102
104
|
# if tlp_span_kind == "graph":
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
+
# session_id = entity_name + "_" + str(uuid.uuid4())
|
|
106
|
+
# set_session_id(session_id)
|
|
105
107
|
if tlp_span_kind == "graph":
|
|
106
108
|
span_name = f"{entity_name}.{tlp_span_kind}"
|
|
107
109
|
else:
|
|
@@ -122,13 +124,14 @@ def _setup_span(
|
|
|
122
124
|
"agent_start_event",
|
|
123
125
|
{
|
|
124
126
|
"agent_name": entity_name,
|
|
127
|
+
"description": description if description else "",
|
|
125
128
|
"type": tlp_span_kind.value,
|
|
126
129
|
},
|
|
127
130
|
)
|
|
128
131
|
# start_span.end() # end the span immediately
|
|
129
|
-
#
|
|
130
|
-
# if
|
|
131
|
-
# span.set_attribute("
|
|
132
|
+
# session_id = get_value("session.id")
|
|
133
|
+
# if session_id is not None:
|
|
134
|
+
# span.set_attribute("session.id", session_id)
|
|
132
135
|
if tlp_span_kind in [
|
|
133
136
|
ObserveSpanKindValues.TASK,
|
|
134
137
|
ObserveSpanKindValues.TOOL,
|
|
@@ -145,9 +148,8 @@ def _setup_span(
|
|
|
145
148
|
if version:
|
|
146
149
|
span.set_attribute(OBSERVE_ENTITY_VERSION, version)
|
|
147
150
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# span.set_attribute("execution.id", execution_id)
|
|
151
|
+
if tlp_span_kind == ObserveSpanKindValues.AGENT:
|
|
152
|
+
span.set_attribute("agent_chain_start_time", time.time())
|
|
151
153
|
|
|
152
154
|
return span, ctx, ctx_token
|
|
153
155
|
|
|
@@ -243,12 +245,27 @@ def _handle_span_output(span, tlp_span_kind, res, cls=None):
|
|
|
243
245
|
|
|
244
246
|
def _cleanup_span(span, ctx_token):
|
|
245
247
|
"""End the span process and detach the context token"""
|
|
248
|
+
|
|
249
|
+
# Calculate agent chain completion time before ending span
|
|
250
|
+
span_kind = span.attributes.get(OBSERVE_SPAN_KIND)
|
|
251
|
+
if span_kind == ObserveSpanKindValues.AGENT.value:
|
|
252
|
+
start_time = span.attributes.get("agent_chain_start_time")
|
|
253
|
+
if start_time is not None:
|
|
254
|
+
import time
|
|
255
|
+
|
|
256
|
+
completion_time = time.time() - start_time
|
|
257
|
+
|
|
258
|
+
# Emit the metric
|
|
259
|
+
TracerWrapper().agent_chain_completion_time_histogram.record(
|
|
260
|
+
completion_time, attributes=span.attributes
|
|
261
|
+
)
|
|
246
262
|
span.end()
|
|
247
263
|
context_api.detach(ctx_token)
|
|
248
264
|
|
|
249
265
|
|
|
250
266
|
def entity_method(
|
|
251
267
|
name: Optional[str] = None,
|
|
268
|
+
description: Optional[str] = None,
|
|
252
269
|
version: Optional[int] = None,
|
|
253
270
|
tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
|
|
254
271
|
) -> Callable[[F], F]:
|
|
@@ -266,7 +283,10 @@ def entity_method(
|
|
|
266
283
|
return
|
|
267
284
|
|
|
268
285
|
span, ctx, ctx_token = _setup_span(
|
|
269
|
-
entity_name,
|
|
286
|
+
entity_name,
|
|
287
|
+
tlp_span_kind,
|
|
288
|
+
version,
|
|
289
|
+
description if description else None,
|
|
270
290
|
)
|
|
271
291
|
_handle_span_input(span, args, kwargs, cls=JSONEncoder)
|
|
272
292
|
|
|
@@ -284,7 +304,10 @@ def entity_method(
|
|
|
284
304
|
return await fn(*args, **kwargs)
|
|
285
305
|
|
|
286
306
|
span, ctx, ctx_token = _setup_span(
|
|
287
|
-
entity_name,
|
|
307
|
+
entity_name,
|
|
308
|
+
tlp_span_kind,
|
|
309
|
+
version,
|
|
310
|
+
description if description else None,
|
|
288
311
|
)
|
|
289
312
|
_handle_span_input(span, args, kwargs, cls=JSONEncoder)
|
|
290
313
|
success = False
|
|
@@ -359,6 +382,8 @@ def entity_method(
|
|
|
359
382
|
_handle_agent_failure_event(str(e), span, tlp_span_kind)
|
|
360
383
|
raise e
|
|
361
384
|
finally:
|
|
385
|
+
if tlp_span_kind == ObserveSpanKindValues.AGENT:
|
|
386
|
+
TracerWrapper().record_agent_execution(entity_name, success)
|
|
362
387
|
_cleanup_span(span, ctx_token)
|
|
363
388
|
return res
|
|
364
389
|
|
|
@@ -370,10 +395,15 @@ def entity_method(
|
|
|
370
395
|
if not TracerWrapper.verify_initialized():
|
|
371
396
|
return fn(*args, **kwargs)
|
|
372
397
|
|
|
373
|
-
span, ctx, ctx_token = _setup_span(
|
|
398
|
+
span, ctx, ctx_token = _setup_span(
|
|
399
|
+
entity_name,
|
|
400
|
+
tlp_span_kind,
|
|
401
|
+
version,
|
|
402
|
+
description if description else None,
|
|
403
|
+
)
|
|
374
404
|
|
|
375
405
|
_handle_span_input(span, args, kwargs, cls=JSONEncoder)
|
|
376
|
-
_handle_agent_span(span, entity_name, tlp_span_kind)
|
|
406
|
+
_handle_agent_span(span, entity_name, description, tlp_span_kind)
|
|
377
407
|
success = False
|
|
378
408
|
|
|
379
409
|
# Record heartbeat for agent
|
|
@@ -459,6 +489,8 @@ def entity_method(
|
|
|
459
489
|
_handle_agent_failure_event(str(e), span, tlp_span_kind)
|
|
460
490
|
raise e
|
|
461
491
|
finally:
|
|
492
|
+
if tlp_span_kind == ObserveSpanKindValues.AGENT:
|
|
493
|
+
TracerWrapper().record_agent_execution(entity_name, success)
|
|
462
494
|
_cleanup_span(span, ctx_token)
|
|
463
495
|
return res
|
|
464
496
|
|
|
@@ -469,26 +501,51 @@ def entity_method(
|
|
|
469
501
|
|
|
470
502
|
def entity_class(
|
|
471
503
|
name: Optional[str],
|
|
504
|
+
description: Optional[str],
|
|
472
505
|
version: Optional[int],
|
|
473
|
-
method_name: str,
|
|
506
|
+
method_name: Optional[str],
|
|
474
507
|
tlp_span_kind: Optional[ObserveSpanKindValues] = ObserveSpanKindValues.TASK,
|
|
475
508
|
):
|
|
476
509
|
def decorator(cls):
|
|
477
510
|
task_name = name if name else camel_to_snake(cls.__qualname__)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
511
|
+
|
|
512
|
+
methods_to_wrap = []
|
|
513
|
+
|
|
514
|
+
if method_name:
|
|
515
|
+
# Specific method specified - existing behavior
|
|
516
|
+
methods_to_wrap = [method_name]
|
|
517
|
+
else:
|
|
518
|
+
# No method specified - wrap all public methods
|
|
519
|
+
for attr_name in dir(cls):
|
|
520
|
+
if (
|
|
521
|
+
not attr_name.startswith("_") # Skip private/built-in methods
|
|
522
|
+
and attr_name != "mro" # Skip class method
|
|
523
|
+
and hasattr(cls, attr_name)
|
|
524
|
+
and callable(getattr(cls, attr_name))
|
|
525
|
+
and not isinstance(
|
|
526
|
+
getattr(cls, attr_name), (classmethod, staticmethod, property)
|
|
527
|
+
)
|
|
528
|
+
):
|
|
529
|
+
methods_to_wrap.append(attr_name)
|
|
530
|
+
|
|
531
|
+
# Wrap all detected methods
|
|
532
|
+
for method_to_wrap in methods_to_wrap:
|
|
533
|
+
if hasattr(cls, method_to_wrap):
|
|
534
|
+
method = getattr(cls, method_to_wrap)
|
|
535
|
+
wrapped_method = entity_method(
|
|
536
|
+
name=f"{task_name}.{method_to_wrap}",
|
|
537
|
+
description=description,
|
|
538
|
+
version=version,
|
|
539
|
+
tlp_span_kind=tlp_span_kind,
|
|
540
|
+
)(method)
|
|
541
|
+
setattr(cls, method_to_wrap, wrapped_method)
|
|
542
|
+
|
|
486
543
|
return cls
|
|
487
544
|
|
|
488
545
|
return decorator
|
|
489
546
|
|
|
490
547
|
|
|
491
|
-
def _handle_agent_span(span, entity_name, tlp_span_kind):
|
|
548
|
+
def _handle_agent_span(span, entity_name, description, tlp_span_kind):
|
|
492
549
|
if tlp_span_kind == ObserveSpanKindValues.AGENT:
|
|
493
550
|
try:
|
|
494
551
|
set_agent_id_event(entity_name)
|
|
@@ -496,6 +553,7 @@ def _handle_agent_span(span, entity_name, tlp_span_kind):
|
|
|
496
553
|
"agent_start_event",
|
|
497
554
|
{
|
|
498
555
|
"agent_name": entity_name,
|
|
556
|
+
"description": description if description else "",
|
|
499
557
|
"type": tlp_span_kind.value
|
|
500
558
|
if tlp_span_kind != "graph"
|
|
501
559
|
else "graph",
|
|
@@ -581,7 +639,8 @@ def _handle_graph_response(span, res, tlp_span_kind):
|
|
|
581
639
|
}
|
|
582
640
|
|
|
583
641
|
# Convert to JSON string
|
|
584
|
-
s_graph_json = json.dumps(graph_dict
|
|
642
|
+
s_graph_json = json.dumps(graph_dict)
|
|
643
|
+
# convert to JSON and set as attribute
|
|
585
644
|
span.set_attribute("gen_ai.ioa.graph", s_graph_json)
|
|
586
645
|
|
|
587
646
|
# get graph dynamism
|