coco-framework 0.1.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.
- coco/__init__.py +3 -0
- coco/agents/__init__.py +9 -0
- coco/agents/_subagent_transformer.py +307 -0
- coco/agents/factory.py +2164 -0
- coco/agents/middleware/__init__.py +100 -0
- coco/agents/middleware/_execution.py +385 -0
- coco/agents/middleware/_redaction.py +454 -0
- coco/agents/middleware/_retry.py +134 -0
- coco/agents/middleware/_trace_policy.py +61 -0
- coco/agents/middleware/context_editing.py +306 -0
- coco/agents/middleware/file_search.py +433 -0
- coco/agents/middleware/human_in_the_loop.py +680 -0
- coco/agents/middleware/internal_call_transformer.py +108 -0
- coco/agents/middleware/model_call_limit.py +267 -0
- coco/agents/middleware/model_fallback.py +417 -0
- coco/agents/middleware/model_retry.py +327 -0
- coco/agents/middleware/pii.py +878 -0
- coco/agents/middleware/provider_tool_search.py +307 -0
- coco/agents/middleware/shell_tool.py +949 -0
- coco/agents/middleware/summarization.py +910 -0
- coco/agents/middleware/todo.py +357 -0
- coco/agents/middleware/tool_call_limit.py +495 -0
- coco/agents/middleware/tool_emulator.py +251 -0
- coco/agents/middleware/tool_error.py +209 -0
- coco/agents/middleware/tool_retry.py +421 -0
- coco/agents/middleware/tool_selection.py +515 -0
- coco/agents/middleware/types.py +2201 -0
- coco/agents/structured_output.py +472 -0
- coco/chat_models/__init__.py +7 -0
- coco/chat_models/base.py +1082 -0
- coco/embeddings/__init__.py +18 -0
- coco/embeddings/base.py +279 -0
- coco/mcp/__init__.py +36 -0
- coco/mcp/adapter.py +245 -0
- coco/mcp/elicitation.py +394 -0
- coco/mcp/tools.py +307 -0
- coco/messages/__init__.py +73 -0
- coco/py.typed +0 -0
- coco/rate_limiters/__init__.py +13 -0
- coco/tools/__init__.py +22 -0
- coco/tools/tool_node.py +20 -0
- coco_anthropic/__init__.py +15 -0
- coco_anthropic/_client_utils.py +81 -0
- coco_anthropic/_compat.py +228 -0
- coco_anthropic/_sdk_compat.py +124 -0
- coco_anthropic/_version.py +3 -0
- coco_anthropic/chat_models.py +3349 -0
- coco_anthropic/data/__init__.py +1 -0
- coco_anthropic/data/_profiles.py +491 -0
- coco_anthropic/data/profile_augmentations.toml +69 -0
- coco_anthropic/experimental.py +140 -0
- coco_anthropic/llms.py +428 -0
- coco_anthropic/middleware/__init__.py +25 -0
- coco_anthropic/middleware/anthropic_tools.py +1197 -0
- coco_anthropic/middleware/bash.py +84 -0
- coco_anthropic/middleware/file_search.py +360 -0
- coco_anthropic/middleware/prompt_caching.py +276 -0
- coco_anthropic/output_parsers.py +93 -0
- coco_anthropic/py.typed +0 -0
- coco_core/__init__.py +20 -0
- coco_core/_api/__init__.py +87 -0
- coco_core/_api/beta_decorator.py +266 -0
- coco_core/_api/deprecation.py +637 -0
- coco_core/_api/internal.py +23 -0
- coco_core/_api/path.py +50 -0
- coco_core/_import_utils.py +41 -0
- coco_core/_protocol.py +625 -0
- coco_core/_security/__init__.py +36 -0
- coco_core/_security/_exceptions.py +9 -0
- coco_core/_security/_policy.py +313 -0
- coco_core/_security/_ssrf_protection.py +155 -0
- coco_core/_security/_transport.py +254 -0
- coco_core/agents.py +258 -0
- coco_core/caches.py +272 -0
- coco_core/callbacks/__init__.py +132 -0
- coco_core/callbacks/base.py +1229 -0
- coco_core/callbacks/file.py +266 -0
- coco_core/callbacks/manager.py +2826 -0
- coco_core/callbacks/stdout.py +123 -0
- coco_core/callbacks/streaming_stdout.py +154 -0
- coco_core/callbacks/usage.py +124 -0
- coco_core/chat_history.py +267 -0
- coco_core/chat_loaders.py +26 -0
- coco_core/chat_sessions.py +19 -0
- coco_core/cross_encoders.py +18 -0
- coco_core/document_loaders/__init__.py +36 -0
- coco_core/document_loaders/base.py +155 -0
- coco_core/document_loaders/blob_loaders.py +38 -0
- coco_core/documents/__init__.py +55 -0
- coco_core/documents/base.py +347 -0
- coco_core/documents/compressor.py +74 -0
- coco_core/documents/transformers.py +79 -0
- coco_core/embeddings/__init__.py +31 -0
- coco_core/embeddings/embeddings.py +78 -0
- coco_core/embeddings/fake.py +129 -0
- coco_core/env.py +22 -0
- coco_core/example_selectors/__init__.py +47 -0
- coco_core/example_selectors/base.py +60 -0
- coco_core/example_selectors/length_based.py +131 -0
- coco_core/example_selectors/semantic_similarity.py +366 -0
- coco_core/exceptions.py +166 -0
- coco_core/globals.py +72 -0
- coco_core/indexing/__init__.py +53 -0
- coco_core/indexing/api.py +954 -0
- coco_core/indexing/base.py +661 -0
- coco_core/indexing/in_memory.py +104 -0
- coco_core/language_models/__init__.py +116 -0
- coco_core/language_models/_compat_bridge.py +844 -0
- coco_core/language_models/_utils.py +343 -0
- coco_core/language_models/base.py +500 -0
- coco_core/language_models/chat_model_stream.py +1525 -0
- coco_core/language_models/chat_models.py +2748 -0
- coco_core/language_models/fake.py +137 -0
- coco_core/language_models/fake_chat_models.py +396 -0
- coco_core/language_models/llms.py +1569 -0
- coco_core/language_models/model_profile.py +183 -0
- coco_core/load/__init__.py +44 -0
- coco_core/load/_validation.py +191 -0
- coco_core/load/dump.py +120 -0
- coco_core/load/load.py +828 -0
- coco_core/load/mapping.py +1097 -0
- coco_core/load/serializable.py +406 -0
- coco_core/messages/__init__.py +198 -0
- coco_core/messages/ai.py +854 -0
- coco_core/messages/base.py +518 -0
- coco_core/messages/block_translators/__init__.py +112 -0
- coco_core/messages/block_translators/anthropic.py +534 -0
- coco_core/messages/block_translators/bedrock.py +108 -0
- coco_core/messages/block_translators/bedrock_converse.py +329 -0
- coco_core/messages/block_translators/coco_v0.py +303 -0
- coco_core/messages/block_translators/google_genai.py +610 -0
- coco_core/messages/block_translators/google_vertexai.py +21 -0
- coco_core/messages/block_translators/groq.py +157 -0
- coco_core/messages/block_translators/openai.py +1089 -0
- coco_core/messages/chat.py +64 -0
- coco_core/messages/content.py +1488 -0
- coco_core/messages/function.py +62 -0
- coco_core/messages/human.py +70 -0
- coco_core/messages/modifier.py +33 -0
- coco_core/messages/system.py +70 -0
- coco_core/messages/tool.py +418 -0
- coco_core/messages/utils.py +2442 -0
- coco_core/output_parsers/__init__.py +101 -0
- coco_core/output_parsers/base.py +361 -0
- coco_core/output_parsers/format_instructions.py +16 -0
- coco_core/output_parsers/json.py +139 -0
- coco_core/output_parsers/list.py +249 -0
- coco_core/output_parsers/openai_functions.py +324 -0
- coco_core/output_parsers/openai_tools.py +383 -0
- coco_core/output_parsers/pydantic.py +141 -0
- coco_core/output_parsers/string.py +63 -0
- coco_core/output_parsers/transform.py +175 -0
- coco_core/output_parsers/xml.py +301 -0
- coco_core/outputs/__init__.py +66 -0
- coco_core/outputs/chat_generation.py +147 -0
- coco_core/outputs/chat_result.py +39 -0
- coco_core/outputs/generation.py +80 -0
- coco_core/outputs/llm_result.py +112 -0
- coco_core/outputs/run_info.py +22 -0
- coco_core/prompt_values.py +161 -0
- coco_core/prompts/__init__.py +101 -0
- coco_core/prompts/base.py +508 -0
- coco_core/prompts/chat.py +1495 -0
- coco_core/prompts/dict.py +177 -0
- coco_core/prompts/few_shot.py +483 -0
- coco_core/prompts/few_shot_with_templates.py +239 -0
- coco_core/prompts/image.py +177 -0
- coco_core/prompts/loading.py +296 -0
- coco_core/prompts/message.py +97 -0
- coco_core/prompts/prompt.py +312 -0
- coco_core/prompts/string.py +399 -0
- coco_core/prompts/structured.py +214 -0
- coco_core/py.typed +0 -0
- coco_core/rate_limiters.py +254 -0
- coco_core/retrievers.py +328 -0
- coco_core/runnables/__init__.py +136 -0
- coco_core/runnables/base.py +6725 -0
- coco_core/runnables/branch.py +457 -0
- coco_core/runnables/config.py +712 -0
- coco_core/runnables/configurable.py +710 -0
- coco_core/runnables/fallbacks.py +663 -0
- coco_core/runnables/graph.py +741 -0
- coco_core/runnables/graph_ascii.py +366 -0
- coco_core/runnables/graph_mermaid.py +503 -0
- coco_core/runnables/graph_png.py +215 -0
- coco_core/runnables/history.py +631 -0
- coco_core/runnables/passthrough.py +849 -0
- coco_core/runnables/retry.py +379 -0
- coco_core/runnables/router.py +239 -0
- coco_core/runnables/schema.py +188 -0
- coco_core/runnables/utils.py +798 -0
- coco_core/stores.py +291 -0
- coco_core/structured_query.py +203 -0
- coco_core/sys_info.py +154 -0
- coco_core/tools/__init__.py +95 -0
- coco_core/tools/base.py +1948 -0
- coco_core/tools/convert.py +497 -0
- coco_core/tools/render.py +67 -0
- coco_core/tools/retriever.py +94 -0
- coco_core/tools/simple.py +208 -0
- coco_core/tools/structured.py +322 -0
- coco_core/tracers/__init__.py +47 -0
- coco_core/tracers/_compat.py +95 -0
- coco_core/tracers/_context.py +337 -0
- coco_core/tracers/_streaming.py +53 -0
- coco_core/tracers/base.py +955 -0
- coco_core/tracers/coco.py +279 -0
- coco_core/tracers/context.py +205 -0
- coco_core/tracers/core.py +766 -0
- coco_core/tracers/event_stream.py +1101 -0
- coco_core/tracers/log_stream.py +765 -0
- coco_core/tracers/memory_stream.py +200 -0
- coco_core/tracers/root_listeners.py +130 -0
- coco_core/tracers/run_collector.py +40 -0
- coco_core/tracers/schemas.py +14 -0
- coco_core/tracers/stdout.py +205 -0
- coco_core/utils/__init__.py +111 -0
- coco_core/utils/_gateway.py +274 -0
- coco_core/utils/_merge.py +218 -0
- coco_core/utils/aiter.py +357 -0
- coco_core/utils/env.py +86 -0
- coco_core/utils/formatting.py +80 -0
- coco_core/utils/function_calling.py +869 -0
- coco_core/utils/html.py +132 -0
- coco_core/utils/image.py +15 -0
- coco_core/utils/input.py +82 -0
- coco_core/utils/interactive_env.py +12 -0
- coco_core/utils/iter.py +229 -0
- coco_core/utils/json.py +230 -0
- coco_core/utils/json_schema.py +273 -0
- coco_core/utils/mustache.py +706 -0
- coco_core/utils/pydantic.py +631 -0
- coco_core/utils/strings.py +69 -0
- coco_core/utils/usage.py +61 -0
- coco_core/utils/utils.py +528 -0
- coco_core/utils/uuid.py +57 -0
- coco_core/vectorstores/__init__.py +53 -0
- coco_core/vectorstores/base.py +1111 -0
- coco_core/vectorstores/in_memory.py +546 -0
- coco_core/vectorstores/utils.py +163 -0
- coco_core/version.py +3 -0
- coco_framework-0.1.0.dist-info/METADATA +184 -0
- coco_framework-0.1.0.dist-info/RECORD +400 -0
- coco_framework-0.1.0.dist-info/WHEEL +4 -0
- coco_framework-0.1.0.dist-info/licenses/LICENSE +24 -0
- coco_framework-0.1.0.dist-info/licenses/NOTICE +15 -0
- coco_graph/_internal/__init__.py +4 -0
- coco_graph/_internal/_cache.py +31 -0
- coco_graph/_internal/_config.py +474 -0
- coco_graph/_internal/_constants.py +140 -0
- coco_graph/_internal/_fields.py +213 -0
- coco_graph/_internal/_future.py +220 -0
- coco_graph/_internal/_pydantic.py +275 -0
- coco_graph/_internal/_queue.py +124 -0
- coco_graph/_internal/_replay.py +90 -0
- coco_graph/_internal/_retry.py +29 -0
- coco_graph/_internal/_runnable.py +969 -0
- coco_graph/_internal/_scratchpad.py +19 -0
- coco_graph/_internal/_serde.py +253 -0
- coco_graph/_internal/_timeout.py +25 -0
- coco_graph/_internal/_typing.py +54 -0
- coco_graph/cache/base/__init__.py +48 -0
- coco_graph/cache/base/py.typed +0 -0
- coco_graph/cache/memory/__init__.py +73 -0
- coco_graph/cache/redis/__init__.py +144 -0
- coco_graph/cache/sqlite/__init__.py +120 -0
- coco_graph/callbacks.py +394 -0
- coco_graph/channels/__init__.py +29 -0
- coco_graph/channels/any_value.py +72 -0
- coco_graph/channels/base.py +121 -0
- coco_graph/channels/binop.py +155 -0
- coco_graph/channels/delta.py +202 -0
- coco_graph/channels/ephemeral_value.py +79 -0
- coco_graph/channels/last_value.py +151 -0
- coco_graph/channels/named_barrier_value.py +167 -0
- coco_graph/channels/topic.py +94 -0
- coco_graph/channels/untracked_value.py +73 -0
- coco_graph/checkpoint/base/__init__.py +861 -0
- coco_graph/checkpoint/base/id.py +109 -0
- coco_graph/checkpoint/base/py.typed +0 -0
- coco_graph/checkpoint/memory/__init__.py +698 -0
- coco_graph/checkpoint/memory/py.typed +0 -0
- coco_graph/checkpoint/postgres/__init__.py +602 -0
- coco_graph/checkpoint/postgres/_ainternal.py +23 -0
- coco_graph/checkpoint/postgres/_internal.py +21 -0
- coco_graph/checkpoint/postgres/aio.py +691 -0
- coco_graph/checkpoint/postgres/base.py +666 -0
- coco_graph/checkpoint/postgres/py.typed +0 -0
- coco_graph/checkpoint/postgres/shallow.py +967 -0
- coco_graph/checkpoint/serde/__init__.py +0 -0
- coco_graph/checkpoint/serde/_msgpack.py +97 -0
- coco_graph/checkpoint/serde/base.py +64 -0
- coco_graph/checkpoint/serde/encrypted.py +80 -0
- coco_graph/checkpoint/serde/event_hooks.py +52 -0
- coco_graph/checkpoint/serde/jsonplus.py +883 -0
- coco_graph/checkpoint/serde/py.typed +0 -0
- coco_graph/checkpoint/serde/types.py +68 -0
- coco_graph/checkpoint/sqlite/__init__.py +645 -0
- coco_graph/checkpoint/sqlite/_delta.py +172 -0
- coco_graph/checkpoint/sqlite/aio.py +738 -0
- coco_graph/checkpoint/sqlite/py.typed +0 -0
- coco_graph/checkpoint/sqlite/utils.py +116 -0
- coco_graph/config.py +196 -0
- coco_graph/constants.py +64 -0
- coco_graph/errors.py +241 -0
- coco_graph/func/__init__.py +620 -0
- coco_graph/graph/__init__.py +12 -0
- coco_graph/graph/_branch.py +225 -0
- coco_graph/graph/_node.py +103 -0
- coco_graph/graph/message.py +437 -0
- coco_graph/graph/state.py +1978 -0
- coco_graph/graph/ui.py +227 -0
- coco_graph/managed/__init__.py +3 -0
- coco_graph/managed/base.py +31 -0
- coco_graph/managed/is_last_step.py +24 -0
- coco_graph/prebuilt/__init__.py +23 -0
- coco_graph/prebuilt/_tool_call_stream.py +117 -0
- coco_graph/prebuilt/_tool_call_transformer.py +165 -0
- coco_graph/prebuilt/chat_agent_executor.py +1015 -0
- coco_graph/prebuilt/interrupt.py +105 -0
- coco_graph/prebuilt/py.typed +0 -0
- coco_graph/prebuilt/tool_node.py +2030 -0
- coco_graph/prebuilt/tool_validator.py +221 -0
- coco_graph/pregel/__init__.py +3 -0
- coco_graph/pregel/_algo.py +1460 -0
- coco_graph/pregel/_call.py +298 -0
- coco_graph/pregel/_checkpoint.py +331 -0
- coco_graph/pregel/_config.py +0 -0
- coco_graph/pregel/_draw.py +294 -0
- coco_graph/pregel/_executor.py +223 -0
- coco_graph/pregel/_io.py +174 -0
- coco_graph/pregel/_log.py +3 -0
- coco_graph/pregel/_loop.py +1988 -0
- coco_graph/pregel/_messages.py +461 -0
- coco_graph/pregel/_read.py +312 -0
- coco_graph/pregel/_retry.py +854 -0
- coco_graph/pregel/_runner.py +941 -0
- coco_graph/pregel/_tools.py +268 -0
- coco_graph/pregel/_utils.py +221 -0
- coco_graph/pregel/_validate.py +120 -0
- coco_graph/pregel/_write.py +192 -0
- coco_graph/pregel/debug.py +302 -0
- coco_graph/pregel/main.py +4364 -0
- coco_graph/pregel/protocol.py +288 -0
- coco_graph/pregel/types.py +38 -0
- coco_graph/py.typed +0 -0
- coco_graph/runtime.py +350 -0
- coco_graph/store/base/__init__.py +1322 -0
- coco_graph/store/base/batch.py +371 -0
- coco_graph/store/base/embed.py +436 -0
- coco_graph/store/base/py.typed +0 -0
- coco_graph/store/memory/__init__.py +592 -0
- coco_graph/store/memory/py.typed +0 -0
- coco_graph/store/postgres/__init__.py +4 -0
- coco_graph/store/postgres/aio.py +592 -0
- coco_graph/store/postgres/base.py +1480 -0
- coco_graph/store/postgres/py.typed +0 -0
- coco_graph/store/sqlite/__init__.py +4 -0
- coco_graph/store/sqlite/aio.py +632 -0
- coco_graph/store/sqlite/base.py +1514 -0
- coco_graph/stream/__init__.py +45 -0
- coco_graph/stream/_convert.py +32 -0
- coco_graph/stream/_mux.py +523 -0
- coco_graph/stream/_types.py +330 -0
- coco_graph/stream/run_stream.py +735 -0
- coco_graph/stream/stream_channel.py +341 -0
- coco_graph/stream/transformers.py +1039 -0
- coco_graph/types.py +1079 -0
- coco_graph/typing.py +48 -0
- coco_graph/utils/__init__.py +1 -0
- coco_graph/utils/config.py +4 -0
- coco_graph/utils/runnable.py +3 -0
- coco_graph/version.py +13 -0
- coco_graph/warnings.py +69 -0
- coco_openai/__init__.py +20 -0
- coco_openai/_compat.py +45 -0
- coco_openai/_version.py +3 -0
- coco_openai/chat_models/__init__.py +6 -0
- coco_openai/chat_models/_client_utils.py +684 -0
- coco_openai/chat_models/_compat.py +525 -0
- coco_openai/chat_models/azure.py +1227 -0
- coco_openai/chat_models/base.py +5654 -0
- coco_openai/chat_models/codex.py +557 -0
- coco_openai/chatgpt_oauth.py +1063 -0
- coco_openai/data/__init__.py +1 -0
- coco_openai/data/_profiles.py +1655 -0
- coco_openai/data/profile_augmentations.toml +127 -0
- coco_openai/embeddings/__init__.py +6 -0
- coco_openai/embeddings/azure.py +240 -0
- coco_openai/embeddings/base.py +820 -0
- coco_openai/llms/__init__.py +6 -0
- coco_openai/llms/azure.py +249 -0
- coco_openai/llms/base.py +919 -0
- coco_openai/middleware/__init__.py +11 -0
- coco_openai/middleware/openai_moderation.py +484 -0
- coco_openai/output_parsers/__init__.py +9 -0
- coco_openai/output_parsers/tools.py +9 -0
- coco_openai/py.typed +0 -0
- coco_openai/tools/__init__.py +5 -0
- coco_openai/tools/custom_tool.py +111 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
"""Utilities for the root listener."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import traceback
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Any,
|
|
12
|
+
Literal,
|
|
13
|
+
cast,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from coco_core.exceptions import TracerException
|
|
17
|
+
from coco_core.load import dumpd
|
|
18
|
+
from coco_core.tracers.schemas import Run
|
|
19
|
+
from coco_core.utils._gateway import GATEWAY_METADATA_RESPONSE_KEY
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Coroutine, Sequence
|
|
23
|
+
from uuid import UUID
|
|
24
|
+
|
|
25
|
+
from tenacity import RetryCallState
|
|
26
|
+
|
|
27
|
+
from coco_core.documents import Document
|
|
28
|
+
from coco_core.messages import BaseMessage
|
|
29
|
+
from coco_core.outputs import (
|
|
30
|
+
ChatGeneration,
|
|
31
|
+
ChatGenerationChunk,
|
|
32
|
+
GenerationChunk,
|
|
33
|
+
LLMResult,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
SCHEMA_FORMAT_TYPE = Literal["original", "streaming_events"]
|
|
39
|
+
|
|
40
|
+
# Key under which gateway metadata is attached to a run's metadata for tracing.
|
|
41
|
+
_GATEWAY_RUN_METADATA_KEY = "ls_gateway_info"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _extract_gateway_metadata(response: LLMResult) -> dict[str, Any] | None:
|
|
45
|
+
"""Extract LangSmith gateway metadata from a model response.
|
|
46
|
+
|
|
47
|
+
When a request is routed through the LangSmith gateway, the gateway returns
|
|
48
|
+
metadata (e.g. the resolved provider and model) that integrations surface on
|
|
49
|
+
the generation's `generation_info` under `GATEWAY_METADATA_RESPONSE_KEY`.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
response: The `LLMResult` produced by the model.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The gateway metadata, or `None` if the response carries none.
|
|
56
|
+
"""
|
|
57
|
+
for generations in response.generations:
|
|
58
|
+
for generation in generations:
|
|
59
|
+
generation_info = getattr(generation, "generation_info", None) or {}
|
|
60
|
+
gateway_metadata = generation_info.get(GATEWAY_METADATA_RESPONSE_KEY)
|
|
61
|
+
if isinstance(gateway_metadata, dict):
|
|
62
|
+
return gateway_metadata
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class _TracerCore(ABC):
|
|
67
|
+
"""Abstract base class for tracers.
|
|
68
|
+
|
|
69
|
+
This class provides common methods, and reusable methods for tracers.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
log_missing_parent: bool = True
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
*,
|
|
77
|
+
_schema_format: Literal[
|
|
78
|
+
"original", "streaming_events", "original+chat"
|
|
79
|
+
] = "original",
|
|
80
|
+
run_map: dict[str, Run] | None = None,
|
|
81
|
+
order_map: dict[UUID, tuple[UUID, str]] | None = None,
|
|
82
|
+
_external_run_ids: dict[str, int] | None = None,
|
|
83
|
+
**kwargs: Any,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Initialize the tracer.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
_schema_format: Primarily changes how the inputs and outputs are handled.
|
|
89
|
+
|
|
90
|
+
For internal use only. This API will change.
|
|
91
|
+
|
|
92
|
+
- `'original'` is the format used by all current tracers.
|
|
93
|
+
|
|
94
|
+
This format is slightly inconsistent with respect to inputs and
|
|
95
|
+
outputs.
|
|
96
|
+
- `'streaming_events'` is used for supporting streaming events, for
|
|
97
|
+
internal usage. It will likely change in the future, or be
|
|
98
|
+
deprecated entirely in favor of a dedicated async tracer for
|
|
99
|
+
streaming events.
|
|
100
|
+
- `'original+chat'` is a format that is the same as `'original'` except
|
|
101
|
+
it does NOT raise an attribute error `on_chat_model_start`
|
|
102
|
+
run_map: Optional shared map of run ID to run.
|
|
103
|
+
order_map: Optional shared map of run ID to trace ordering data.
|
|
104
|
+
_external_run_ids: Optional shared set of externally injected run IDs.
|
|
105
|
+
**kwargs: Additional keyword arguments that will be passed to the
|
|
106
|
+
superclass.
|
|
107
|
+
"""
|
|
108
|
+
super().__init__(**kwargs)
|
|
109
|
+
|
|
110
|
+
self._schema_format = _schema_format # For internal use only API will change.
|
|
111
|
+
|
|
112
|
+
self.run_map = run_map if run_map is not None else {}
|
|
113
|
+
"""Map of run ID to run. Cleared on run end."""
|
|
114
|
+
|
|
115
|
+
self.order_map = order_map if order_map is not None else {}
|
|
116
|
+
"""Map of run ID to (trace_id, dotted_order). Cleared when tracer GCed."""
|
|
117
|
+
|
|
118
|
+
self._external_run_ids: dict[str, int] = (
|
|
119
|
+
_external_run_ids if _external_run_ids is not None else {}
|
|
120
|
+
)
|
|
121
|
+
"""Refcount of active children per externally-injected run ID.
|
|
122
|
+
|
|
123
|
+
These runs are added to `run_map` so child runs can find their parent,
|
|
124
|
+
but they are not managed by the tracer's callback lifecycle. When
|
|
125
|
+
the last child finishes the entry is evicted to avoid memory leaks.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def _persist_run(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
130
|
+
"""Persist a run."""
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _add_child_run(
|
|
134
|
+
parent_run: Run,
|
|
135
|
+
child_run: Run,
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Add child run to a chain run or tool run."""
|
|
138
|
+
parent_run.child_runs.append(child_run)
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def _get_stacktrace(error: BaseException) -> str:
|
|
142
|
+
"""Get the stacktrace of the parent error."""
|
|
143
|
+
msg = repr(error)
|
|
144
|
+
try:
|
|
145
|
+
tb = traceback.format_exception(error)
|
|
146
|
+
return (msg + "\n\n".join(tb)).strip()
|
|
147
|
+
except Exception:
|
|
148
|
+
return msg
|
|
149
|
+
|
|
150
|
+
def _start_trace(self, run: Run) -> Coroutine[Any, Any, None] | None: # type: ignore[return]
|
|
151
|
+
current_dotted_order = run.start_time.strftime("%Y%m%dT%H%M%S%fZ") + str(run.id)
|
|
152
|
+
if run.parent_run_id:
|
|
153
|
+
if parent := self.order_map.get(run.parent_run_id):
|
|
154
|
+
run.trace_id, run.dotted_order = parent
|
|
155
|
+
run.dotted_order += "." + current_dotted_order
|
|
156
|
+
if parent_run := self.run_map.get(str(run.parent_run_id)):
|
|
157
|
+
self._add_child_run(parent_run, run)
|
|
158
|
+
parent_key = str(run.parent_run_id)
|
|
159
|
+
if parent_key in self._external_run_ids:
|
|
160
|
+
self._external_run_ids[parent_key] += 1
|
|
161
|
+
else:
|
|
162
|
+
if self.log_missing_parent:
|
|
163
|
+
logger.debug(
|
|
164
|
+
"Parent run %s not found for run %s. Treating as a root run.",
|
|
165
|
+
run.parent_run_id,
|
|
166
|
+
run.id,
|
|
167
|
+
)
|
|
168
|
+
run.parent_run_id = None
|
|
169
|
+
run.trace_id = run.id
|
|
170
|
+
run.dotted_order = current_dotted_order
|
|
171
|
+
else:
|
|
172
|
+
run.trace_id = run.id
|
|
173
|
+
run.dotted_order = current_dotted_order
|
|
174
|
+
self.order_map[run.id] = (run.trace_id, run.dotted_order)
|
|
175
|
+
self.run_map[str(run.id)] = run
|
|
176
|
+
|
|
177
|
+
def _get_run(self, run_id: UUID, run_type: str | set[str] | None = None) -> Run:
|
|
178
|
+
try:
|
|
179
|
+
run = self.run_map[str(run_id)]
|
|
180
|
+
except KeyError as exc:
|
|
181
|
+
msg = f"No indexed run ID {run_id}."
|
|
182
|
+
raise TracerException(msg) from exc
|
|
183
|
+
|
|
184
|
+
if isinstance(run_type, str):
|
|
185
|
+
run_types: set[str] | None = {run_type}
|
|
186
|
+
else:
|
|
187
|
+
run_types = run_type
|
|
188
|
+
if run_types is not None and run.run_type not in run_types:
|
|
189
|
+
msg = (
|
|
190
|
+
f"Found {run.run_type} run at ID {run_id}, "
|
|
191
|
+
f"but expected {run_types} run."
|
|
192
|
+
)
|
|
193
|
+
raise TracerException(msg)
|
|
194
|
+
return run
|
|
195
|
+
|
|
196
|
+
def _create_chat_model_run(
|
|
197
|
+
self,
|
|
198
|
+
serialized: dict[str, Any],
|
|
199
|
+
messages: list[list[BaseMessage]],
|
|
200
|
+
run_id: UUID,
|
|
201
|
+
tags: list[str] | None = None,
|
|
202
|
+
parent_run_id: UUID | None = None,
|
|
203
|
+
metadata: dict[str, Any] | None = None,
|
|
204
|
+
name: str | None = None,
|
|
205
|
+
**kwargs: Any,
|
|
206
|
+
) -> Run:
|
|
207
|
+
"""Create a chat model run."""
|
|
208
|
+
if self._schema_format not in {"streaming_events", "original+chat"}:
|
|
209
|
+
# Please keep this un-implemented for backwards compatibility.
|
|
210
|
+
# When it's unimplemented old tracers that use the "original" format
|
|
211
|
+
# fallback on the on_llm_start method implementation if they
|
|
212
|
+
# find that the on_chat_model_start method is not implemented.
|
|
213
|
+
# This can eventually be cleaned up by writing a "modern" tracer
|
|
214
|
+
# that has all the updated schema changes corresponding to
|
|
215
|
+
# the "streaming_events" format.
|
|
216
|
+
msg = (
|
|
217
|
+
f"Chat model tracing is not supported in "
|
|
218
|
+
f"for {self._schema_format} format."
|
|
219
|
+
)
|
|
220
|
+
raise NotImplementedError(msg)
|
|
221
|
+
start_time = datetime.now(timezone.utc)
|
|
222
|
+
if metadata:
|
|
223
|
+
kwargs.update({"metadata": metadata})
|
|
224
|
+
return Run(
|
|
225
|
+
id=run_id,
|
|
226
|
+
parent_run_id=parent_run_id,
|
|
227
|
+
serialized=serialized,
|
|
228
|
+
inputs={"messages": [[dumpd(msg) for msg in batch] for batch in messages]},
|
|
229
|
+
extra=kwargs,
|
|
230
|
+
events=[{"name": "start", "time": start_time}],
|
|
231
|
+
start_time=start_time,
|
|
232
|
+
# WARNING: This is valid ONLY for streaming_events.
|
|
233
|
+
# run_type="llm" is what's used by virtually all tracers.
|
|
234
|
+
# Changing this to "chat_model" may break triggering on_llm_start
|
|
235
|
+
run_type="chat_model",
|
|
236
|
+
tags=tags,
|
|
237
|
+
name=name,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def _create_llm_run(
|
|
241
|
+
self,
|
|
242
|
+
serialized: dict[str, Any],
|
|
243
|
+
prompts: list[str],
|
|
244
|
+
run_id: UUID,
|
|
245
|
+
tags: list[str] | None = None,
|
|
246
|
+
parent_run_id: UUID | None = None,
|
|
247
|
+
metadata: dict[str, Any] | None = None,
|
|
248
|
+
name: str | None = None,
|
|
249
|
+
**kwargs: Any,
|
|
250
|
+
) -> Run:
|
|
251
|
+
"""Create a llm run."""
|
|
252
|
+
start_time = datetime.now(timezone.utc)
|
|
253
|
+
if metadata:
|
|
254
|
+
kwargs.update({"metadata": metadata})
|
|
255
|
+
return Run(
|
|
256
|
+
id=run_id,
|
|
257
|
+
parent_run_id=parent_run_id,
|
|
258
|
+
serialized=serialized,
|
|
259
|
+
# TODO: Figure out how to expose kwargs here
|
|
260
|
+
inputs={"prompts": prompts},
|
|
261
|
+
extra=kwargs,
|
|
262
|
+
events=[{"name": "start", "time": start_time}],
|
|
263
|
+
start_time=start_time,
|
|
264
|
+
run_type="llm",
|
|
265
|
+
tags=tags or [],
|
|
266
|
+
name=name,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
def _llm_run_with_token_event(
|
|
270
|
+
self,
|
|
271
|
+
token: str | list[str | dict[str, Any]],
|
|
272
|
+
run_id: UUID,
|
|
273
|
+
chunk: GenerationChunk | ChatGenerationChunk | None = None,
|
|
274
|
+
parent_run_id: UUID | None = None,
|
|
275
|
+
) -> Run:
|
|
276
|
+
"""Append token event to LLM run and return the run."""
|
|
277
|
+
_ = parent_run_id
|
|
278
|
+
llm_run = self._get_run(run_id, run_type={"llm", "chat_model"})
|
|
279
|
+
event_kwargs: dict[str, Any] = {"token": token}
|
|
280
|
+
if chunk:
|
|
281
|
+
event_kwargs["chunk"] = chunk
|
|
282
|
+
llm_run.events.append(
|
|
283
|
+
{
|
|
284
|
+
"name": "new_token",
|
|
285
|
+
"time": datetime.now(timezone.utc),
|
|
286
|
+
"kwargs": event_kwargs,
|
|
287
|
+
},
|
|
288
|
+
)
|
|
289
|
+
return llm_run
|
|
290
|
+
|
|
291
|
+
def _llm_run_with_retry_event(
|
|
292
|
+
self,
|
|
293
|
+
retry_state: RetryCallState,
|
|
294
|
+
run_id: UUID,
|
|
295
|
+
) -> Run:
|
|
296
|
+
llm_run = self._get_run(run_id)
|
|
297
|
+
retry_d: dict[str, Any] = {
|
|
298
|
+
"slept": retry_state.idle_for,
|
|
299
|
+
"attempt": retry_state.attempt_number,
|
|
300
|
+
}
|
|
301
|
+
if retry_state.outcome is None:
|
|
302
|
+
retry_d["outcome"] = "N/A"
|
|
303
|
+
elif retry_state.outcome.failed:
|
|
304
|
+
retry_d["outcome"] = "failed"
|
|
305
|
+
exception = retry_state.outcome.exception()
|
|
306
|
+
retry_d["exception"] = str(exception)
|
|
307
|
+
retry_d["exception_type"] = exception.__class__.__name__
|
|
308
|
+
else:
|
|
309
|
+
retry_d["outcome"] = "success"
|
|
310
|
+
retry_d["result"] = str(retry_state.outcome.result())
|
|
311
|
+
llm_run.events.append(
|
|
312
|
+
{
|
|
313
|
+
"name": "retry",
|
|
314
|
+
"time": datetime.now(timezone.utc),
|
|
315
|
+
"kwargs": retry_d,
|
|
316
|
+
},
|
|
317
|
+
)
|
|
318
|
+
return llm_run
|
|
319
|
+
|
|
320
|
+
def _complete_llm_run(self, response: LLMResult, run_id: UUID) -> Run:
|
|
321
|
+
llm_run = self._get_run(run_id, run_type={"llm", "chat_model"})
|
|
322
|
+
if getattr(llm_run, "outputs", None) is None:
|
|
323
|
+
llm_run.outputs = {}
|
|
324
|
+
else:
|
|
325
|
+
llm_run.outputs = cast("dict[str, Any]", llm_run.outputs)
|
|
326
|
+
if not llm_run.extra.get("__omit_auto_outputs", False):
|
|
327
|
+
llm_run.outputs.update(response.model_dump())
|
|
328
|
+
for i, generations in enumerate(response.generations):
|
|
329
|
+
for j, generation in enumerate(generations):
|
|
330
|
+
output_generation = llm_run.outputs["generations"][i][j]
|
|
331
|
+
if "message" in output_generation:
|
|
332
|
+
output_generation["message"] = dumpd(
|
|
333
|
+
cast("ChatGeneration", generation).message
|
|
334
|
+
)
|
|
335
|
+
llm_run.end_time = datetime.now(timezone.utc)
|
|
336
|
+
llm_run.events.append({"name": "end", "time": llm_run.end_time})
|
|
337
|
+
|
|
338
|
+
tool_call_count = 0
|
|
339
|
+
for generations in response.generations:
|
|
340
|
+
for generation in generations:
|
|
341
|
+
if hasattr(generation, "message"):
|
|
342
|
+
msg = generation.message
|
|
343
|
+
if hasattr(msg, "tool_calls") and msg.tool_calls:
|
|
344
|
+
tool_call_count += len(msg.tool_calls)
|
|
345
|
+
if tool_call_count > 0:
|
|
346
|
+
llm_run.extra["tool_call_count"] = tool_call_count
|
|
347
|
+
|
|
348
|
+
self._attach_gateway_metadata(llm_run, response)
|
|
349
|
+
|
|
350
|
+
return llm_run
|
|
351
|
+
|
|
352
|
+
def _attach_gateway_metadata(self, run: Run, response: LLMResult) -> None:
|
|
353
|
+
"""Promote LangSmith gateway metadata from the response to run metadata."""
|
|
354
|
+
gateway_metadata = _extract_gateway_metadata(response)
|
|
355
|
+
if gateway_metadata is None:
|
|
356
|
+
return
|
|
357
|
+
metadata = run.extra.setdefault("metadata", {})
|
|
358
|
+
metadata[_GATEWAY_RUN_METADATA_KEY] = gateway_metadata
|
|
359
|
+
# The resolved gateway identity takes precedence over request-time metadata.
|
|
360
|
+
for source, target in (("model", "ls_model_name"), ("provider", "ls_provider")):
|
|
361
|
+
value = gateway_metadata.get(source)
|
|
362
|
+
if isinstance(value, str) and (value := value.strip()):
|
|
363
|
+
metadata[target] = value
|
|
364
|
+
|
|
365
|
+
def _errored_llm_run(
|
|
366
|
+
self, error: BaseException, run_id: UUID, response: LLMResult | None = None
|
|
367
|
+
) -> Run:
|
|
368
|
+
llm_run = self._get_run(run_id, run_type={"llm", "chat_model"})
|
|
369
|
+
llm_run.error = self._get_stacktrace(error)
|
|
370
|
+
if response:
|
|
371
|
+
if getattr(llm_run, "outputs", None) is None:
|
|
372
|
+
llm_run.outputs = {}
|
|
373
|
+
else:
|
|
374
|
+
llm_run.outputs = cast("dict[str, Any]", llm_run.outputs)
|
|
375
|
+
if not llm_run.extra.get("__omit_auto_outputs", False):
|
|
376
|
+
llm_run.outputs.update(response.model_dump())
|
|
377
|
+
for i, generations in enumerate(response.generations):
|
|
378
|
+
for j, generation in enumerate(generations):
|
|
379
|
+
output_generation = llm_run.outputs["generations"][i][j]
|
|
380
|
+
if "message" in output_generation:
|
|
381
|
+
output_generation["message"] = dumpd(
|
|
382
|
+
cast("ChatGeneration", generation).message
|
|
383
|
+
)
|
|
384
|
+
self._attach_gateway_metadata(llm_run, response)
|
|
385
|
+
llm_run.end_time = datetime.now(timezone.utc)
|
|
386
|
+
llm_run.events.append({"name": "error", "time": llm_run.end_time})
|
|
387
|
+
|
|
388
|
+
return llm_run
|
|
389
|
+
|
|
390
|
+
def _create_chain_run(
|
|
391
|
+
self,
|
|
392
|
+
serialized: dict[str, Any],
|
|
393
|
+
inputs: dict[str, Any],
|
|
394
|
+
run_id: UUID,
|
|
395
|
+
tags: list[str] | None = None,
|
|
396
|
+
parent_run_id: UUID | None = None,
|
|
397
|
+
metadata: dict[str, Any] | None = None,
|
|
398
|
+
run_type: str | None = None,
|
|
399
|
+
name: str | None = None,
|
|
400
|
+
**kwargs: Any,
|
|
401
|
+
) -> Run:
|
|
402
|
+
"""Create a chain Run."""
|
|
403
|
+
start_time = datetime.now(timezone.utc)
|
|
404
|
+
if metadata:
|
|
405
|
+
kwargs.update({"metadata": metadata})
|
|
406
|
+
return Run(
|
|
407
|
+
id=run_id,
|
|
408
|
+
parent_run_id=parent_run_id,
|
|
409
|
+
serialized=serialized,
|
|
410
|
+
inputs=self._get_chain_inputs(inputs),
|
|
411
|
+
extra=kwargs,
|
|
412
|
+
events=[{"name": "start", "time": start_time}],
|
|
413
|
+
start_time=start_time,
|
|
414
|
+
child_runs=[],
|
|
415
|
+
run_type=run_type or "chain",
|
|
416
|
+
name=name,
|
|
417
|
+
tags=tags or [],
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
def _get_chain_inputs(self, inputs: Any) -> Any:
|
|
421
|
+
"""Get the inputs for a chain run."""
|
|
422
|
+
if self._schema_format in {"original", "original+chat"}:
|
|
423
|
+
return inputs if isinstance(inputs, dict) else {"input": inputs}
|
|
424
|
+
if self._schema_format == "streaming_events":
|
|
425
|
+
return {
|
|
426
|
+
"input": inputs,
|
|
427
|
+
}
|
|
428
|
+
msg = f"Invalid format: {self._schema_format}"
|
|
429
|
+
raise ValueError(msg)
|
|
430
|
+
|
|
431
|
+
def _get_chain_outputs(self, outputs: Any) -> Any:
|
|
432
|
+
"""Get the outputs for a chain run."""
|
|
433
|
+
if self._schema_format in {"original", "original+chat"}:
|
|
434
|
+
return outputs if isinstance(outputs, dict) else {"output": outputs}
|
|
435
|
+
if self._schema_format == "streaming_events":
|
|
436
|
+
return {
|
|
437
|
+
"output": outputs,
|
|
438
|
+
}
|
|
439
|
+
msg = f"Invalid format: {self._schema_format}"
|
|
440
|
+
raise ValueError(msg)
|
|
441
|
+
|
|
442
|
+
def _complete_chain_run(
|
|
443
|
+
self,
|
|
444
|
+
outputs: dict[str, Any],
|
|
445
|
+
run_id: UUID,
|
|
446
|
+
inputs: dict[str, Any] | None = None,
|
|
447
|
+
) -> Run:
|
|
448
|
+
"""Update a chain run with outputs and end time."""
|
|
449
|
+
chain_run = self._get_run(run_id)
|
|
450
|
+
if getattr(chain_run, "outputs", None) is None:
|
|
451
|
+
chain_run.outputs = {}
|
|
452
|
+
if not chain_run.extra.get("__omit_auto_outputs", False):
|
|
453
|
+
cast("dict[str, Any]", chain_run.outputs).update(
|
|
454
|
+
self._get_chain_outputs(outputs)
|
|
455
|
+
)
|
|
456
|
+
chain_run.end_time = datetime.now(timezone.utc)
|
|
457
|
+
chain_run.events.append({"name": "end", "time": chain_run.end_time})
|
|
458
|
+
if inputs is not None:
|
|
459
|
+
chain_run.inputs = self._get_chain_inputs(inputs)
|
|
460
|
+
return chain_run
|
|
461
|
+
|
|
462
|
+
def _errored_chain_run(
|
|
463
|
+
self,
|
|
464
|
+
error: BaseException,
|
|
465
|
+
inputs: dict[str, Any] | None,
|
|
466
|
+
run_id: UUID,
|
|
467
|
+
) -> Run:
|
|
468
|
+
chain_run = self._get_run(run_id)
|
|
469
|
+
chain_run.error = self._get_stacktrace(error)
|
|
470
|
+
chain_run.end_time = datetime.now(timezone.utc)
|
|
471
|
+
chain_run.events.append({"name": "error", "time": chain_run.end_time})
|
|
472
|
+
if inputs is not None:
|
|
473
|
+
chain_run.inputs = self._get_chain_inputs(inputs)
|
|
474
|
+
return chain_run
|
|
475
|
+
|
|
476
|
+
def _create_tool_run(
|
|
477
|
+
self,
|
|
478
|
+
serialized: dict[str, Any],
|
|
479
|
+
input_str: str,
|
|
480
|
+
run_id: UUID,
|
|
481
|
+
tags: list[str] | None = None,
|
|
482
|
+
parent_run_id: UUID | None = None,
|
|
483
|
+
metadata: dict[str, Any] | None = None,
|
|
484
|
+
name: str | None = None,
|
|
485
|
+
inputs: dict[str, Any] | None = None,
|
|
486
|
+
**kwargs: Any,
|
|
487
|
+
) -> Run:
|
|
488
|
+
"""Create a tool run."""
|
|
489
|
+
start_time = datetime.now(timezone.utc)
|
|
490
|
+
if metadata:
|
|
491
|
+
kwargs.update({"metadata": metadata})
|
|
492
|
+
|
|
493
|
+
if self._schema_format in {"original", "original+chat"}:
|
|
494
|
+
inputs = inputs if isinstance(inputs, dict) else {"input": input_str}
|
|
495
|
+
elif self._schema_format == "streaming_events":
|
|
496
|
+
inputs = {"input": inputs}
|
|
497
|
+
else:
|
|
498
|
+
msg = f"Invalid format: {self._schema_format}"
|
|
499
|
+
raise AssertionError(msg)
|
|
500
|
+
|
|
501
|
+
return Run(
|
|
502
|
+
id=run_id,
|
|
503
|
+
parent_run_id=parent_run_id,
|
|
504
|
+
serialized=serialized,
|
|
505
|
+
# Wrapping in dict since Run requires a dict object.
|
|
506
|
+
inputs=inputs,
|
|
507
|
+
extra=kwargs,
|
|
508
|
+
events=[{"name": "start", "time": start_time}],
|
|
509
|
+
start_time=start_time,
|
|
510
|
+
child_runs=[],
|
|
511
|
+
run_type="tool",
|
|
512
|
+
tags=tags or [],
|
|
513
|
+
name=name,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
def _complete_tool_run(
|
|
517
|
+
self,
|
|
518
|
+
output: dict[str, Any],
|
|
519
|
+
run_id: UUID,
|
|
520
|
+
) -> Run:
|
|
521
|
+
"""Update a tool run with outputs and end time."""
|
|
522
|
+
tool_run = self._get_run(run_id, run_type="tool")
|
|
523
|
+
if getattr(tool_run, "outputs", None) is None:
|
|
524
|
+
tool_run.outputs = {}
|
|
525
|
+
if not tool_run.extra.get("__omit_auto_outputs", False):
|
|
526
|
+
cast("dict[str, Any]", tool_run.outputs).update({"output": output})
|
|
527
|
+
tool_run.end_time = datetime.now(timezone.utc)
|
|
528
|
+
tool_run.events.append({"name": "end", "time": tool_run.end_time})
|
|
529
|
+
return tool_run
|
|
530
|
+
|
|
531
|
+
def _errored_tool_run(
|
|
532
|
+
self,
|
|
533
|
+
error: BaseException,
|
|
534
|
+
run_id: UUID,
|
|
535
|
+
) -> Run:
|
|
536
|
+
"""Update a tool run with error and end time."""
|
|
537
|
+
tool_run = self._get_run(run_id, run_type="tool")
|
|
538
|
+
tool_run.error = self._get_stacktrace(error)
|
|
539
|
+
tool_run.end_time = datetime.now(timezone.utc)
|
|
540
|
+
tool_run.events.append({"name": "error", "time": tool_run.end_time})
|
|
541
|
+
return tool_run
|
|
542
|
+
|
|
543
|
+
def _create_retrieval_run(
|
|
544
|
+
self,
|
|
545
|
+
serialized: dict[str, Any],
|
|
546
|
+
query: str,
|
|
547
|
+
run_id: UUID,
|
|
548
|
+
parent_run_id: UUID | None = None,
|
|
549
|
+
tags: list[str] | None = None,
|
|
550
|
+
metadata: dict[str, Any] | None = None,
|
|
551
|
+
name: str | None = None,
|
|
552
|
+
**kwargs: Any,
|
|
553
|
+
) -> Run:
|
|
554
|
+
"""Create a retrieval run."""
|
|
555
|
+
start_time = datetime.now(timezone.utc)
|
|
556
|
+
if metadata:
|
|
557
|
+
kwargs.update({"metadata": metadata})
|
|
558
|
+
return Run(
|
|
559
|
+
id=run_id,
|
|
560
|
+
name=name or "Retriever",
|
|
561
|
+
parent_run_id=parent_run_id,
|
|
562
|
+
serialized=serialized,
|
|
563
|
+
inputs={"query": query},
|
|
564
|
+
extra=kwargs,
|
|
565
|
+
events=[{"name": "start", "time": start_time}],
|
|
566
|
+
start_time=start_time,
|
|
567
|
+
tags=tags,
|
|
568
|
+
child_runs=[],
|
|
569
|
+
run_type="retriever",
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
def _complete_retrieval_run(
|
|
573
|
+
self,
|
|
574
|
+
documents: Sequence[Document],
|
|
575
|
+
run_id: UUID,
|
|
576
|
+
) -> Run:
|
|
577
|
+
"""Update a retrieval run with outputs and end time."""
|
|
578
|
+
retrieval_run = self._get_run(run_id, run_type="retriever")
|
|
579
|
+
if getattr(retrieval_run, "outputs", None) is None:
|
|
580
|
+
retrieval_run.outputs = {}
|
|
581
|
+
if not retrieval_run.extra.get("__omit_auto_outputs", False):
|
|
582
|
+
cast("dict[str, Any]", retrieval_run.outputs).update(
|
|
583
|
+
{"documents": documents}
|
|
584
|
+
)
|
|
585
|
+
retrieval_run.end_time = datetime.now(timezone.utc)
|
|
586
|
+
retrieval_run.events.append({"name": "end", "time": retrieval_run.end_time})
|
|
587
|
+
return retrieval_run
|
|
588
|
+
|
|
589
|
+
def _errored_retrieval_run(
|
|
590
|
+
self,
|
|
591
|
+
error: BaseException,
|
|
592
|
+
run_id: UUID,
|
|
593
|
+
) -> Run:
|
|
594
|
+
retrieval_run = self._get_run(run_id, run_type="retriever")
|
|
595
|
+
retrieval_run.error = self._get_stacktrace(error)
|
|
596
|
+
retrieval_run.end_time = datetime.now(timezone.utc)
|
|
597
|
+
retrieval_run.events.append({"name": "error", "time": retrieval_run.end_time})
|
|
598
|
+
return retrieval_run
|
|
599
|
+
|
|
600
|
+
def __deepcopy__(self, memo: dict[int, Any] | None = None) -> _TracerCore:
|
|
601
|
+
"""Return self deepcopied."""
|
|
602
|
+
return self
|
|
603
|
+
|
|
604
|
+
def __copy__(self) -> _TracerCore:
|
|
605
|
+
"""Return self copied."""
|
|
606
|
+
return self
|
|
607
|
+
|
|
608
|
+
def _end_trace(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
609
|
+
"""End a trace for a run.
|
|
610
|
+
|
|
611
|
+
Args:
|
|
612
|
+
run: The run.
|
|
613
|
+
"""
|
|
614
|
+
_ = run
|
|
615
|
+
return None
|
|
616
|
+
|
|
617
|
+
def _on_run_create(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
618
|
+
"""Process a run upon creation.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
run: The created run.
|
|
622
|
+
"""
|
|
623
|
+
_ = run
|
|
624
|
+
return None
|
|
625
|
+
|
|
626
|
+
def _on_run_update(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
627
|
+
"""Process a run upon update.
|
|
628
|
+
|
|
629
|
+
Args:
|
|
630
|
+
run: The updated run.
|
|
631
|
+
"""
|
|
632
|
+
_ = run
|
|
633
|
+
return None
|
|
634
|
+
|
|
635
|
+
def _on_llm_start(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
636
|
+
"""Process the LLM Run upon start.
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
run: The LLM run.
|
|
640
|
+
"""
|
|
641
|
+
_ = run
|
|
642
|
+
return None
|
|
643
|
+
|
|
644
|
+
def _on_llm_new_token(
|
|
645
|
+
self,
|
|
646
|
+
run: Run,
|
|
647
|
+
token: str | list[str | dict[str, Any]],
|
|
648
|
+
chunk: GenerationChunk | ChatGenerationChunk | None,
|
|
649
|
+
) -> Coroutine[Any, Any, None] | None:
|
|
650
|
+
"""Process new LLM token.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
run: The LLM run.
|
|
654
|
+
token: The new token, or a list of content blocks.
|
|
655
|
+
chunk: Optional chunk.
|
|
656
|
+
"""
|
|
657
|
+
_ = (run, token, chunk)
|
|
658
|
+
return None
|
|
659
|
+
|
|
660
|
+
def _on_llm_end(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
661
|
+
"""Process the LLM Run.
|
|
662
|
+
|
|
663
|
+
Args:
|
|
664
|
+
run: The LLM run.
|
|
665
|
+
"""
|
|
666
|
+
_ = run
|
|
667
|
+
return None
|
|
668
|
+
|
|
669
|
+
def _on_llm_error(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
670
|
+
"""Process the LLM Run upon error.
|
|
671
|
+
|
|
672
|
+
Args:
|
|
673
|
+
run: The LLM run.
|
|
674
|
+
"""
|
|
675
|
+
_ = run
|
|
676
|
+
return None
|
|
677
|
+
|
|
678
|
+
def _on_chain_start(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
679
|
+
"""Process the Chain Run upon start.
|
|
680
|
+
|
|
681
|
+
Args:
|
|
682
|
+
run: The chain run.
|
|
683
|
+
"""
|
|
684
|
+
_ = run
|
|
685
|
+
return None
|
|
686
|
+
|
|
687
|
+
def _on_chain_end(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
688
|
+
"""Process the Chain Run.
|
|
689
|
+
|
|
690
|
+
Args:
|
|
691
|
+
run: The chain run.
|
|
692
|
+
"""
|
|
693
|
+
_ = run
|
|
694
|
+
return None
|
|
695
|
+
|
|
696
|
+
def _on_chain_error(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
697
|
+
"""Process the Chain Run upon error.
|
|
698
|
+
|
|
699
|
+
Args:
|
|
700
|
+
run: The chain run.
|
|
701
|
+
"""
|
|
702
|
+
_ = run
|
|
703
|
+
return None
|
|
704
|
+
|
|
705
|
+
def _on_tool_start(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
706
|
+
"""Process the Tool Run upon start.
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
run: The tool run.
|
|
710
|
+
"""
|
|
711
|
+
_ = run
|
|
712
|
+
return None
|
|
713
|
+
|
|
714
|
+
def _on_tool_end(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
715
|
+
"""Process the Tool Run.
|
|
716
|
+
|
|
717
|
+
Args:
|
|
718
|
+
run: The tool run.
|
|
719
|
+
"""
|
|
720
|
+
_ = run
|
|
721
|
+
return None
|
|
722
|
+
|
|
723
|
+
def _on_tool_error(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
724
|
+
"""Process the Tool Run upon error.
|
|
725
|
+
|
|
726
|
+
Args:
|
|
727
|
+
run: The tool run.
|
|
728
|
+
"""
|
|
729
|
+
_ = run
|
|
730
|
+
return None
|
|
731
|
+
|
|
732
|
+
def _on_chat_model_start(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
733
|
+
"""Process the Chat Model Run upon start.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
run: The chat model run.
|
|
737
|
+
"""
|
|
738
|
+
_ = run
|
|
739
|
+
return None
|
|
740
|
+
|
|
741
|
+
def _on_retriever_start(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
742
|
+
"""Process the Retriever Run upon start.
|
|
743
|
+
|
|
744
|
+
Args:
|
|
745
|
+
run: The retriever run.
|
|
746
|
+
"""
|
|
747
|
+
_ = run
|
|
748
|
+
return None
|
|
749
|
+
|
|
750
|
+
def _on_retriever_end(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
751
|
+
"""Process the Retriever Run.
|
|
752
|
+
|
|
753
|
+
Args:
|
|
754
|
+
run: The retriever run.
|
|
755
|
+
"""
|
|
756
|
+
_ = run
|
|
757
|
+
return None
|
|
758
|
+
|
|
759
|
+
def _on_retriever_error(self, run: Run) -> Coroutine[Any, Any, None] | None:
|
|
760
|
+
"""Process the Retriever Run upon error.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
run: The retriever run.
|
|
764
|
+
"""
|
|
765
|
+
_ = run
|
|
766
|
+
return None
|