Skip to content

Commit 9e036ab

Browse files
nagkumar91Copilot
andcommitted
Revert LangChain/OpenAI Agents changes, align Mem0 with slimmed spec
- Revert all changes to langchain and openai-agents-v2 packages (these should be separate PRs) - Update Mem0 instrumentation to match slimmed-down memory spec: only 5 core attributes (store.id, record.id, record.content, query.text, search.result.count) - Remove references to moved/removed attributes: memory.type, memory.scope, store.name, similarity.threshold, expiration_date Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8a0b022 commit 9e036ab

11 files changed

Lines changed: 163 additions & 870 deletions

File tree

instrumentation-genai/opentelemetry-instrumentation-langchain/CHANGELOG.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
([#3889](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3889))
1414
- Added log and metrics provider to langchain genai utils handler
1515
([#4214](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4214))
16-
- Added retriever memory search span/event instrumentation aligned with the
17-
GenAI memory semantic convention proposal.
18-
([#3250](https://github.com/open-telemetry/semantic-conventions/pull/3250))

instrumentation-genai/opentelemetry-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/callback_handler.py

Lines changed: 8 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from __future__ import annotations
1616

17-
from typing import Any, Optional, Sequence
17+
from typing import Any, Optional, cast
1818
from uuid import UUID
1919

2020
from langchain_core.callbacks import BaseCallbackHandler
@@ -24,60 +24,15 @@
2424
from opentelemetry.instrumentation.langchain.invocation_manager import (
2525
_InvocationManager,
2626
)
27-
from opentelemetry.semconv._incubating.attributes import (
28-
gen_ai_attributes as GenAI,
29-
)
3027
from opentelemetry.util.genai.handler import TelemetryHandler
3128
from opentelemetry.util.genai.types import (
32-
ContentCapturingMode,
3329
Error,
3430
InputMessage,
3531
LLMInvocation,
32+
MessagePart,
3633
OutputMessage,
3734
Text,
3835
)
39-
from opentelemetry.util.genai.utils import (
40-
get_content_capturing_mode,
41-
is_experimental_mode,
42-
)
43-
44-
GEN_AI_MEMORY_STORE_ID = getattr(
45-
GenAI, "GEN_AI_MEMORY_STORE_ID", "gen_ai.memory.store.id"
46-
)
47-
GEN_AI_MEMORY_STORE_NAME = getattr(
48-
GenAI, "GEN_AI_MEMORY_STORE_NAME", "gen_ai.memory.store.name"
49-
)
50-
GEN_AI_MEMORY_QUERY = getattr(
51-
GenAI, "GEN_AI_MEMORY_QUERY", "gen_ai.memory.query"
52-
)
53-
GEN_AI_MEMORY_SEARCH_RESULT_COUNT = getattr(
54-
GenAI,
55-
"GEN_AI_MEMORY_SEARCH_RESULT_COUNT",
56-
"gen_ai.memory.search.result.count",
57-
)
58-
GEN_AI_MEMORY_NAMESPACE = getattr(
59-
GenAI, "GEN_AI_MEMORY_NAMESPACE", "gen_ai.memory.namespace"
60-
)
61-
62-
_SEARCH_MEMORY_MEMBER = getattr(
63-
getattr(GenAI, "GenAiOperationNameValues", object()),
64-
"SEARCH_MEMORY",
65-
None,
66-
)
67-
SEARCH_MEMORY_OPERATION = (
68-
_SEARCH_MEMORY_MEMBER.value
69-
if _SEARCH_MEMORY_MEMBER is not None
70-
else "search_memory"
71-
)
72-
73-
_RETRIEVAL_MEMBER = getattr(
74-
getattr(GenAI, "GenAiOperationNameValues", object()),
75-
"RETRIEVAL",
76-
None,
77-
)
78-
RETRIEVAL_OPERATION = (
79-
_RETRIEVAL_MEMBER.value if _RETRIEVAL_MEMBER is not None else "retrieval"
80-
)
8136

8237

8338
class OpenTelemetryLangChainCallbackHandler(BaseCallbackHandler):
@@ -90,62 +45,6 @@ def __init__(self, telemetry_handler: TelemetryHandler) -> None:
9045
self._telemetry_handler = telemetry_handler
9146
self._invocation_manager = _InvocationManager()
9247

93-
@staticmethod
94-
def _resolve_retriever_store_name(
95-
serialized: dict[str, Any],
96-
metadata: Optional[dict[str, Any]],
97-
) -> Optional[str]:
98-
if metadata and metadata.get("memory_store_name"):
99-
return str(metadata["memory_store_name"])
100-
if metadata and metadata.get("ls_retriever_name"):
101-
return str(metadata["ls_retriever_name"])
102-
name = serialized.get("name")
103-
return str(name) if isinstance(name, str) and name else None
104-
105-
@staticmethod
106-
def _resolve_retriever_store_id(
107-
serialized: dict[str, Any],
108-
metadata: Optional[dict[str, Any]],
109-
) -> Optional[str]:
110-
if metadata and metadata.get("memory_store_id"):
111-
return str(metadata["memory_store_id"])
112-
113-
serialized_id = serialized.get("id")
114-
if isinstance(serialized_id, str) and serialized_id:
115-
return serialized_id
116-
if isinstance(serialized_id, list) and serialized_id:
117-
try:
118-
return ".".join(str(part) for part in serialized_id)
119-
except TypeError:
120-
return None
121-
return None
122-
123-
@staticmethod
124-
def _should_capture_memory_query() -> bool:
125-
if not is_experimental_mode():
126-
return False
127-
try:
128-
mode = get_content_capturing_mode()
129-
except ValueError:
130-
return False
131-
return mode in (
132-
ContentCapturingMode.SPAN_ONLY,
133-
ContentCapturingMode.SPAN_AND_EVENT,
134-
)
135-
136-
@staticmethod
137-
def _is_memory_retriever(
138-
metadata: Optional[dict[str, Any]],
139-
) -> bool:
140-
"""Detect if a retriever is a memory retriever based on metadata hints."""
141-
if not metadata:
142-
return False
143-
return bool(
144-
metadata.get("memory_store_name")
145-
or metadata.get("memory_store_id")
146-
or metadata.get("is_memory_retriever")
147-
)
148-
14948
def on_chat_model_start(
15049
self,
15150
serialized: dict[str, Any],
@@ -235,7 +134,11 @@ def on_chat_model_start(
235134
Text(content=text_value, type="text")
236135
)
237136

238-
input_messages.append(InputMessage(parts=parts, role=role))
137+
input_messages.append(
138+
InputMessage(
139+
parts=cast(list[MessagePart], parts), role=role
140+
)
141+
)
239142

240143
llm_invocation = LLMInvocation(
241144
request_model=request_model,
@@ -308,7 +211,7 @@ def on_llm_end(
308211
role = chat_generation.message.type
309212
output_message = OutputMessage(
310213
role=role,
311-
parts=parts,
214+
parts=cast(list[MessagePart], parts),
312215
finish_reason=finish_reason,
313216
)
314217
output_messages.append(output_message)
@@ -370,100 +273,3 @@ def on_llm_error(
370273
)
371274
if llm_invocation.span and not llm_invocation.span.is_recording():
372275
self._invocation_manager.delete_invocation_state(run_id=run_id)
373-
374-
def on_retriever_start(
375-
self,
376-
serialized: dict[str, Any],
377-
query: str,
378-
*,
379-
run_id: UUID,
380-
parent_run_id: Optional[UUID] = None,
381-
tags: Optional[list[str]] = None,
382-
metadata: Optional[dict[str, Any]] = None,
383-
**kwargs: Any,
384-
) -> None:
385-
provider = "unknown"
386-
if metadata is not None:
387-
provider = metadata.get("ls_provider", "unknown")
388-
389-
attributes: dict[str, Any] = {}
390-
is_memory = self._is_memory_retriever(metadata)
391-
operation = (
392-
SEARCH_MEMORY_OPERATION if is_memory else RETRIEVAL_OPERATION
393-
)
394-
395-
if store_name := self._resolve_retriever_store_name(
396-
serialized, metadata
397-
):
398-
attributes[GEN_AI_MEMORY_STORE_NAME] = store_name
399-
if store_id := self._resolve_retriever_store_id(serialized, metadata):
400-
attributes[GEN_AI_MEMORY_STORE_ID] = store_id
401-
if (
402-
query
403-
and self._should_capture_memory_query()
404-
and isinstance(query, str)
405-
):
406-
attributes[GEN_AI_MEMORY_QUERY] = query
407-
if metadata and metadata.get("memory_namespace"):
408-
attributes[GEN_AI_MEMORY_NAMESPACE] = metadata["memory_namespace"]
409-
410-
llm_invocation = LLMInvocation(
411-
request_model="",
412-
provider=provider,
413-
operation_name=operation,
414-
attributes=attributes,
415-
)
416-
llm_invocation = self._telemetry_handler.start_llm(
417-
invocation=llm_invocation
418-
)
419-
if llm_invocation.span and store_name:
420-
llm_invocation.span.update_name(f"{operation} {store_name}")
421-
self._invocation_manager.add_invocation_state(
422-
run_id=run_id,
423-
parent_run_id=parent_run_id,
424-
invocation=llm_invocation,
425-
)
426-
427-
def on_retriever_end(
428-
self,
429-
documents: Sequence[Any],
430-
*,
431-
run_id: UUID,
432-
parent_run_id: Optional[UUID] = None,
433-
**kwargs: Any,
434-
) -> None:
435-
llm_invocation = self._invocation_manager.get_invocation(run_id=run_id)
436-
if llm_invocation is None or not isinstance(
437-
llm_invocation, LLMInvocation
438-
):
439-
return
440-
441-
llm_invocation.attributes[GEN_AI_MEMORY_SEARCH_RESULT_COUNT] = len(
442-
documents
443-
)
444-
llm_invocation = self._telemetry_handler.stop_llm(
445-
invocation=llm_invocation
446-
)
447-
if llm_invocation.span and not llm_invocation.span.is_recording():
448-
self._invocation_manager.delete_invocation_state(run_id=run_id)
449-
450-
def on_retriever_error(
451-
self,
452-
error: BaseException,
453-
*,
454-
run_id: UUID,
455-
parent_run_id: Optional[UUID] = None,
456-
**kwargs: Any,
457-
) -> None:
458-
llm_invocation = self._invocation_manager.get_invocation(run_id=run_id)
459-
if llm_invocation is None or not isinstance(
460-
llm_invocation, LLMInvocation
461-
):
462-
return
463-
464-
error_otel = Error(message=str(error), type=type(error))
465-
llm_invocation = self._telemetry_handler.fail_llm(
466-
invocation=llm_invocation, error=error_otel
467-
)
468-
if llm_invocation.span and not llm_invocation.span.is_recording():
469-
self._invocation_manager.delete_invocation_state(run_id=run_id)

instrumentation-genai/opentelemetry-instrumentation-mem0/src/opentelemetry/instrumentation/mem0/patch.py

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,18 @@ def _attr(name: str, fallback: str) -> str:
5454
GEN_AI_MEMORY_STORE_ID = _attr(
5555
"GEN_AI_MEMORY_STORE_ID", "gen_ai.memory.store.id"
5656
)
57-
GEN_AI_MEMORY_STORE_NAME = _attr(
58-
"GEN_AI_MEMORY_STORE_NAME", "gen_ai.memory.store.name"
57+
GEN_AI_MEMORY_RECORD_ID = _attr(
58+
"GEN_AI_MEMORY_RECORD_ID", "gen_ai.memory.record.id"
5959
)
60-
GEN_AI_MEMORY_ID = _attr("GEN_AI_MEMORY_ID", "gen_ai.memory.id")
61-
GEN_AI_MEMORY_TYPE = _attr("GEN_AI_MEMORY_TYPE", "gen_ai.memory.type")
62-
GEN_AI_MEMORY_SCOPE = _attr("GEN_AI_MEMORY_SCOPE", "gen_ai.memory.scope")
63-
GEN_AI_MEMORY_QUERY = _attr("GEN_AI_MEMORY_QUERY", "gen_ai.memory.query")
64-
GEN_AI_MEMORY_CONTENT = _attr("GEN_AI_MEMORY_CONTENT", "gen_ai.memory.content")
65-
GEN_AI_MEMORY_NAMESPACE = _attr(
66-
"GEN_AI_MEMORY_NAMESPACE", "gen_ai.memory.namespace"
60+
GEN_AI_MEMORY_RECORD_CONTENT = _attr(
61+
"GEN_AI_MEMORY_RECORD_CONTENT", "gen_ai.memory.record.content"
62+
)
63+
GEN_AI_MEMORY_QUERY_TEXT = _attr(
64+
"GEN_AI_MEMORY_QUERY_TEXT", "gen_ai.memory.query.text"
6765
)
6866
GEN_AI_MEMORY_SEARCH_RESULT_COUNT = _attr(
6967
"GEN_AI_MEMORY_SEARCH_RESULT_COUNT", "gen_ai.memory.search.result.count"
7068
)
71-
GEN_AI_MEMORY_UPDATE_STRATEGY = _attr(
72-
"GEN_AI_MEMORY_UPDATE_STRATEGY", "gen_ai.memory.update.strategy"
73-
)
7469
ERROR_TYPE = getattr(ErrorAttributes, "ERROR_TYPE", "error.type")
7570

7671
_PROVIDER = "mem0"
@@ -87,28 +82,6 @@ def _capture_content() -> bool:
8782
).lower() in ("true", "1")
8883

8984

90-
def _scope_from_kwargs(kwargs: dict[str, Any]) -> Optional[str]:
91-
"""Infer memory scope from Mem0 filter kwargs."""
92-
if kwargs.get("user_id"):
93-
return "user"
94-
if kwargs.get("agent_id"):
95-
return "agent"
96-
if kwargs.get("run_id"):
97-
return "session"
98-
return None
99-
100-
101-
def _namespace_from_kwargs(kwargs: dict[str, Any]) -> Optional[str]:
102-
"""Build namespace string from Mem0 identity kwargs."""
103-
user_id = kwargs.get("user_id")
104-
agent_id = kwargs.get("agent_id")
105-
if user_id:
106-
return f"user:{user_id}"
107-
if agent_id:
108-
return f"agent:{agent_id}"
109-
return None
110-
111-
11285
def _set_common_attributes(
11386
span: trace.Span,
11487
operation: str,
@@ -119,14 +92,6 @@ def _set_common_attributes(
11992
span.set_attribute(GEN_AI_SYSTEM, _PROVIDER)
12093
span.set_attribute(GEN_AI_PROVIDER_NAME, _PROVIDER)
12194

122-
scope = _scope_from_kwargs(kwargs)
123-
if scope:
124-
span.set_attribute(GEN_AI_MEMORY_SCOPE, scope)
125-
126-
namespace = _namespace_from_kwargs(kwargs)
127-
if namespace:
128-
span.set_attribute(GEN_AI_MEMORY_NAMESPACE, namespace)
129-
13095

13196
def _set_error(span: trace.Span, exc: BaseException) -> str:
13297
"""Record error details on the span and return the error type string."""
@@ -198,13 +163,11 @@ def wrapper(
198163
span_name, kind=SpanKind.CLIENT
199164
) as span:
200165
_set_common_attributes(span, "update_memory", kwargs)
201-
# Mem0 add() is an upsert
202-
span.set_attribute(GEN_AI_MEMORY_UPDATE_STRATEGY, "merge")
203166

204167
if _capture_content() and args:
205168
messages = args[0] if args else kwargs.get("messages")
206169
if messages and isinstance(messages, str):
207-
span.set_attribute(GEN_AI_MEMORY_CONTENT, messages)
170+
span.set_attribute(GEN_AI_MEMORY_RECORD_CONTENT, messages)
208171

209172
try:
210173
result = wrapped(*args, **kwargs)
@@ -221,7 +184,7 @@ def wrapper(
221184

222185
mem_id = _first_memory_id(result)
223186
if mem_id:
224-
span.set_attribute(GEN_AI_MEMORY_ID, mem_id)
187+
span.set_attribute(GEN_AI_MEMORY_RECORD_ID, mem_id)
225188

226189
return result
227190

@@ -251,7 +214,7 @@ def wrapper(
251214

252215
query = args[0] if args else kwargs.get("query")
253216
if _capture_content() and query and isinstance(query, str):
254-
span.set_attribute(GEN_AI_MEMORY_QUERY, query)
217+
span.set_attribute(GEN_AI_MEMORY_QUERY_TEXT, query)
255218

256219
try:
257220
result = wrapped(*args, **kwargs)
@@ -296,14 +259,13 @@ def wrapper(
296259
span_name, kind=SpanKind.CLIENT
297260
) as span:
298261
_set_common_attributes(span, "update_memory", kwargs)
299-
span.set_attribute(GEN_AI_MEMORY_UPDATE_STRATEGY, "overwrite")
300262
if memory_id:
301-
span.set_attribute(GEN_AI_MEMORY_ID, str(memory_id))
263+
span.set_attribute(GEN_AI_MEMORY_RECORD_ID, str(memory_id))
302264

303265
if _capture_content():
304266
data = kwargs.get("data")
305267
if data and isinstance(data, str):
306-
span.set_attribute(GEN_AI_MEMORY_CONTENT, data)
268+
span.set_attribute(GEN_AI_MEMORY_RECORD_CONTENT, data)
307269

308270
try:
309271
result = wrapped(*args, **kwargs)
@@ -345,7 +307,7 @@ def wrapper(
345307
) as span:
346308
_set_common_attributes(span, "delete_memory", kwargs)
347309
if memory_id:
348-
span.set_attribute(GEN_AI_MEMORY_ID, str(memory_id))
310+
span.set_attribute(GEN_AI_MEMORY_RECORD_ID, str(memory_id))
349311

350312
try:
351313
result = wrapped(*args, **kwargs)

0 commit comments

Comments
 (0)