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,279 @@
|
|
|
1
|
+
"""A tracer that keeps runs in memory. It sends nothing anywhere.
|
|
2
|
+
|
|
3
|
+
Upstream's tracer at this path posted every run to its vendor's hosted service. Coco
|
|
4
|
+
keeps the class, so the callback manager, the graph runtime and user code that look
|
|
5
|
+
for it still work, and drops the network. The last finished root run stays on
|
|
6
|
+
`latest_run`, which is where the Coco receipt reads from.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
14
|
+
from uuid import UUID
|
|
15
|
+
|
|
16
|
+
from typing_extensions import override
|
|
17
|
+
|
|
18
|
+
from coco_core.env import get_runtime_environment
|
|
19
|
+
from coco_core.load import dumpd
|
|
20
|
+
from coco_core.messages.ai import UsageMetadata, add_usage
|
|
21
|
+
from coco_core.tracers._compat import run_construct, run_to_dict
|
|
22
|
+
from coco_core.tracers._context import get_tracer_project, get_tracing_context
|
|
23
|
+
from coco_core.tracers.base import BaseTracer
|
|
24
|
+
from coco_core.tracers.schemas import Run
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Mapping
|
|
28
|
+
|
|
29
|
+
from coco_core.messages import BaseMessage
|
|
30
|
+
from coco_core.outputs import ChatGenerationChunk, GenerationChunk
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
_LOGGED: set[tuple[str, type[Exception]]] = set()
|
|
34
|
+
|
|
35
|
+
OVERRIDABLE_LANGSMITH_INHERITABLE_METADATA_KEYS: frozenset[str] = frozenset({"ls_agent_type"})
|
|
36
|
+
"""Tracing metadata keys a nested caller may override. Every other key is first wins."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def log_error_once(method: str, exception: Exception) -> None:
|
|
40
|
+
"""Log an error once per method and exception type."""
|
|
41
|
+
if (method, type(exception)) in _LOGGED:
|
|
42
|
+
return
|
|
43
|
+
_LOGGED.add((method, type(exception)))
|
|
44
|
+
logger.error(exception)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def wait_for_all_tracers() -> None:
|
|
48
|
+
"""Nothing is queued, so there is nothing to wait for."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_usage_metadata_from_generations(
|
|
52
|
+
generations: list[list[dict[str, Any]]],
|
|
53
|
+
) -> UsageMetadata | None:
|
|
54
|
+
"""Sum `usage_metadata` across serialized generations."""
|
|
55
|
+
output: UsageMetadata | None = None
|
|
56
|
+
for batch in generations:
|
|
57
|
+
for generation in batch:
|
|
58
|
+
if isinstance(generation, dict) and "message" in generation:
|
|
59
|
+
usage = _get_usage_metadata_from_message(generation["message"])
|
|
60
|
+
if usage is not None:
|
|
61
|
+
output = add_usage(output, usage)
|
|
62
|
+
return output
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_usage_metadata_from_message(message: Any) -> UsageMetadata | None:
|
|
66
|
+
if not isinstance(message, dict):
|
|
67
|
+
return None
|
|
68
|
+
kwargs = message.get("kwargs")
|
|
69
|
+
if isinstance(kwargs, dict) and isinstance(kwargs.get("usage_metadata"), dict):
|
|
70
|
+
return cast("UsageMetadata", kwargs["usage_metadata"])
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CocoTracer(BaseTracer):
|
|
75
|
+
"""Keeps each trace's runs in memory, with project, tags and metadata applied."""
|
|
76
|
+
|
|
77
|
+
run_inline = True
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
example_id: UUID | str | None = None,
|
|
82
|
+
project_name: str | None = None,
|
|
83
|
+
client: Any | None = None,
|
|
84
|
+
tags: list[str] | None = None,
|
|
85
|
+
*,
|
|
86
|
+
metadata: Mapping[str, str] | None = None,
|
|
87
|
+
**kwargs: Any,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Create the tracer.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
example_id: Attached to root runs as `reference_example_id`.
|
|
93
|
+
project_name: The project on each run. Defaults to `COCO_PROJECT`.
|
|
94
|
+
client: Accepted for signature compatibility and never called.
|
|
95
|
+
tags: Added to every run.
|
|
96
|
+
metadata: Added to a run's metadata where the run lacks the key.
|
|
97
|
+
**kwargs: Passed to `BaseTracer`.
|
|
98
|
+
"""
|
|
99
|
+
super().__init__(**kwargs)
|
|
100
|
+
self.example_id = UUID(example_id) if isinstance(example_id, str) else example_id
|
|
101
|
+
self.project_name = project_name or get_tracer_project()
|
|
102
|
+
self.client = client
|
|
103
|
+
self.tags = tags or []
|
|
104
|
+
self.latest_run: Run | None = None
|
|
105
|
+
self.run_has_token_event_map: dict[str, bool] = {}
|
|
106
|
+
self.tracing_metadata: dict[str, str] | None = (
|
|
107
|
+
dict(metadata) if metadata is not None else None
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def copy_with_metadata_defaults(
|
|
111
|
+
self,
|
|
112
|
+
*,
|
|
113
|
+
metadata: Mapping[str, str] | None = None,
|
|
114
|
+
tags: list[str] | None = None,
|
|
115
|
+
) -> CocoTracer:
|
|
116
|
+
"""Return a new tracer with merged tracer-only defaults."""
|
|
117
|
+
base = self.tracing_metadata
|
|
118
|
+
if metadata is None:
|
|
119
|
+
merged = dict(base) if base is not None else None
|
|
120
|
+
elif base is None:
|
|
121
|
+
merged = dict(metadata)
|
|
122
|
+
else:
|
|
123
|
+
merged = dict(base)
|
|
124
|
+
for key, value in metadata.items():
|
|
125
|
+
if key not in merged or key in OVERRIDABLE_LANGSMITH_INHERITABLE_METADATA_KEYS:
|
|
126
|
+
merged[key] = value
|
|
127
|
+
merged_tags = sorted(set(self.tags + tags)) if tags else self.tags
|
|
128
|
+
return self.__class__(
|
|
129
|
+
example_id=self.example_id,
|
|
130
|
+
project_name=self.project_name,
|
|
131
|
+
client=self.client,
|
|
132
|
+
tags=merged_tags,
|
|
133
|
+
metadata=merged,
|
|
134
|
+
run_map=self.run_map,
|
|
135
|
+
order_map=self.order_map,
|
|
136
|
+
_external_run_ids=self._external_run_ids,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def _start_trace(self, run: Run) -> None:
|
|
140
|
+
if self.project_name:
|
|
141
|
+
run.session_name = self.project_name
|
|
142
|
+
if self.tags is not None:
|
|
143
|
+
run.tags = sorted(set(run.tags + self.tags)) if run.tags else self.tags.copy()
|
|
144
|
+
super()._start_trace(run)
|
|
145
|
+
if get_tracing_context().get("enabled") is False:
|
|
146
|
+
run.extra["__disabled"] = True
|
|
147
|
+
|
|
148
|
+
def on_chat_model_start(
|
|
149
|
+
self,
|
|
150
|
+
serialized: dict[str, Any],
|
|
151
|
+
messages: list[list[BaseMessage]],
|
|
152
|
+
*,
|
|
153
|
+
run_id: UUID,
|
|
154
|
+
tags: list[str] | None = None,
|
|
155
|
+
parent_run_id: UUID | None = None,
|
|
156
|
+
metadata: dict[str, Any] | None = None,
|
|
157
|
+
name: str | None = None,
|
|
158
|
+
**kwargs: Any,
|
|
159
|
+
) -> Run:
|
|
160
|
+
"""Start a run for a chat model call, recorded as an LLM run."""
|
|
161
|
+
start_time = datetime.now(timezone.utc)
|
|
162
|
+
if metadata:
|
|
163
|
+
kwargs.update({"metadata": metadata})
|
|
164
|
+
run = Run(
|
|
165
|
+
id=run_id,
|
|
166
|
+
parent_run_id=parent_run_id,
|
|
167
|
+
serialized=serialized,
|
|
168
|
+
inputs={"messages": [[dumpd(msg) for msg in batch] for batch in messages]},
|
|
169
|
+
extra=kwargs,
|
|
170
|
+
events=[{"name": "start", "time": start_time}],
|
|
171
|
+
start_time=start_time,
|
|
172
|
+
run_type="llm",
|
|
173
|
+
tags=tags,
|
|
174
|
+
name=name,
|
|
175
|
+
)
|
|
176
|
+
self._start_trace(run)
|
|
177
|
+
self._on_chat_model_start(run)
|
|
178
|
+
return run
|
|
179
|
+
|
|
180
|
+
def _persist_run(self, run: Run) -> None:
|
|
181
|
+
# Keep the finished root without its whole child tree.
|
|
182
|
+
data = run_to_dict(run, exclude={"child_runs", "inputs", "outputs"})
|
|
183
|
+
self.latest_run = run_construct(**data, inputs=run.inputs, outputs=run.outputs)
|
|
184
|
+
|
|
185
|
+
def get_run_url(self) -> str:
|
|
186
|
+
"""Traces stay in this process, so there is no URL to give.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: Always.
|
|
190
|
+
"""
|
|
191
|
+
msg = "CocoTracer keeps runs in memory; there is no run URL."
|
|
192
|
+
raise ValueError(msg)
|
|
193
|
+
|
|
194
|
+
def _get_tags(self, run: Run) -> list[str]:
|
|
195
|
+
tags = set(run.tags or [])
|
|
196
|
+
tags.update(self.tags or [])
|
|
197
|
+
return list(tags)
|
|
198
|
+
|
|
199
|
+
def _record(self, run: Run) -> None:
|
|
200
|
+
if run.extra.get("__disabled"):
|
|
201
|
+
return
|
|
202
|
+
run.extra["runtime"] = get_runtime_environment()
|
|
203
|
+
run.tags = self._get_tags(run)
|
|
204
|
+
_patch_missing_metadata(self, run)
|
|
205
|
+
|
|
206
|
+
def _on_llm_start(self, run: Run) -> None:
|
|
207
|
+
if run.parent_run_id is None:
|
|
208
|
+
run.reference_example_id = self.example_id
|
|
209
|
+
self._record(run)
|
|
210
|
+
|
|
211
|
+
@override
|
|
212
|
+
def _llm_run_with_token_event(
|
|
213
|
+
self,
|
|
214
|
+
token: str | list[str | dict[str, Any]],
|
|
215
|
+
run_id: UUID,
|
|
216
|
+
chunk: GenerationChunk | ChatGenerationChunk | None = None,
|
|
217
|
+
parent_run_id: UUID | None = None,
|
|
218
|
+
) -> Run:
|
|
219
|
+
run_id_str = str(run_id)
|
|
220
|
+
if run_id_str not in self.run_has_token_event_map:
|
|
221
|
+
self.run_has_token_event_map[run_id_str] = True
|
|
222
|
+
else:
|
|
223
|
+
return self._get_run(run_id, run_type={"llm", "chat_model"})
|
|
224
|
+
return super()._llm_run_with_token_event(
|
|
225
|
+
token, run_id, chunk=None, parent_run_id=parent_run_id
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def _on_chat_model_start(self, run: Run) -> None:
|
|
229
|
+
if run.parent_run_id is None:
|
|
230
|
+
run.reference_example_id = self.example_id
|
|
231
|
+
self._record(run)
|
|
232
|
+
|
|
233
|
+
def _on_llm_end(self, run: Run) -> None:
|
|
234
|
+
if run.outputs and "generations" in run.outputs:
|
|
235
|
+
usage = _get_usage_metadata_from_generations(run.outputs["generations"])
|
|
236
|
+
if usage is not None:
|
|
237
|
+
run.extra.setdefault("metadata", {})["usage_metadata"] = usage
|
|
238
|
+
|
|
239
|
+
def _on_chain_start(self, run: Run) -> None:
|
|
240
|
+
if run.parent_run_id is None:
|
|
241
|
+
run.reference_example_id = self.example_id
|
|
242
|
+
if not run.extra.get("defers_inputs"):
|
|
243
|
+
self._record(run)
|
|
244
|
+
|
|
245
|
+
def _on_chain_end(self, run: Run) -> None:
|
|
246
|
+
if run.extra.get("defers_inputs"):
|
|
247
|
+
self._record(run)
|
|
248
|
+
|
|
249
|
+
def _on_chain_error(self, run: Run) -> None:
|
|
250
|
+
if run.extra.get("defers_inputs"):
|
|
251
|
+
self._record(run)
|
|
252
|
+
|
|
253
|
+
def _on_tool_start(self, run: Run) -> None:
|
|
254
|
+
if run.parent_run_id is None:
|
|
255
|
+
run.reference_example_id = self.example_id
|
|
256
|
+
self._record(run)
|
|
257
|
+
|
|
258
|
+
def _on_retriever_start(self, run: Run) -> None:
|
|
259
|
+
if run.parent_run_id is None:
|
|
260
|
+
run.reference_example_id = self.example_id
|
|
261
|
+
self._record(run)
|
|
262
|
+
|
|
263
|
+
def wait_for_futures(self) -> None:
|
|
264
|
+
"""Nothing is queued, so there is nothing to wait for."""
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _patch_missing_metadata(self: CocoTracer, run: Run) -> None:
|
|
268
|
+
if not self.tracing_metadata:
|
|
269
|
+
return
|
|
270
|
+
metadata = run.metadata
|
|
271
|
+
patched = None
|
|
272
|
+
for k, v in self.tracing_metadata.items():
|
|
273
|
+
if k not in metadata or k in OVERRIDABLE_LANGSMITH_INHERITABLE_METADATA_KEYS:
|
|
274
|
+
if k in metadata and metadata[k] == v:
|
|
275
|
+
continue
|
|
276
|
+
if patched is None:
|
|
277
|
+
patched = {**metadata}
|
|
278
|
+
run.extra["metadata"] = patched
|
|
279
|
+
patched[k] = v
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Context management for tracers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from contextvars import ContextVar
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
Literal,
|
|
11
|
+
cast,
|
|
12
|
+
)
|
|
13
|
+
from uuid import UUID
|
|
14
|
+
|
|
15
|
+
from coco_core.tracers import _context as ls_rh
|
|
16
|
+
from coco_core.tracers import _context as ls_utils
|
|
17
|
+
|
|
18
|
+
from coco_core.tracers.coco import CocoTracer
|
|
19
|
+
from coco_core.tracers.run_collector import RunCollectorCallbackHandler
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Generator
|
|
23
|
+
|
|
24
|
+
from coco_core.tracers._context import Client as LangSmithClient
|
|
25
|
+
|
|
26
|
+
from coco_core.callbacks.base import BaseCallbackHandler, Callbacks
|
|
27
|
+
from coco_core.callbacks.manager import AsyncCallbackManager, CallbackManager
|
|
28
|
+
|
|
29
|
+
# for backwards partial compatibility if this is imported by users but unused
|
|
30
|
+
tracing_callback_var: Any = None
|
|
31
|
+
tracing_v2_callback_var: ContextVar[CocoTracer | None] = ContextVar(
|
|
32
|
+
"tracing_callback_v2", default=None
|
|
33
|
+
)
|
|
34
|
+
run_collector_var: ContextVar[RunCollectorCallbackHandler | None] = ContextVar(
|
|
35
|
+
"run_collector", default=None
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@contextmanager
|
|
40
|
+
def tracing_v2_enabled(
|
|
41
|
+
project_name: str | None = None,
|
|
42
|
+
*,
|
|
43
|
+
example_id: str | UUID | None = None,
|
|
44
|
+
tags: list[str] | None = None,
|
|
45
|
+
client: LangSmithClient | None = None,
|
|
46
|
+
) -> Generator[CocoTracer, None, None]:
|
|
47
|
+
"""Instruct Coco to log all runs in context to LangSmith.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
project_name: The name of the project.
|
|
51
|
+
|
|
52
|
+
Defaults to `'default'`.
|
|
53
|
+
example_id: The ID of the example.
|
|
54
|
+
tags: The tags to add to the run.
|
|
55
|
+
client: The client of the langsmith.
|
|
56
|
+
|
|
57
|
+
Yields:
|
|
58
|
+
The Coco tracer.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> with tracing_v2_enabled():
|
|
62
|
+
... # Coco code will automatically be traced
|
|
63
|
+
|
|
64
|
+
You can use this to fetch the LangSmith run URL:
|
|
65
|
+
|
|
66
|
+
>>> with tracing_v2_enabled() as cb:
|
|
67
|
+
... chain.invoke("foo")
|
|
68
|
+
... run_url = cb.get_run_url()
|
|
69
|
+
"""
|
|
70
|
+
if isinstance(example_id, str):
|
|
71
|
+
example_id = UUID(example_id)
|
|
72
|
+
cb = CocoTracer(
|
|
73
|
+
example_id=example_id,
|
|
74
|
+
project_name=project_name,
|
|
75
|
+
tags=tags,
|
|
76
|
+
client=client,
|
|
77
|
+
)
|
|
78
|
+
token = tracing_v2_callback_var.set(cb)
|
|
79
|
+
try:
|
|
80
|
+
yield cb
|
|
81
|
+
finally:
|
|
82
|
+
tracing_v2_callback_var.reset(token)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@contextmanager
|
|
86
|
+
def collect_runs() -> Generator[RunCollectorCallbackHandler, None, None]:
|
|
87
|
+
"""Collect all run traces in context.
|
|
88
|
+
|
|
89
|
+
Yields:
|
|
90
|
+
The run collector callback handler.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> with collect_runs() as runs_cb:
|
|
94
|
+
chain.invoke("foo")
|
|
95
|
+
run_id = runs_cb.traced_runs[0].id
|
|
96
|
+
"""
|
|
97
|
+
cb = RunCollectorCallbackHandler()
|
|
98
|
+
token = run_collector_var.set(cb)
|
|
99
|
+
try:
|
|
100
|
+
yield cb
|
|
101
|
+
finally:
|
|
102
|
+
run_collector_var.reset(token)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _get_trace_callbacks(
|
|
106
|
+
project_name: str | None = None,
|
|
107
|
+
example_id: str | UUID | None = None,
|
|
108
|
+
callback_manager: CallbackManager | AsyncCallbackManager | None = None,
|
|
109
|
+
) -> Callbacks:
|
|
110
|
+
if _tracing_v2_is_enabled():
|
|
111
|
+
project_name_ = project_name or _get_tracer_project()
|
|
112
|
+
tracer = tracing_v2_callback_var.get() or CocoTracer(
|
|
113
|
+
project_name=project_name_,
|
|
114
|
+
example_id=example_id,
|
|
115
|
+
)
|
|
116
|
+
if callback_manager is None:
|
|
117
|
+
cb = cast("Callbacks", [tracer])
|
|
118
|
+
else:
|
|
119
|
+
if not any(
|
|
120
|
+
isinstance(handler, CocoTracer)
|
|
121
|
+
for handler in callback_manager.handlers
|
|
122
|
+
):
|
|
123
|
+
callback_manager.add_handler(tracer)
|
|
124
|
+
# If it already has a CocoTracer, we don't need to add another one.
|
|
125
|
+
# this would likely mess up the trace hierarchy.
|
|
126
|
+
cb = callback_manager
|
|
127
|
+
else:
|
|
128
|
+
cb = None
|
|
129
|
+
return cb
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _tracing_v2_is_enabled() -> bool | Literal["local"]:
|
|
133
|
+
if tracing_v2_callback_var.get() is not None:
|
|
134
|
+
return True
|
|
135
|
+
return ls_utils.tracing_is_enabled()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _get_tracer_project() -> str:
|
|
139
|
+
tracing_context = ls_rh.get_tracing_context()
|
|
140
|
+
run_tree = tracing_context["parent"]
|
|
141
|
+
if run_tree is None and tracing_context["project_name"] is not None:
|
|
142
|
+
return cast("str", tracing_context["project_name"])
|
|
143
|
+
return getattr(
|
|
144
|
+
run_tree,
|
|
145
|
+
"session_name",
|
|
146
|
+
getattr(
|
|
147
|
+
# Note, if people are trying to nest @traceable functions and the
|
|
148
|
+
# tracing_v2_enabled context manager, this will likely mess up the
|
|
149
|
+
# tree structure.
|
|
150
|
+
tracing_v2_callback_var.get(),
|
|
151
|
+
"project",
|
|
152
|
+
# Have to set this to a string even though it always will return
|
|
153
|
+
# a string because `get_tracer_project` technically can return
|
|
154
|
+
# None, but only when a specific argument is supplied.
|
|
155
|
+
# Therefore, this just tricks the mypy type checker
|
|
156
|
+
str(ls_utils.get_tracer_project()),
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
_configure_hooks: list[
|
|
162
|
+
tuple[
|
|
163
|
+
ContextVar[BaseCallbackHandler | None],
|
|
164
|
+
bool,
|
|
165
|
+
type[BaseCallbackHandler] | None,
|
|
166
|
+
str | None,
|
|
167
|
+
]
|
|
168
|
+
] = []
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def register_configure_hook(
|
|
172
|
+
context_var: ContextVar[Any | None],
|
|
173
|
+
inheritable: bool, # noqa: FBT001
|
|
174
|
+
handle_class: type[BaseCallbackHandler] | None = None,
|
|
175
|
+
env_var: str | None = None,
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Register a configure hook.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
context_var: The context variable.
|
|
181
|
+
inheritable: Whether the context variable is inheritable.
|
|
182
|
+
handle_class: The callback handler class.
|
|
183
|
+
env_var: The environment variable.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If `env_var` is set, `handle_class` must also be set to a non-`None`
|
|
187
|
+
value.
|
|
188
|
+
"""
|
|
189
|
+
if env_var is not None and handle_class is None:
|
|
190
|
+
msg = "If env_var is set, handle_class must also be set to a non-None value."
|
|
191
|
+
raise ValueError(msg)
|
|
192
|
+
|
|
193
|
+
_configure_hooks.append(
|
|
194
|
+
(
|
|
195
|
+
# the typings of ContextVar do not have the generic arg set as covariant
|
|
196
|
+
# so we have to cast it
|
|
197
|
+
cast("ContextVar[BaseCallbackHandler | None]", context_var),
|
|
198
|
+
inheritable,
|
|
199
|
+
handle_class,
|
|
200
|
+
env_var,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
register_configure_hook(run_collector_var, inheritable=False)
|