genkit-plugin-google-cloud 0.4.0__py3-none-any.whl → 0.5.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.
- genkit/plugins/google_cloud/__init__.py +127 -2
- genkit/plugins/google_cloud/telemetry/__init__.py +74 -0
- genkit/plugins/google_cloud/telemetry/action.py +124 -0
- genkit/plugins/google_cloud/telemetry/engagement.py +170 -0
- genkit/plugins/google_cloud/telemetry/feature.py +186 -0
- genkit/plugins/google_cloud/telemetry/generate.py +605 -0
- genkit/plugins/google_cloud/telemetry/metrics.py +246 -0
- genkit/plugins/google_cloud/telemetry/path.py +157 -0
- genkit/plugins/google_cloud/telemetry/tracing.py +880 -29
- genkit/plugins/google_cloud/telemetry/utils.py +217 -0
- {genkit_plugin_google_cloud-0.4.0.dist-info → genkit_plugin_google_cloud-0.5.0.dist-info}/METADATA +10 -2
- genkit_plugin_google_cloud-0.5.0.dist-info/RECORD +15 -0
- {genkit_plugin_google_cloud-0.4.0.dist-info → genkit_plugin_google_cloud-0.5.0.dist-info}/WHEEL +1 -1
- genkit_plugin_google_cloud-0.4.0.dist-info/RECORD +0 -9
- /genkit/{py.typed → plugins/google_cloud/py.typed} +0 -0
- {genkit_plugin_google_cloud-0.4.0.dist-info → genkit_plugin_google_cloud-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
|
|
17
|
+
"""Feature telemetry for GCP.
|
|
18
|
+
|
|
19
|
+
This module tracks feature-level metrics (requests, latencies) and logs
|
|
20
|
+
input/output for root spans, matching the JavaScript implementation.
|
|
21
|
+
|
|
22
|
+
Metrics Recorded:
|
|
23
|
+
- genkit/feature/requests: Counter for root span calls
|
|
24
|
+
- genkit/feature/latency: Histogram for root span latency (ms)
|
|
25
|
+
|
|
26
|
+
Cross-Language Parity:
|
|
27
|
+
- JavaScript: js/plugins/google-cloud/src/telemetry/feature.ts
|
|
28
|
+
- Go: go/plugins/googlecloud/feature.go
|
|
29
|
+
|
|
30
|
+
See Also:
|
|
31
|
+
- Cloud Monitoring Custom Metrics: https://cloud.google.com/monitoring/custom-metrics
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import structlog
|
|
37
|
+
from opentelemetry import metrics
|
|
38
|
+
from opentelemetry.sdk.trace import ReadableSpan
|
|
39
|
+
|
|
40
|
+
from genkit.core import GENKIT_VERSION
|
|
41
|
+
|
|
42
|
+
from .utils import (
|
|
43
|
+
create_common_log_attributes,
|
|
44
|
+
extract_error_name,
|
|
45
|
+
to_display_path,
|
|
46
|
+
truncate,
|
|
47
|
+
truncate_path,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
logger = structlog.get_logger(__name__)
|
|
51
|
+
|
|
52
|
+
# Lazy-initialized metrics
|
|
53
|
+
_feature_counter: metrics.Counter | None = None
|
|
54
|
+
_feature_latency: metrics.Histogram | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_feature_counter() -> metrics.Counter:
|
|
58
|
+
"""Get or create the feature requests counter."""
|
|
59
|
+
global _feature_counter
|
|
60
|
+
if _feature_counter is None:
|
|
61
|
+
meter = metrics.get_meter('genkit')
|
|
62
|
+
_feature_counter = meter.create_counter(
|
|
63
|
+
'genkit/feature/requests',
|
|
64
|
+
description='Counts calls to genkit features.',
|
|
65
|
+
unit='1',
|
|
66
|
+
)
|
|
67
|
+
return _feature_counter
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _get_feature_latency() -> metrics.Histogram:
|
|
71
|
+
"""Get or create the feature latency histogram."""
|
|
72
|
+
global _feature_latency
|
|
73
|
+
if _feature_latency is None:
|
|
74
|
+
meter = metrics.get_meter('genkit')
|
|
75
|
+
_feature_latency = meter.create_histogram(
|
|
76
|
+
'genkit/feature/latency',
|
|
77
|
+
description='Latencies when calling Genkit features.',
|
|
78
|
+
unit='ms',
|
|
79
|
+
)
|
|
80
|
+
return _feature_latency
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class FeaturesTelemetry:
|
|
84
|
+
"""Telemetry handler for Genkit features (root spans)."""
|
|
85
|
+
|
|
86
|
+
def tick(
|
|
87
|
+
self,
|
|
88
|
+
span: ReadableSpan,
|
|
89
|
+
log_input_and_output: bool,
|
|
90
|
+
project_id: str | None = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Record telemetry for a feature span.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
span: The span to record telemetry for.
|
|
96
|
+
log_input_and_output: Whether to log input/output.
|
|
97
|
+
project_id: Optional GCP project ID.
|
|
98
|
+
"""
|
|
99
|
+
attrs = span.attributes or {}
|
|
100
|
+
name = str(attrs.get('genkit:name', '<unknown>'))
|
|
101
|
+
path = str(attrs.get('genkit:path', ''))
|
|
102
|
+
state = str(attrs.get('genkit:state', ''))
|
|
103
|
+
|
|
104
|
+
# Calculate latency
|
|
105
|
+
latency_ms = 0.0
|
|
106
|
+
if span.end_time and span.start_time:
|
|
107
|
+
latency_ms = (span.end_time - span.start_time) / 1_000_000
|
|
108
|
+
|
|
109
|
+
if state == 'success':
|
|
110
|
+
self._write_feature_success(name, latency_ms)
|
|
111
|
+
elif state == 'error':
|
|
112
|
+
error_name = extract_error_name(list(span.events)) or '<unknown>'
|
|
113
|
+
self._write_feature_failure(name, latency_ms, error_name)
|
|
114
|
+
else:
|
|
115
|
+
logger.warning('Unknown state', state=state)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
if log_input_and_output:
|
|
119
|
+
input_val = truncate(str(attrs.get('genkit:input', '')))
|
|
120
|
+
output_val = truncate(str(attrs.get('genkit:output', '')))
|
|
121
|
+
session_id = str(attrs.get('genkit:sessionId', '')) or None
|
|
122
|
+
thread_name = str(attrs.get('genkit:threadName', '')) or None
|
|
123
|
+
|
|
124
|
+
if input_val:
|
|
125
|
+
self._write_log(span, 'Input', name, path, input_val, project_id, session_id, thread_name)
|
|
126
|
+
if output_val:
|
|
127
|
+
self._write_log(span, 'Output', name, path, output_val, project_id, session_id, thread_name)
|
|
128
|
+
|
|
129
|
+
def _write_feature_success(self, feature_name: str, latency_ms: float) -> None:
|
|
130
|
+
"""Record success metrics for a feature."""
|
|
131
|
+
dimensions = {
|
|
132
|
+
'name': feature_name[:256],
|
|
133
|
+
'status': 'success',
|
|
134
|
+
'source': 'py',
|
|
135
|
+
'sourceVersion': GENKIT_VERSION,
|
|
136
|
+
}
|
|
137
|
+
_get_feature_counter().add(1, dimensions)
|
|
138
|
+
_get_feature_latency().record(latency_ms, dimensions)
|
|
139
|
+
|
|
140
|
+
def _write_feature_failure(
|
|
141
|
+
self,
|
|
142
|
+
feature_name: str,
|
|
143
|
+
latency_ms: float,
|
|
144
|
+
error_name: str,
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Record failure metrics for a feature."""
|
|
147
|
+
dimensions = {
|
|
148
|
+
'name': feature_name[:256],
|
|
149
|
+
'status': 'failure',
|
|
150
|
+
'source': 'py',
|
|
151
|
+
'sourceVersion': GENKIT_VERSION,
|
|
152
|
+
'error': error_name[:256],
|
|
153
|
+
}
|
|
154
|
+
_get_feature_counter().add(1, dimensions)
|
|
155
|
+
_get_feature_latency().record(latency_ms, dimensions)
|
|
156
|
+
|
|
157
|
+
def _write_log(
|
|
158
|
+
self,
|
|
159
|
+
span: ReadableSpan,
|
|
160
|
+
tag: str,
|
|
161
|
+
feature_name: str,
|
|
162
|
+
qualified_path: str,
|
|
163
|
+
content: str,
|
|
164
|
+
project_id: str | None,
|
|
165
|
+
session_id: str | None,
|
|
166
|
+
thread_name: str | None,
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Write a structured log entry."""
|
|
169
|
+
path = truncate_path(to_display_path(qualified_path))
|
|
170
|
+
metadata = {
|
|
171
|
+
**create_common_log_attributes(span, project_id),
|
|
172
|
+
'path': path,
|
|
173
|
+
'qualifiedPath': qualified_path,
|
|
174
|
+
'featureName': feature_name,
|
|
175
|
+
'content': content,
|
|
176
|
+
}
|
|
177
|
+
if session_id:
|
|
178
|
+
metadata['sessionId'] = session_id
|
|
179
|
+
if thread_name:
|
|
180
|
+
metadata['threadName'] = thread_name
|
|
181
|
+
|
|
182
|
+
logger.info(f'{tag}[{path}, {feature_name}]', **metadata)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# Singleton instance
|
|
186
|
+
features_telemetry = FeaturesTelemetry()
|