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,337 @@
|
|
|
1
|
+
"""The tracing context and run record the framework uses in place of a vendor client.
|
|
2
|
+
|
|
3
|
+
Upstream core imported these from its vendor's tracing client, which posts runs to a
|
|
4
|
+
hosted service. The framework keeps the same names and shapes so upstream code runs
|
|
5
|
+
unchanged, and nothing here leaves the process. Coco's record of what an agent did is
|
|
6
|
+
the receipt the SDK writes, not a trace.
|
|
7
|
+
|
|
8
|
+
`RunTree` keeps the fields, methods and printed form the tracers and their snapshot
|
|
9
|
+
tests use, with the same defaults, so a run built here compares equal to the one
|
|
10
|
+
upstream built. `coco_core.tracers.schemas.Run` is the same class.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import functools
|
|
16
|
+
import os
|
|
17
|
+
import secrets
|
|
18
|
+
import time
|
|
19
|
+
from collections.abc import Callable, Generator, Mapping, Sequence
|
|
20
|
+
from contextlib import contextmanager
|
|
21
|
+
from contextvars import ContextVar
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
|
+
from typing import Any, TypeVar, cast
|
|
24
|
+
from uuid import UUID
|
|
25
|
+
|
|
26
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
27
|
+
|
|
28
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
29
|
+
|
|
30
|
+
_PARENT_RUN_TREE: ContextVar[RunTree | None] = ContextVar("_PARENT_RUN_TREE", default=None)
|
|
31
|
+
_PROJECT_NAME: ContextVar[str | None] = ContextVar("_PROJECT_NAME", default=None)
|
|
32
|
+
_TAGS: ContextVar[list[str] | None] = ContextVar("_TAGS", default=None)
|
|
33
|
+
_METADATA: ContextVar[dict[str, Any] | None] = ContextVar("_METADATA", default=None)
|
|
34
|
+
_TRACING_ENABLED: ContextVar[bool | None] = ContextVar("_TRACING_ENABLED", default=None)
|
|
35
|
+
_CLIENT: ContextVar[Any] = ContextVar("_CLIENT", default=None)
|
|
36
|
+
_REPLICAS: ContextVar[Any] = ContextVar("_REPLICAS", default=None)
|
|
37
|
+
_DISTRIBUTED_PARENT_ID: ContextVar[str | None] = ContextVar("_DISTRIBUTED_PARENT_ID", default=None)
|
|
38
|
+
|
|
39
|
+
_CONTEXT_VARS: dict[str, ContextVar[Any]] = {
|
|
40
|
+
"parent": _PARENT_RUN_TREE,
|
|
41
|
+
"project_name": _PROJECT_NAME,
|
|
42
|
+
"tags": _TAGS,
|
|
43
|
+
"metadata": _METADATA,
|
|
44
|
+
"enabled": _TRACING_ENABLED,
|
|
45
|
+
"client": _CLIENT,
|
|
46
|
+
"replicas": _REPLICAS,
|
|
47
|
+
"distributed_parent_id": _DISTRIBUTED_PARENT_ID,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Client:
|
|
52
|
+
"""Stands where the vendor client stood in type hints. Never constructed here."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_tracing_context(context: Any = None) -> dict[str, Any]:
|
|
56
|
+
"""The current tracing context: parent run, project, tags, metadata, enabled."""
|
|
57
|
+
if context is None:
|
|
58
|
+
return {key: var.get() for key, var in _CONTEXT_VARS.items()}
|
|
59
|
+
return {key: context.get(var) for key, var in _CONTEXT_VARS.items()}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _set_tracing_context(context: Mapping[str, Any] | None = None) -> None:
|
|
63
|
+
"""Set the tracing context. `None` clears every key."""
|
|
64
|
+
if context is None:
|
|
65
|
+
for var in _CONTEXT_VARS.values():
|
|
66
|
+
var.set(None)
|
|
67
|
+
return
|
|
68
|
+
for key, value in context.items():
|
|
69
|
+
if key in _CONTEXT_VARS:
|
|
70
|
+
_CONTEXT_VARS[key].set(value)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@contextmanager
|
|
74
|
+
def tracing_context(**kwargs: Any) -> Generator[None, None, None]:
|
|
75
|
+
"""Set tracing context keys for the duration of the block."""
|
|
76
|
+
previous = get_tracing_context()
|
|
77
|
+
_set_tracing_context({k: v for k, v in kwargs.items() if k in _CONTEXT_VARS})
|
|
78
|
+
try:
|
|
79
|
+
yield
|
|
80
|
+
finally:
|
|
81
|
+
_set_tracing_context(previous)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def traceable(*args: Any, **kwargs: Any) -> Any:
|
|
85
|
+
"""Accepts the vendor decorator's arguments and returns the function unchanged."""
|
|
86
|
+
del kwargs
|
|
87
|
+
if len(args) == 1 and callable(args[0]):
|
|
88
|
+
return args[0]
|
|
89
|
+
|
|
90
|
+
def decorator(func: F) -> F:
|
|
91
|
+
return func
|
|
92
|
+
|
|
93
|
+
return decorator
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@functools.lru_cache(maxsize=100)
|
|
97
|
+
def get_env_var(
|
|
98
|
+
name: str,
|
|
99
|
+
default: str | None = None,
|
|
100
|
+
*,
|
|
101
|
+
namespaces: tuple[str, ...] = ("COCO",),
|
|
102
|
+
) -> str | None:
|
|
103
|
+
"""The first of `COCO_<name>` (by default) that is set, else `default`."""
|
|
104
|
+
for namespace in namespaces:
|
|
105
|
+
value = os.environ.get(f"{namespace}_{name}")
|
|
106
|
+
if value is not None:
|
|
107
|
+
return value
|
|
108
|
+
return default
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def tracing_is_enabled(ctx: Mapping[str, Any] | None = None) -> bool:
|
|
112
|
+
"""Whether runs should be kept by a `CocoTracer`. Off unless asked for."""
|
|
113
|
+
context = ctx if ctx is not None else get_tracing_context()
|
|
114
|
+
if context.get("enabled") is not None:
|
|
115
|
+
return bool(context["enabled"])
|
|
116
|
+
return (get_env_var("TRACING_V2") or "").lower() == "true"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_tracer_project(return_default_value: bool = True) -> str | None: # noqa: FBT001, FBT002
|
|
120
|
+
"""The project a run belongs to: `COCO_PROJECT`, else "default"."""
|
|
121
|
+
return os.environ.get("COCO_PROJECT") or ("default" if return_default_value else None)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_coco_env_var_metadata() -> dict[str, str]:
|
|
125
|
+
"""Environment variables the vendor client copied into run metadata. None here."""
|
|
126
|
+
return {}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def uuid7() -> UUID:
|
|
130
|
+
"""A time-ordered UUID (version 7), so runs sort by start."""
|
|
131
|
+
return _uuid7_from_ms(time.time_ns() // 1_000_000)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def uuid7_from_datetime(dt: datetime) -> UUID:
|
|
135
|
+
"""A version 7 UUID whose time part is `dt`."""
|
|
136
|
+
return _uuid7_from_ms(int(dt.timestamp() * 1000))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _uuid7_from_ms(ms: int) -> UUID:
|
|
140
|
+
rand = int.from_bytes(secrets.token_bytes(10), "big")
|
|
141
|
+
value = (ms & ((1 << 48) - 1)) << 80
|
|
142
|
+
value |= 0x7 << 76
|
|
143
|
+
value |= ((rand >> 62) & 0xFFF) << 64
|
|
144
|
+
value |= 0b10 << 62
|
|
145
|
+
value |= rand & ((1 << 62) - 1)
|
|
146
|
+
return UUID(int=value)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _dotted_order(start_time: datetime, run_id: UUID) -> str:
|
|
150
|
+
return start_time.strftime("%Y%m%dT%H%M%S%fZ") + str(run_id)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class RunTree(BaseModel):
|
|
154
|
+
"""One unit of work in a trace: a chain, model call, tool call or retriever call."""
|
|
155
|
+
|
|
156
|
+
id: UUID = Field(default_factory=uuid7)
|
|
157
|
+
name: str
|
|
158
|
+
start_time: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
159
|
+
run_type: str = "chain"
|
|
160
|
+
end_time: datetime | None = None
|
|
161
|
+
extra: dict = Field(default_factory=dict)
|
|
162
|
+
error: str | None = None
|
|
163
|
+
serialized: dict | None = None
|
|
164
|
+
events: list[dict] = Field(default_factory=list)
|
|
165
|
+
inputs: dict = Field(default_factory=dict)
|
|
166
|
+
outputs: dict | None = None
|
|
167
|
+
reference_example_id: UUID | None = None
|
|
168
|
+
parent_run_id: UUID | None = None
|
|
169
|
+
tags: list[str] | None = Field(default_factory=list)
|
|
170
|
+
attachments: dict = Field(default_factory=dict)
|
|
171
|
+
parent_run: RunTree | None = Field(default=None, exclude=True)
|
|
172
|
+
parent_dotted_order: str | None = Field(default=None, exclude=True)
|
|
173
|
+
child_runs: list[RunTree] = Field(default_factory=list, exclude=True)
|
|
174
|
+
session_name: str | None = Field(
|
|
175
|
+
default_factory=lambda: get_tracer_project() or "default", alias="project_name"
|
|
176
|
+
)
|
|
177
|
+
session_id: UUID | None = Field(default=None, alias="project_id")
|
|
178
|
+
ls_client: Any | None = Field(default=None, exclude=True)
|
|
179
|
+
dotted_order: str = ""
|
|
180
|
+
trace_id: UUID = Field(default=cast("UUID", ""))
|
|
181
|
+
|
|
182
|
+
model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True, extra="ignore")
|
|
183
|
+
|
|
184
|
+
@model_validator(mode="before")
|
|
185
|
+
@classmethod
|
|
186
|
+
def infer_defaults(cls, values: Any) -> Any:
|
|
187
|
+
"""Fill the name, id, trace id and empty collections as upstream did."""
|
|
188
|
+
if not isinstance(values, dict):
|
|
189
|
+
return values
|
|
190
|
+
serialized = values.get("serialized")
|
|
191
|
+
if values.get("name") is None and serialized is not None:
|
|
192
|
+
if "name" in serialized:
|
|
193
|
+
values["name"] = serialized["name"]
|
|
194
|
+
elif "id" in serialized:
|
|
195
|
+
values["name"] = serialized["id"][-1]
|
|
196
|
+
if values.get("name") is None:
|
|
197
|
+
values["name"] = "Unnamed"
|
|
198
|
+
if "client" in values:
|
|
199
|
+
values["ls_client"] = values.pop("client")
|
|
200
|
+
elif "_client" in values:
|
|
201
|
+
values["ls_client"] = values.pop("_client")
|
|
202
|
+
parent_run = values.pop("parent_run", None)
|
|
203
|
+
if parent_run is not None:
|
|
204
|
+
values["parent_run_id"] = parent_run.id
|
|
205
|
+
values["parent_dotted_order"] = parent_run.dotted_order
|
|
206
|
+
if "id" not in values:
|
|
207
|
+
if values.get("start_time") is not None:
|
|
208
|
+
values["id"] = uuid7_from_datetime(values["start_time"])
|
|
209
|
+
else:
|
|
210
|
+
now = datetime.now(timezone.utc)
|
|
211
|
+
values["start_time"] = now
|
|
212
|
+
values["id"] = uuid7_from_datetime(now)
|
|
213
|
+
if "trace_id" not in values:
|
|
214
|
+
values["trace_id"] = parent_run.trace_id if parent_run is not None else values["id"]
|
|
215
|
+
values.setdefault("extra", {})
|
|
216
|
+
for key in ("events", "tags"):
|
|
217
|
+
if values.get(key) is None:
|
|
218
|
+
values[key] = []
|
|
219
|
+
if values.get("outputs") is None:
|
|
220
|
+
values["outputs"] = {}
|
|
221
|
+
for key in ("inputs", "outputs"):
|
|
222
|
+
value = values.get(key)
|
|
223
|
+
if value is not None and not isinstance(value, dict):
|
|
224
|
+
values[key] = {"output": value}
|
|
225
|
+
if values.get("attachments") is None:
|
|
226
|
+
values["attachments"] = {}
|
|
227
|
+
return values
|
|
228
|
+
|
|
229
|
+
@model_validator(mode="after")
|
|
230
|
+
def ensure_dotted_order(self) -> RunTree:
|
|
231
|
+
"""Give the run its place in the tree when nothing set one."""
|
|
232
|
+
if self.dotted_order and self.dotted_order.strip():
|
|
233
|
+
return self
|
|
234
|
+
current = _dotted_order(self.start_time, self.id)
|
|
235
|
+
if self.parent_dotted_order is not None:
|
|
236
|
+
self.dotted_order = self.parent_dotted_order + "." + current
|
|
237
|
+
else:
|
|
238
|
+
self.dotted_order = current
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def client(self) -> Any:
|
|
243
|
+
"""Whatever the caller attached. Nothing is created here."""
|
|
244
|
+
return self.ls_client
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def metadata(self) -> dict[str, Any]:
|
|
248
|
+
"""The run's metadata, stored under `extra["metadata"]`."""
|
|
249
|
+
if self.extra is None:
|
|
250
|
+
self.extra = {}
|
|
251
|
+
return self.extra.setdefault("metadata", {})
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def revision_id(self) -> UUID | None:
|
|
255
|
+
"""The revision id from metadata, if any."""
|
|
256
|
+
return self.metadata.get("revision_id")
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def latency(self) -> float | None:
|
|
260
|
+
"""Seconds from start to end, once ended."""
|
|
261
|
+
if self.end_time is None:
|
|
262
|
+
return None
|
|
263
|
+
return (self.end_time - self.start_time).total_seconds()
|
|
264
|
+
|
|
265
|
+
def add_tags(self, tags: Sequence[str] | str) -> None:
|
|
266
|
+
"""Add tags to the run."""
|
|
267
|
+
if isinstance(tags, str):
|
|
268
|
+
tags = [tags]
|
|
269
|
+
if self.tags is None:
|
|
270
|
+
self.tags = []
|
|
271
|
+
self.tags.extend(tags)
|
|
272
|
+
|
|
273
|
+
def add_metadata(self, metadata: dict[str, Any]) -> None:
|
|
274
|
+
"""Merge metadata into the run."""
|
|
275
|
+
self.metadata.update(metadata)
|
|
276
|
+
|
|
277
|
+
def add_outputs(self, outputs: dict[str, Any]) -> None:
|
|
278
|
+
"""Merge outputs into the run."""
|
|
279
|
+
if self.outputs is None:
|
|
280
|
+
self.outputs = {}
|
|
281
|
+
self.outputs.update(outputs)
|
|
282
|
+
|
|
283
|
+
def add_inputs(self, inputs: dict[str, Any]) -> None:
|
|
284
|
+
"""Merge inputs into the run."""
|
|
285
|
+
if self.inputs is None:
|
|
286
|
+
self.inputs = {}
|
|
287
|
+
self.inputs.update(inputs)
|
|
288
|
+
|
|
289
|
+
def add_event(self, events: Sequence[dict] | dict | str) -> None:
|
|
290
|
+
"""Append one event, several, or a message recorded as an event."""
|
|
291
|
+
if self.events is None:
|
|
292
|
+
self.events = []
|
|
293
|
+
if isinstance(events, dict):
|
|
294
|
+
self.events.append(events)
|
|
295
|
+
elif isinstance(events, str):
|
|
296
|
+
self.events.append(
|
|
297
|
+
{"name": "event", "time": datetime.now(timezone.utc).isoformat(), "message": events}
|
|
298
|
+
)
|
|
299
|
+
else:
|
|
300
|
+
self.events.extend(events)
|
|
301
|
+
|
|
302
|
+
def end(
|
|
303
|
+
self,
|
|
304
|
+
*,
|
|
305
|
+
outputs: dict | None = None,
|
|
306
|
+
error: str | None = None,
|
|
307
|
+
end_time: datetime | None = None,
|
|
308
|
+
events: Sequence[dict] | None = None,
|
|
309
|
+
metadata: dict[str, Any] | None = None,
|
|
310
|
+
) -> None:
|
|
311
|
+
"""Close the run."""
|
|
312
|
+
self.end_time = end_time or datetime.now(timezone.utc)
|
|
313
|
+
if outputs is not None:
|
|
314
|
+
self.add_outputs(outputs)
|
|
315
|
+
if error is not None:
|
|
316
|
+
self.error = error
|
|
317
|
+
if events is not None:
|
|
318
|
+
self.add_event(list(events))
|
|
319
|
+
if metadata is not None:
|
|
320
|
+
self.add_metadata(metadata)
|
|
321
|
+
|
|
322
|
+
def create_child(self, name: str, run_type: str = "chain", **kwargs: Any) -> RunTree:
|
|
323
|
+
"""A child run under this one."""
|
|
324
|
+
child = RunTree(name=name, run_type=run_type, parent_run=self, **kwargs)
|
|
325
|
+
self.child_runs.append(child)
|
|
326
|
+
return child
|
|
327
|
+
|
|
328
|
+
def __repr__(self) -> str:
|
|
329
|
+
return (
|
|
330
|
+
f"RunTree(id={self.id}, name='{self.name}', "
|
|
331
|
+
f"run_type='{self.run_type}', dotted_order='{self.dotted_order}')"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
RunTree.model_rebuild()
|
|
336
|
+
|
|
337
|
+
Run = RunTree
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Internal tracers used for `stream_log` and `astream` events implementations."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from collections.abc import AsyncIterator, Iterator
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
T = typing.TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# THIS IS USED IN COCO_GRAPH.
|
|
11
|
+
@typing.runtime_checkable
|
|
12
|
+
class _StreamingCallbackHandler(typing.Protocol[T]):
|
|
13
|
+
"""Types for streaming callback handlers.
|
|
14
|
+
|
|
15
|
+
This is a common mixin that the callback handlers for both astream events and
|
|
16
|
+
astream log inherit from.
|
|
17
|
+
|
|
18
|
+
The `tap_output_aiter` method is invoked in some contexts to produce callbacks for
|
|
19
|
+
intermediate results.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def tap_output_aiter(
|
|
23
|
+
self, run_id: UUID, output: AsyncIterator[T]
|
|
24
|
+
) -> AsyncIterator[T]:
|
|
25
|
+
"""Used for internal astream_log and astream events implementations."""
|
|
26
|
+
|
|
27
|
+
def tap_output_iter(self, run_id: UUID, output: Iterator[T]) -> Iterator[T]:
|
|
28
|
+
"""Used for internal astream_log and astream events implementations."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# THIS IS USED IN COCO_GRAPH.
|
|
32
|
+
class _V2StreamingCallbackHandler:
|
|
33
|
+
"""Marker base class for handlers that consume `on_stream_event` (v2).
|
|
34
|
+
|
|
35
|
+
A handler inheriting from this class signals that it wants content-
|
|
36
|
+
block lifecycle events from `stream_events(version="v3")` (and its
|
|
37
|
+
async equivalent) rather than the v1 `on_llm_new_token` chunks.
|
|
38
|
+
`BaseChatModel.invoke` uses
|
|
39
|
+
`isinstance(handler, _V2StreamingCallbackHandler)` to decide whether
|
|
40
|
+
to route an invoke through the v2 event generator.
|
|
41
|
+
|
|
42
|
+
Implemented as a concrete marker class (not a `Protocol`) so opt-in
|
|
43
|
+
is explicit via inheritance. An empty `runtime_checkable` Protocol
|
|
44
|
+
would match every object and misroute every call. The event
|
|
45
|
+
delivery contract itself lives on
|
|
46
|
+
`BaseCallbackHandler.on_stream_event`.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"_StreamingCallbackHandler",
|
|
52
|
+
"_V2StreamingCallbackHandler",
|
|
53
|
+
]
|