opentelemetry-instrumentation-crewai 0.37.1__py3-none-any.whl → 0.38.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.
@@ -1,10 +1,15 @@
1
+ import os
2
+ import time
1
3
  from typing import Collection
4
+
2
5
  from wrapt import wrap_function_wrapper
3
- from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
4
- from opentelemetry.trace import SpanKind, get_tracer
6
+ from opentelemetry.trace import SpanKind, get_tracer, Tracer
5
7
  from opentelemetry.trace.status import Status, StatusCode
8
+ from opentelemetry.metrics import Histogram, Meter, get_meter
9
+ from opentelemetry.instrumentation.utils import unwrap
10
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
6
11
  from opentelemetry.instrumentation.crewai.version import __version__
7
- from opentelemetry.semconv_ai import SpanAttributes, TraceloopSpanKindValues
12
+ from opentelemetry.semconv_ai import SpanAttributes, TraceloopSpanKindValues, Meters
8
13
  from .crewai_span_attributes import CrewAISpanAttributes, set_span_attribute
9
14
 
10
15
  _instruments = ("crewai >= 0.70.0",)
@@ -19,31 +24,54 @@ class CrewAIInstrumentor(BaseInstrumentor):
19
24
  tracer_provider = kwargs.get("tracer_provider")
20
25
  tracer = get_tracer(__name__, __version__, tracer_provider)
21
26
 
22
- wrap_function_wrapper("crewai.crew", "Crew.kickoff", wrap_kickoff(tracer))
23
- wrap_function_wrapper("crewai.agent", "Agent.execute_task", wrap_agent_execute_task(tracer))
24
- wrap_function_wrapper("crewai.task", "Task.execute_sync", wrap_task_execute(tracer))
25
- wrap_function_wrapper("crewai.llm", "LLM.call", wrap_llm_call(tracer))
27
+ meter_provider = kwargs.get("meter_provider")
28
+ meter = get_meter(__name__, __version__, meter_provider)
29
+
30
+ if is_metrics_enabled():
31
+ (
32
+ token_histogram,
33
+ duration_histogram,
34
+ ) = _create_metrics(meter)
35
+ else:
36
+ (
37
+ token_histogram,
38
+ duration_histogram,
39
+ ) = (None, None, None, None)
40
+
41
+ wrap_function_wrapper("crewai.crew", "Crew.kickoff",
42
+ wrap_kickoff(tracer, duration_histogram, token_histogram))
43
+ wrap_function_wrapper("crewai.agent", "Agent.execute_task",
44
+ wrap_agent_execute_task(tracer, duration_histogram, token_histogram))
45
+ wrap_function_wrapper("crewai.task", "Task.execute_sync",
46
+ wrap_task_execute(tracer, duration_histogram, token_histogram))
47
+ wrap_function_wrapper("crewai.llm", "LLM.call",
48
+ wrap_llm_call(tracer, duration_histogram, token_histogram))
26
49
 
27
50
  def _uninstrument(self, **kwargs):
28
- pass
51
+ unwrap("crewai.crew.Crew", "kickoff")
52
+ unwrap("crewai.agent.Agent", "execute_task")
53
+ unwrap("crewai.task.Task", "execute_sync")
54
+ unwrap("crewai.llm.LLM", "call")
29
55
 
30
56
 
31
57
  def with_tracer_wrapper(func):
32
58
  """Helper for providing tracer for wrapper functions."""
33
59
 
34
- def _with_tracer(tracer):
60
+ def _with_tracer(tracer, duration_histogram, token_histogram):
35
61
  def wrapper(wrapped, instance, args, kwargs):
36
- return func(tracer, wrapped, instance, args, kwargs)
62
+ return func(tracer, duration_histogram, token_histogram, wrapped, instance, args, kwargs)
37
63
  return wrapper
38
64
  return _with_tracer
39
65
 
40
66
 
41
67
  @with_tracer_wrapper
42
- def wrap_kickoff(tracer, wrapped, instance, args, kwargs):
68
+ def wrap_kickoff(tracer: Tracer, duration_histogram: Histogram, token_histogram: Histogram,
69
+ wrapped, instance, args, kwargs):
43
70
  with tracer.start_as_current_span(
44
71
  "crewai.workflow",
45
72
  kind=SpanKind.INTERNAL,
46
73
  attributes={
74
+ SpanAttributes.LLM_SYSTEM: "crewai",
47
75
  }
48
76
  ) as span:
49
77
  try:
@@ -64,7 +92,7 @@ def wrap_kickoff(tracer, wrapped, instance, args, kwargs):
64
92
 
65
93
 
66
94
  @with_tracer_wrapper
67
- def wrap_agent_execute_task(tracer, wrapped, instance, args, kwargs):
95
+ def wrap_agent_execute_task(tracer, duration_histogram, token_histogram, wrapped, instance, args, kwargs):
68
96
  agent_name = instance.role if hasattr(instance, "role") else "agent"
69
97
  with tracer.start_as_current_span(
70
98
  f"{agent_name}.agent",
@@ -76,7 +104,26 @@ def wrap_agent_execute_task(tracer, wrapped, instance, args, kwargs):
76
104
  try:
77
105
  CrewAISpanAttributes(span=span, instance=instance)
78
106
  result = wrapped(*args, **kwargs)
107
+ if token_histogram:
108
+ token_histogram.record(
109
+ instance._token_process.get_summary().prompt_tokens,
110
+ attributes={
111
+ SpanAttributes.LLM_SYSTEM: "crewai",
112
+ SpanAttributes.LLM_TOKEN_TYPE: "input",
113
+ SpanAttributes.LLM_RESPONSE_MODEL: str(instance.llm.model),
114
+ }
115
+ )
116
+ token_histogram.record(
117
+ instance._token_process.get_summary().completion_tokens,
118
+ attributes={
119
+ SpanAttributes.LLM_SYSTEM: "crewai",
120
+ SpanAttributes.LLM_TOKEN_TYPE: "output",
121
+ SpanAttributes.LLM_RESPONSE_MODEL: str(instance.llm.model),
122
+ },
123
+ )
124
+
79
125
  set_span_attribute(span, SpanAttributes.LLM_REQUEST_MODEL, str(instance.llm.model))
126
+ set_span_attribute(span, SpanAttributes.LLM_RESPONSE_MODEL, str(instance.llm.model))
80
127
  span.set_status(Status(StatusCode.OK))
81
128
  return result
82
129
  except Exception as ex:
@@ -85,7 +132,7 @@ def wrap_agent_execute_task(tracer, wrapped, instance, args, kwargs):
85
132
 
86
133
 
87
134
  @with_tracer_wrapper
88
- def wrap_task_execute(tracer, wrapped, instance, args, kwargs):
135
+ def wrap_task_execute(tracer, duration_histogram, token_histogram, wrapped, instance, args, kwargs):
89
136
  task_name = instance.description if hasattr(instance, "description") else "task"
90
137
 
91
138
  with tracer.start_as_current_span(
@@ -107,7 +154,7 @@ def wrap_task_execute(tracer, wrapped, instance, args, kwargs):
107
154
 
108
155
 
109
156
  @with_tracer_wrapper
110
- def wrap_llm_call(tracer, wrapped, instance, args, kwargs):
157
+ def wrap_llm_call(tracer, duration_histogram, token_histogram, wrapped, instance, args, kwargs):
111
158
  llm = instance.model if hasattr(instance, "model") else "llm"
112
159
  with tracer.start_as_current_span(
113
160
  f"{llm}.llm",
@@ -115,13 +162,43 @@ def wrap_llm_call(tracer, wrapped, instance, args, kwargs):
115
162
  attributes={
116
163
  }
117
164
  ) as span:
165
+ start_time = time.time()
118
166
  try:
119
167
  CrewAISpanAttributes(span=span, instance=instance)
120
168
  result = wrapped(*args, **kwargs)
121
- set_span_attribute(span, SpanAttributes.LLM_SYSTEM, "crewai")
122
- set_span_attribute(span, SpanAttributes.LLM_REQUEST_MODEL, str(instance.model))
169
+
170
+ if duration_histogram:
171
+ duration = time.time() - start_time
172
+ duration_histogram.record(
173
+ duration,
174
+ attributes={
175
+ SpanAttributes.LLM_SYSTEM: "crewai",
176
+ SpanAttributes.LLM_RESPONSE_MODEL: str(instance.model)
177
+ },
178
+ )
179
+
123
180
  span.set_status(Status(StatusCode.OK))
124
181
  return result
125
182
  except Exception as ex:
126
183
  span.set_status(Status(StatusCode.ERROR, str(ex)))
127
184
  raise
185
+
186
+
187
+ def is_metrics_enabled() -> bool:
188
+ return (os.getenv("TRACELOOP_METRICS_ENABLED") or "true").lower() == "true"
189
+
190
+
191
+ def _create_metrics(meter: Meter):
192
+ token_histogram = meter.create_histogram(
193
+ name=Meters.LLM_TOKEN_USAGE,
194
+ unit="token",
195
+ description="Measures number of input and output tokens used",
196
+ )
197
+
198
+ duration_histogram = meter.create_histogram(
199
+ name=Meters.LLM_OPERATION_DURATION,
200
+ unit="s",
201
+ description="GenAI operation duration",
202
+ )
203
+
204
+ return token_histogram, duration_histogram
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: opentelemetry-instrumentation-crewai
3
- Version: 0.37.1
3
+ Version: 0.38.0
4
4
  Summary: OpenTelemetry crewAI instrumentation
5
5
  License: Apache-2.0
6
6
  Author: Gal Kleinman
@@ -1,8 +1,8 @@
1
1
  opentelemetry/instrumentation/crewai/__init__.py,sha256=zdNyWV62d5IaueXR9clUD_c9F32MF39AIchZf66XI2c,245
2
2
  opentelemetry/instrumentation/crewai/crewai_span_attributes.py,sha256=9ax6fn0IT4dqH2iaRG8VIZ0SWTkxQrMTiF7DWUdwfy0,4945
3
- opentelemetry/instrumentation/crewai/instrumentation.py,sha256=_Y8dmgwYoSRkDAYSoR9zNkgQYAk1aqSTkDoQIsjV5DI,4867
3
+ opentelemetry/instrumentation/crewai/instrumentation.py,sha256=AhQpPgpgtx9DKdnRDcrCKXn4AIuErdfznxfLlfs9OTc,7913
4
4
  opentelemetry/instrumentation/crewai/version.py,sha256=ITH8IVCEQOekRajZpPo6sKNlMBNDRQRnzipIN0dqXoc,23
5
- opentelemetry_instrumentation_crewai-0.37.1.dist-info/METADATA,sha256=PcZhRSwQOheSdDPd2h8Ef2XkC9goD-cuwleLkDul4lc,2098
6
- opentelemetry_instrumentation_crewai-0.37.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
7
- opentelemetry_instrumentation_crewai-0.37.1.dist-info/entry_points.txt,sha256=4Zs98MjB2HIaX8fk8evs41x1J1xBNfztvPN428bf_b8,93
8
- opentelemetry_instrumentation_crewai-0.37.1.dist-info/RECORD,,
5
+ opentelemetry_instrumentation_crewai-0.38.0.dist-info/METADATA,sha256=KPAaEX4E4viDPTDRDFqdoywMGSysXXjQq2JqND3Q0Ds,2098
6
+ opentelemetry_instrumentation_crewai-0.38.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
7
+ opentelemetry_instrumentation_crewai-0.38.0.dist-info/entry_points.txt,sha256=4Zs98MjB2HIaX8fk8evs41x1J1xBNfztvPN428bf_b8,93
8
+ opentelemetry_instrumentation_crewai-0.38.0.dist-info/RECORD,,