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,712 @@
|
|
|
1
|
+
"""Configuration utilities for `Runnable` objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
# Cannot move uuid to TYPE_CHECKING as RunnableConfig is used in Pydantic models
|
|
8
|
+
import uuid # noqa: TC003
|
|
9
|
+
import warnings
|
|
10
|
+
from collections.abc import (
|
|
11
|
+
Awaitable,
|
|
12
|
+
Callable,
|
|
13
|
+
Generator,
|
|
14
|
+
Iterable,
|
|
15
|
+
Iterator,
|
|
16
|
+
Mapping,
|
|
17
|
+
Sequence,
|
|
18
|
+
)
|
|
19
|
+
from concurrent.futures import Executor, Future, ThreadPoolExecutor
|
|
20
|
+
from contextlib import contextmanager
|
|
21
|
+
from contextvars import Context, ContextVar, Token, copy_context
|
|
22
|
+
from functools import partial
|
|
23
|
+
from typing import (
|
|
24
|
+
TYPE_CHECKING,
|
|
25
|
+
Any,
|
|
26
|
+
ParamSpec,
|
|
27
|
+
TypeVar,
|
|
28
|
+
cast,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from typing_extensions import TypedDict
|
|
32
|
+
|
|
33
|
+
from coco_core.callbacks.manager import AsyncCallbackManager, CallbackManager
|
|
34
|
+
from coco_core.runnables.utils import (
|
|
35
|
+
Input,
|
|
36
|
+
Output,
|
|
37
|
+
accepts_config,
|
|
38
|
+
accepts_run_manager,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from coco_core.callbacks.base import BaseCallbackManager, Callbacks
|
|
43
|
+
from coco_core.callbacks.manager import (
|
|
44
|
+
AsyncCallbackManagerForChainRun,
|
|
45
|
+
CallbackManagerForChainRun,
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
# Pydantic validates through typed dicts, but
|
|
49
|
+
# the callbacks need forward refs updated
|
|
50
|
+
Callbacks = list | Any | None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class EmptyDict(TypedDict, total=False):
|
|
54
|
+
"""Empty dict type."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class RunnableConfig(TypedDict, total=False):
|
|
58
|
+
"""Configuration for a `Runnable`.
|
|
59
|
+
|
|
60
|
+
!!! note Custom values
|
|
61
|
+
|
|
62
|
+
The `TypedDict` has `total=False` set intentionally to:
|
|
63
|
+
|
|
64
|
+
- Allow partial configs to be created and merged together via `merge_configs`
|
|
65
|
+
- Support config propagation from parent to child runnables via
|
|
66
|
+
`var_child_runnable_config` (a `ContextVar` that automatically passes
|
|
67
|
+
config down the call stack without explicit parameter passing), where
|
|
68
|
+
configs are merged rather than replaced
|
|
69
|
+
|
|
70
|
+
!!! example
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# Parent sets tags
|
|
74
|
+
chain.invoke(input, config={"tags": ["parent"]})
|
|
75
|
+
# Child automatically inherits and can add:
|
|
76
|
+
# ensure_config({"tags": ["child"]}) -> {"tags": ["parent", "child"]}
|
|
77
|
+
```
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
tags: list[str]
|
|
81
|
+
"""Tags for this call and any sub-calls (e.g. a Chain calling an LLM).
|
|
82
|
+
|
|
83
|
+
You can use these to filter calls.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
metadata: dict[str, Any]
|
|
87
|
+
"""Metadata for this call and any sub-calls (e.g. a Chain calling an LLM).
|
|
88
|
+
|
|
89
|
+
Keys should be strings, values should be JSON-serializable.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
callbacks: Callbacks
|
|
93
|
+
"""Callbacks for this call and any sub-calls (e.g. a Chain calling an LLM).
|
|
94
|
+
|
|
95
|
+
Tags are passed to all callbacks, metadata is passed to handle*Start callbacks.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
run_name: str
|
|
99
|
+
"""Name for the tracer run for this call.
|
|
100
|
+
|
|
101
|
+
Defaults to the name of the class."""
|
|
102
|
+
|
|
103
|
+
max_concurrency: int | None
|
|
104
|
+
"""Maximum number of parallel calls to make.
|
|
105
|
+
|
|
106
|
+
If not provided, defaults to `ThreadPoolExecutor`'s default.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
recursion_limit: int
|
|
110
|
+
"""Maximum number of times a call can recurse.
|
|
111
|
+
|
|
112
|
+
If not provided, defaults to `25`.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
configurable: dict[str, Any]
|
|
116
|
+
"""Runtime values for attributes previously made configurable on this `Runnable`,
|
|
117
|
+
or sub-`Runnable` objects, through `configurable_fields` or
|
|
118
|
+
`configurable_alternatives`.
|
|
119
|
+
|
|
120
|
+
Check `output_schema` for a description of the attributes that have been made
|
|
121
|
+
configurable.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
run_id: uuid.UUID | None
|
|
125
|
+
"""Unique identifier for the tracer run for this call.
|
|
126
|
+
|
|
127
|
+
If not provided, a new UUID will be generated.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
CONFIG_KEYS = [
|
|
132
|
+
"tags",
|
|
133
|
+
"metadata",
|
|
134
|
+
"callbacks",
|
|
135
|
+
"run_name",
|
|
136
|
+
"max_concurrency",
|
|
137
|
+
"recursion_limit",
|
|
138
|
+
"configurable",
|
|
139
|
+
"run_id",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
COPIABLE_KEYS = [
|
|
143
|
+
"tags",
|
|
144
|
+
"metadata",
|
|
145
|
+
"callbacks",
|
|
146
|
+
"configurable",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# Users are expected to use the `context` API with a context object
|
|
151
|
+
# (which does not get traced)
|
|
152
|
+
CONFIGURABLE_TO_TRACING_METADATA_EXCLUDED_KEYS = frozenset(("api_key",))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _get_langsmith_inheritable_metadata_from_config(
|
|
156
|
+
config: RunnableConfig,
|
|
157
|
+
) -> dict[str, Any] | None:
|
|
158
|
+
"""Get LangSmith-only inheritable metadata defaults derived from config."""
|
|
159
|
+
configurable = config.get("configurable") or {}
|
|
160
|
+
metadata = {
|
|
161
|
+
key: value
|
|
162
|
+
for key, value in configurable.items()
|
|
163
|
+
if not key.startswith("__")
|
|
164
|
+
and isinstance(value, (str, int, float, bool))
|
|
165
|
+
and key not in config.get("metadata", {})
|
|
166
|
+
and key not in CONFIGURABLE_TO_TRACING_METADATA_EXCLUDED_KEYS
|
|
167
|
+
}
|
|
168
|
+
return metadata or None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
DEFAULT_RECURSION_LIMIT = 25
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
var_child_runnable_config: ContextVar[RunnableConfig | None] = ContextVar(
|
|
175
|
+
"child_runnable_config", default=None
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# This is imported and used in coco_graph, so don't break.
|
|
180
|
+
def _set_config_context(
|
|
181
|
+
config: RunnableConfig,
|
|
182
|
+
) -> tuple[Token[RunnableConfig | None], dict[str, Any] | None]:
|
|
183
|
+
"""Set the child Runnable config + tracing context.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
config: The config to set.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The token to reset the config and the previous tracing context.
|
|
190
|
+
"""
|
|
191
|
+
# Deferred to avoid importing langsmith at module level (~132ms).
|
|
192
|
+
from coco_core.tracers._context import ( # noqa: PLC0415
|
|
193
|
+
_set_tracing_context,
|
|
194
|
+
get_tracing_context,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
from coco_core.tracers.coco import CocoTracer # noqa: PLC0415
|
|
198
|
+
|
|
199
|
+
config_token = var_child_runnable_config.set(config)
|
|
200
|
+
current_context = None
|
|
201
|
+
if (
|
|
202
|
+
(callbacks := config.get("callbacks"))
|
|
203
|
+
and (
|
|
204
|
+
parent_run_id := getattr(callbacks, "parent_run_id", None)
|
|
205
|
+
) # Is callback manager
|
|
206
|
+
and (
|
|
207
|
+
tracer := next(
|
|
208
|
+
(
|
|
209
|
+
handler
|
|
210
|
+
for handler in getattr(callbacks, "handlers", [])
|
|
211
|
+
if isinstance(handler, CocoTracer)
|
|
212
|
+
),
|
|
213
|
+
None,
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
and (run := tracer.run_map.get(str(parent_run_id)))
|
|
217
|
+
):
|
|
218
|
+
current_context = get_tracing_context()
|
|
219
|
+
_set_tracing_context({"parent": run})
|
|
220
|
+
return config_token, current_context
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@contextmanager
|
|
224
|
+
def set_config_context(config: RunnableConfig) -> Generator[Context, None, None]:
|
|
225
|
+
"""Set the child Runnable config + tracing context.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
config: The config to set.
|
|
229
|
+
|
|
230
|
+
Yields:
|
|
231
|
+
The config context.
|
|
232
|
+
"""
|
|
233
|
+
# Deferred to avoid importing langsmith at module level (~132ms).
|
|
234
|
+
from coco_core.tracers._context import _set_tracing_context # noqa: PLC0415
|
|
235
|
+
|
|
236
|
+
ctx = copy_context()
|
|
237
|
+
config_token, _ = ctx.run(_set_config_context, config)
|
|
238
|
+
try:
|
|
239
|
+
yield ctx
|
|
240
|
+
finally:
|
|
241
|
+
ctx.run(var_child_runnable_config.reset, config_token)
|
|
242
|
+
ctx.run(
|
|
243
|
+
_set_tracing_context,
|
|
244
|
+
{
|
|
245
|
+
"parent": None,
|
|
246
|
+
"project_name": None,
|
|
247
|
+
"tags": None,
|
|
248
|
+
"metadata": None,
|
|
249
|
+
"enabled": None,
|
|
250
|
+
"client": None,
|
|
251
|
+
},
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def ensure_config(config: RunnableConfig | None = None) -> RunnableConfig:
|
|
256
|
+
"""Ensure that a config is a dict with all keys present.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
config: The config to ensure.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
The ensured config.
|
|
263
|
+
"""
|
|
264
|
+
empty = RunnableConfig(
|
|
265
|
+
tags=[],
|
|
266
|
+
metadata={},
|
|
267
|
+
callbacks=None,
|
|
268
|
+
recursion_limit=DEFAULT_RECURSION_LIMIT,
|
|
269
|
+
configurable={},
|
|
270
|
+
)
|
|
271
|
+
if var_config := var_child_runnable_config.get():
|
|
272
|
+
empty.update(
|
|
273
|
+
cast(
|
|
274
|
+
"RunnableConfig",
|
|
275
|
+
{
|
|
276
|
+
k: v.copy() if k in COPIABLE_KEYS else v # type: ignore[attr-defined]
|
|
277
|
+
for k, v in var_config.items()
|
|
278
|
+
if v is not None
|
|
279
|
+
},
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
if config is not None:
|
|
283
|
+
empty.update(
|
|
284
|
+
cast(
|
|
285
|
+
"RunnableConfig",
|
|
286
|
+
{
|
|
287
|
+
k: v.copy() if k in COPIABLE_KEYS else v # type: ignore[attr-defined]
|
|
288
|
+
for k, v in config.items()
|
|
289
|
+
if v is not None and k in CONFIG_KEYS
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
)
|
|
293
|
+
if config is not None:
|
|
294
|
+
for k, v in config.items():
|
|
295
|
+
if k not in CONFIG_KEYS and v is not None:
|
|
296
|
+
empty["configurable"][k] = v
|
|
297
|
+
for configurable_key in ("model", "checkpoint_ns"):
|
|
298
|
+
if (
|
|
299
|
+
isinstance(
|
|
300
|
+
configurable_value := empty.get("configurable", {}).get(
|
|
301
|
+
configurable_key
|
|
302
|
+
),
|
|
303
|
+
str,
|
|
304
|
+
)
|
|
305
|
+
and configurable_key not in empty["metadata"]
|
|
306
|
+
):
|
|
307
|
+
empty["metadata"][configurable_key] = configurable_value
|
|
308
|
+
return empty
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def get_config_list(
|
|
312
|
+
config: RunnableConfig | Sequence[RunnableConfig] | None, length: int
|
|
313
|
+
) -> list[RunnableConfig]:
|
|
314
|
+
"""Get a list of configs from a single config or a list of configs.
|
|
315
|
+
|
|
316
|
+
It is useful for subclasses overriding batch() or abatch().
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
config: The config or list of configs.
|
|
320
|
+
length: The length of the list.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
The list of configs.
|
|
324
|
+
|
|
325
|
+
Raises:
|
|
326
|
+
ValueError: If the length of the list is not equal to the length of the inputs.
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
if length < 0:
|
|
330
|
+
msg = f"length must be >= 0, but got {length}"
|
|
331
|
+
raise ValueError(msg)
|
|
332
|
+
if isinstance(config, Sequence) and len(config) != length:
|
|
333
|
+
msg = (
|
|
334
|
+
f"config must be a list of the same length as inputs, "
|
|
335
|
+
f"but got {len(config)} configs for {length} inputs"
|
|
336
|
+
)
|
|
337
|
+
raise ValueError(msg)
|
|
338
|
+
|
|
339
|
+
if isinstance(config, Sequence):
|
|
340
|
+
return list(map(ensure_config, config))
|
|
341
|
+
if length > 1 and isinstance(config, dict) and config.get("run_id") is not None:
|
|
342
|
+
warnings.warn(
|
|
343
|
+
"Provided run_id be used only for the first element of the batch.",
|
|
344
|
+
category=RuntimeWarning,
|
|
345
|
+
stacklevel=3,
|
|
346
|
+
)
|
|
347
|
+
subsequent = cast(
|
|
348
|
+
"RunnableConfig", {k: v for k, v in config.items() if k != "run_id"}
|
|
349
|
+
)
|
|
350
|
+
return [
|
|
351
|
+
ensure_config(subsequent) if i else ensure_config(config)
|
|
352
|
+
for i in range(length)
|
|
353
|
+
]
|
|
354
|
+
return [ensure_config(config) for i in range(length)]
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def patch_config(
|
|
358
|
+
config: RunnableConfig | None,
|
|
359
|
+
*,
|
|
360
|
+
callbacks: BaseCallbackManager | None = None,
|
|
361
|
+
recursion_limit: int | None = None,
|
|
362
|
+
max_concurrency: int | None = None,
|
|
363
|
+
run_name: str | None = None,
|
|
364
|
+
configurable: dict[str, Any] | None = None,
|
|
365
|
+
) -> RunnableConfig:
|
|
366
|
+
"""Patch a config with new values.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
config: The config to patch.
|
|
370
|
+
callbacks: The callbacks to set.
|
|
371
|
+
recursion_limit: The recursion limit to set.
|
|
372
|
+
max_concurrency: The max concurrency to set.
|
|
373
|
+
run_name: The run name to set.
|
|
374
|
+
configurable: The configurable to set.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
The patched config.
|
|
378
|
+
"""
|
|
379
|
+
config = ensure_config(config)
|
|
380
|
+
if callbacks is not None:
|
|
381
|
+
# If we're replacing callbacks, we need to unset run_name
|
|
382
|
+
# As that should apply only to the same run as the original callbacks
|
|
383
|
+
config["callbacks"] = callbacks
|
|
384
|
+
if "run_name" in config:
|
|
385
|
+
del config["run_name"]
|
|
386
|
+
if "run_id" in config:
|
|
387
|
+
del config["run_id"]
|
|
388
|
+
if recursion_limit is not None:
|
|
389
|
+
config["recursion_limit"] = recursion_limit
|
|
390
|
+
if max_concurrency is not None:
|
|
391
|
+
config["max_concurrency"] = max_concurrency
|
|
392
|
+
if run_name is not None:
|
|
393
|
+
config["run_name"] = run_name
|
|
394
|
+
if configurable is not None:
|
|
395
|
+
config["configurable"] = {**config.get("configurable", {}), **configurable}
|
|
396
|
+
return config
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _merge_metadata_dicts(
|
|
400
|
+
base: Mapping[str, Any], incoming: Mapping[str, Any]
|
|
401
|
+
) -> dict[str, Any]:
|
|
402
|
+
"""Merge metadata dicts, accumulating only `lc_versions` nested mappings.
|
|
403
|
+
|
|
404
|
+
Metadata generally uses last-writer-wins semantics. The Coco-owned
|
|
405
|
+
`lc_versions` key is the only nested mapping that accumulates across merges
|
|
406
|
+
so package versions from core, `coco`, and partner packages coexist.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
base: The base metadata dict.
|
|
410
|
+
|
|
411
|
+
Values here are kept unless overridden by `incoming`.
|
|
412
|
+
incoming: The metadata dict to merge on top.
|
|
413
|
+
|
|
414
|
+
Its values take precedence on conflict.
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
A new merged dict.
|
|
418
|
+
"""
|
|
419
|
+
merged = {**base, **incoming}
|
|
420
|
+
base_versions = base.get("lc_versions")
|
|
421
|
+
incoming_versions = incoming.get("lc_versions")
|
|
422
|
+
if isinstance(base_versions, Mapping) and isinstance(incoming_versions, Mapping):
|
|
423
|
+
merged["lc_versions"] = {**base_versions, **incoming_versions}
|
|
424
|
+
elif isinstance(incoming_versions, Mapping):
|
|
425
|
+
merged["lc_versions"] = {**incoming_versions}
|
|
426
|
+
elif "lc_versions" not in incoming and isinstance(base_versions, Mapping):
|
|
427
|
+
merged["lc_versions"] = {**base_versions}
|
|
428
|
+
return merged
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def merge_configs(*configs: RunnableConfig | None) -> RunnableConfig:
|
|
432
|
+
"""Merge multiple configs into one.
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
*configs: The configs to merge.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
The merged config.
|
|
439
|
+
"""
|
|
440
|
+
base: RunnableConfig = {}
|
|
441
|
+
# Even though the keys aren't literals, this is correct
|
|
442
|
+
# because both dicts are the same type
|
|
443
|
+
for config in (ensure_config(c) for c in configs if c is not None):
|
|
444
|
+
for key in config:
|
|
445
|
+
if key == "metadata":
|
|
446
|
+
base["metadata"] = _merge_metadata_dicts(
|
|
447
|
+
base.get("metadata", {}),
|
|
448
|
+
config.get("metadata") or {},
|
|
449
|
+
)
|
|
450
|
+
elif key == "tags":
|
|
451
|
+
base["tags"] = sorted(
|
|
452
|
+
set(base.get("tags", []) + (config.get("tags") or [])),
|
|
453
|
+
)
|
|
454
|
+
elif key == "configurable":
|
|
455
|
+
base["configurable"] = {
|
|
456
|
+
**base.get("configurable", {}),
|
|
457
|
+
**(config.get("configurable") or {}),
|
|
458
|
+
}
|
|
459
|
+
elif key == "callbacks":
|
|
460
|
+
base_callbacks = base.get("callbacks")
|
|
461
|
+
these_callbacks = config["callbacks"]
|
|
462
|
+
# callbacks can be either None, list[handler] or manager
|
|
463
|
+
# so merging two callbacks values has 6 cases
|
|
464
|
+
if isinstance(these_callbacks, list):
|
|
465
|
+
if base_callbacks is None:
|
|
466
|
+
base["callbacks"] = these_callbacks.copy()
|
|
467
|
+
elif isinstance(base_callbacks, list):
|
|
468
|
+
base["callbacks"] = base_callbacks + these_callbacks
|
|
469
|
+
else:
|
|
470
|
+
# base_callbacks is a manager
|
|
471
|
+
mngr = base_callbacks.copy()
|
|
472
|
+
for callback in these_callbacks:
|
|
473
|
+
mngr.add_handler(callback, inherit=True)
|
|
474
|
+
base["callbacks"] = mngr
|
|
475
|
+
elif these_callbacks is not None:
|
|
476
|
+
# these_callbacks is a manager
|
|
477
|
+
if base_callbacks is None:
|
|
478
|
+
base["callbacks"] = these_callbacks.copy()
|
|
479
|
+
elif isinstance(base_callbacks, list):
|
|
480
|
+
mngr = these_callbacks.copy()
|
|
481
|
+
for callback in base_callbacks:
|
|
482
|
+
mngr.add_handler(callback, inherit=True)
|
|
483
|
+
base["callbacks"] = mngr
|
|
484
|
+
else:
|
|
485
|
+
# base_callbacks is also a manager
|
|
486
|
+
base["callbacks"] = base_callbacks.merge(these_callbacks)
|
|
487
|
+
elif key == "recursion_limit":
|
|
488
|
+
if config["recursion_limit"] != DEFAULT_RECURSION_LIMIT:
|
|
489
|
+
base["recursion_limit"] = config["recursion_limit"]
|
|
490
|
+
elif key in COPIABLE_KEYS and config[key] is not None: # type: ignore[literal-required]
|
|
491
|
+
base[key] = config[key].copy() # type: ignore[literal-required]
|
|
492
|
+
else:
|
|
493
|
+
base[key] = config[key] or base.get(key) # type: ignore[literal-required]
|
|
494
|
+
return base
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def call_func_with_variable_args(
|
|
498
|
+
func: Callable[[Input], Output]
|
|
499
|
+
| Callable[[Input, RunnableConfig], Output]
|
|
500
|
+
| Callable[[Input, CallbackManagerForChainRun], Output]
|
|
501
|
+
| Callable[[Input, CallbackManagerForChainRun, RunnableConfig], Output],
|
|
502
|
+
input: Input,
|
|
503
|
+
config: RunnableConfig,
|
|
504
|
+
run_manager: CallbackManagerForChainRun | None = None,
|
|
505
|
+
**kwargs: Any,
|
|
506
|
+
) -> Output:
|
|
507
|
+
"""Call function that may optionally accept a run_manager and/or config.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
func: The function to call.
|
|
511
|
+
input: The input to the function.
|
|
512
|
+
config: The config to pass to the function.
|
|
513
|
+
run_manager: The run manager to pass to the function.
|
|
514
|
+
**kwargs: The keyword arguments to pass to the function.
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
The output of the function.
|
|
518
|
+
"""
|
|
519
|
+
if accepts_config(func):
|
|
520
|
+
if run_manager is not None:
|
|
521
|
+
kwargs["config"] = patch_config(config, callbacks=run_manager.get_child())
|
|
522
|
+
else:
|
|
523
|
+
kwargs["config"] = config
|
|
524
|
+
if run_manager is not None and accepts_run_manager(func):
|
|
525
|
+
kwargs["run_manager"] = run_manager
|
|
526
|
+
return func(input, **kwargs) # type: ignore[call-arg]
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def acall_func_with_variable_args(
|
|
530
|
+
func: Callable[[Input], Awaitable[Output]]
|
|
531
|
+
| Callable[[Input, RunnableConfig], Awaitable[Output]]
|
|
532
|
+
| Callable[[Input, AsyncCallbackManagerForChainRun], Awaitable[Output]]
|
|
533
|
+
| Callable[
|
|
534
|
+
[Input, AsyncCallbackManagerForChainRun, RunnableConfig], Awaitable[Output]
|
|
535
|
+
],
|
|
536
|
+
input: Input,
|
|
537
|
+
config: RunnableConfig,
|
|
538
|
+
run_manager: AsyncCallbackManagerForChainRun | None = None,
|
|
539
|
+
**kwargs: Any,
|
|
540
|
+
) -> Awaitable[Output]:
|
|
541
|
+
"""Async call function that may optionally accept a run_manager and/or config.
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
func: The function to call.
|
|
545
|
+
input: The input to the function.
|
|
546
|
+
config: The config to pass to the function.
|
|
547
|
+
run_manager: The run manager to pass to the function.
|
|
548
|
+
**kwargs: The keyword arguments to pass to the function.
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
The output of the function.
|
|
552
|
+
"""
|
|
553
|
+
if accepts_config(func):
|
|
554
|
+
if run_manager is not None:
|
|
555
|
+
kwargs["config"] = patch_config(config, callbacks=run_manager.get_child())
|
|
556
|
+
else:
|
|
557
|
+
kwargs["config"] = config
|
|
558
|
+
if run_manager is not None and accepts_run_manager(func):
|
|
559
|
+
kwargs["run_manager"] = run_manager
|
|
560
|
+
return func(input, **kwargs) # type: ignore[call-arg]
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def get_callback_manager_for_config(config: RunnableConfig) -> CallbackManager:
|
|
564
|
+
"""Get a callback manager for a config.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
config: The config.
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
The callback manager.
|
|
571
|
+
"""
|
|
572
|
+
return CallbackManager.configure(
|
|
573
|
+
inheritable_callbacks=config.get("callbacks"),
|
|
574
|
+
inheritable_tags=config.get("tags"),
|
|
575
|
+
inheritable_metadata=config.get("metadata"),
|
|
576
|
+
langsmith_inheritable_metadata=_get_langsmith_inheritable_metadata_from_config(
|
|
577
|
+
config
|
|
578
|
+
),
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def get_async_callback_manager_for_config(
|
|
583
|
+
config: RunnableConfig,
|
|
584
|
+
) -> AsyncCallbackManager:
|
|
585
|
+
"""Get an async callback manager for a config.
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
config: The config.
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
The async callback manager.
|
|
592
|
+
"""
|
|
593
|
+
return AsyncCallbackManager.configure(
|
|
594
|
+
inheritable_callbacks=config.get("callbacks"),
|
|
595
|
+
inheritable_tags=config.get("tags"),
|
|
596
|
+
inheritable_metadata=config.get("metadata"),
|
|
597
|
+
langsmith_inheritable_metadata=_get_langsmith_inheritable_metadata_from_config(
|
|
598
|
+
config
|
|
599
|
+
),
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
P = ParamSpec("P")
|
|
604
|
+
T = TypeVar("T")
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
class ContextThreadPoolExecutor(ThreadPoolExecutor):
|
|
608
|
+
"""ThreadPoolExecutor that copies the context to the child thread."""
|
|
609
|
+
|
|
610
|
+
def submit( # type: ignore[override]
|
|
611
|
+
self,
|
|
612
|
+
func: Callable[P, T],
|
|
613
|
+
*args: P.args,
|
|
614
|
+
**kwargs: P.kwargs,
|
|
615
|
+
) -> Future[T]:
|
|
616
|
+
"""Submit a function to the executor.
|
|
617
|
+
|
|
618
|
+
Args:
|
|
619
|
+
func: The function to submit.
|
|
620
|
+
*args: The positional arguments to the function.
|
|
621
|
+
**kwargs: The keyword arguments to the function.
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
The future for the function.
|
|
625
|
+
"""
|
|
626
|
+
return super().submit(
|
|
627
|
+
cast("Callable[..., T]", partial(copy_context().run, func, *args, **kwargs))
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
def map(
|
|
631
|
+
self,
|
|
632
|
+
fn: Callable[..., T],
|
|
633
|
+
*iterables: Iterable[Any],
|
|
634
|
+
**kwargs: Any,
|
|
635
|
+
) -> Iterator[T]:
|
|
636
|
+
"""Map a function to multiple iterables.
|
|
637
|
+
|
|
638
|
+
Args:
|
|
639
|
+
fn: The function to map.
|
|
640
|
+
*iterables: The iterables to map over.
|
|
641
|
+
timeout: The timeout for the map.
|
|
642
|
+
chunksize: The chunksize for the map.
|
|
643
|
+
|
|
644
|
+
Returns:
|
|
645
|
+
The iterator for the mapped function.
|
|
646
|
+
"""
|
|
647
|
+
contexts = [copy_context() for _ in range(len(iterables[0]))] # type: ignore[arg-type]
|
|
648
|
+
|
|
649
|
+
def _wrapped_fn(*args: Any) -> T:
|
|
650
|
+
return contexts.pop().run(fn, *args)
|
|
651
|
+
|
|
652
|
+
return super().map(
|
|
653
|
+
_wrapped_fn,
|
|
654
|
+
*iterables,
|
|
655
|
+
**kwargs,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@contextmanager
|
|
660
|
+
def get_executor_for_config(
|
|
661
|
+
config: RunnableConfig | None,
|
|
662
|
+
) -> Generator[Executor, None, None]:
|
|
663
|
+
"""Get an executor for a config.
|
|
664
|
+
|
|
665
|
+
Args:
|
|
666
|
+
config: The config.
|
|
667
|
+
|
|
668
|
+
Yields:
|
|
669
|
+
The executor.
|
|
670
|
+
"""
|
|
671
|
+
config = config or {}
|
|
672
|
+
with ContextThreadPoolExecutor(
|
|
673
|
+
max_workers=config.get("max_concurrency")
|
|
674
|
+
) as executor:
|
|
675
|
+
yield executor
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
async def run_in_executor(
|
|
679
|
+
executor_or_config: Executor | RunnableConfig | None,
|
|
680
|
+
func: Callable[P, T],
|
|
681
|
+
*args: P.args,
|
|
682
|
+
**kwargs: P.kwargs,
|
|
683
|
+
) -> T:
|
|
684
|
+
"""Run a function in an executor.
|
|
685
|
+
|
|
686
|
+
Args:
|
|
687
|
+
executor_or_config: The executor or config to run in.
|
|
688
|
+
func: The function.
|
|
689
|
+
*args: The positional arguments to the function.
|
|
690
|
+
**kwargs: The keyword arguments to the function.
|
|
691
|
+
|
|
692
|
+
Returns:
|
|
693
|
+
The output of the function.
|
|
694
|
+
"""
|
|
695
|
+
|
|
696
|
+
def wrapper() -> T:
|
|
697
|
+
try:
|
|
698
|
+
return func(*args, **kwargs)
|
|
699
|
+
except StopIteration as exc:
|
|
700
|
+
# StopIteration can't be set on an asyncio.Future
|
|
701
|
+
# it raises a TypeError and leaves the Future pending forever
|
|
702
|
+
# so we need to convert it to a RuntimeError
|
|
703
|
+
raise RuntimeError from exc
|
|
704
|
+
|
|
705
|
+
if executor_or_config is None or isinstance(executor_or_config, dict):
|
|
706
|
+
# Use default executor with context copied from current context
|
|
707
|
+
return await asyncio.get_running_loop().run_in_executor(
|
|
708
|
+
None,
|
|
709
|
+
cast("Callable[..., T]", partial(copy_context().run, wrapper)),
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
return await asyncio.get_running_loop().run_in_executor(executor_or_config, wrapper)
|