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,518 @@
|
|
|
1
|
+
"""Base message."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, cast, overload
|
|
6
|
+
|
|
7
|
+
from pydantic import ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
from coco_core._api.deprecation import warn_deprecated
|
|
10
|
+
from coco_core.load.serializable import Serializable
|
|
11
|
+
from coco_core.messages import content as types
|
|
12
|
+
from coco_core.utils import get_bolded_text
|
|
13
|
+
from coco_core.utils._merge import merge_dicts, merge_lists
|
|
14
|
+
from coco_core.utils.interactive_env import is_interactive_env
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Sequence
|
|
18
|
+
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
21
|
+
from coco_core.prompts.chat import ChatPromptTemplate
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _extract_reasoning_from_additional_kwargs(
|
|
25
|
+
message: BaseMessage,
|
|
26
|
+
) -> types.ReasoningContentBlock | None:
|
|
27
|
+
"""Extract `reasoning_content` from `additional_kwargs`.
|
|
28
|
+
|
|
29
|
+
Handles reasoning content stored in various formats:
|
|
30
|
+
- `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
message: The message to extract reasoning from.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
A `ReasoningContentBlock` if reasoning content is found, None otherwise.
|
|
37
|
+
"""
|
|
38
|
+
additional_kwargs = getattr(message, "additional_kwargs", {})
|
|
39
|
+
|
|
40
|
+
reasoning_content = additional_kwargs.get("reasoning_content")
|
|
41
|
+
if reasoning_content is not None and isinstance(reasoning_content, str):
|
|
42
|
+
return {"type": "reasoning", "reasoning": reasoning_content}
|
|
43
|
+
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TextAccessor(str):
|
|
48
|
+
"""String-like object that supports both property and method access patterns.
|
|
49
|
+
|
|
50
|
+
Exists to maintain backward compatibility while transitioning from method-based to
|
|
51
|
+
property-based text access in message objects. In Coco <v1.0, message text was
|
|
52
|
+
accessed via `.text()` method calls. In v1.0=<, the preferred pattern is property
|
|
53
|
+
access via `.text`.
|
|
54
|
+
|
|
55
|
+
Rather than breaking existing code immediately, `TextAccessor` allows both
|
|
56
|
+
patterns:
|
|
57
|
+
- Modern property access: `message.text` (returns string directly)
|
|
58
|
+
- Legacy method access: `message.text()` (callable, emits deprecation warning)
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
__slots__ = ()
|
|
63
|
+
|
|
64
|
+
def __new__(cls, value: str) -> Self:
|
|
65
|
+
"""Create new TextAccessor instance."""
|
|
66
|
+
return str.__new__(cls, value)
|
|
67
|
+
|
|
68
|
+
def __call__(self) -> str:
|
|
69
|
+
"""Enable method-style text access for backward compatibility.
|
|
70
|
+
|
|
71
|
+
This method exists solely to support legacy code that calls `.text()`
|
|
72
|
+
as a method. New code should use property access (`.text`) instead.
|
|
73
|
+
|
|
74
|
+
!!! deprecated
|
|
75
|
+
As of `trustcoco-core` 1.0.0, calling `.text()` as a method is deprecated.
|
|
76
|
+
Use `.text` as a property instead. This method will be removed in 2.0.0.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The string content, identical to property access.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
warn_deprecated(
|
|
83
|
+
since="1.0.0",
|
|
84
|
+
message=(
|
|
85
|
+
"Calling .text() as a method is deprecated. "
|
|
86
|
+
"Use .text as a property instead (e.g., message.text)."
|
|
87
|
+
),
|
|
88
|
+
removal="2.0.0",
|
|
89
|
+
)
|
|
90
|
+
return str(self)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class BaseMessage(Serializable):
|
|
94
|
+
"""Base abstract message class.
|
|
95
|
+
|
|
96
|
+
Messages are the inputs and outputs of a chat model.
|
|
97
|
+
|
|
98
|
+
Examples include [`HumanMessage`][coco.messages.HumanMessage],
|
|
99
|
+
[`AIMessage`][coco.messages.AIMessage], and
|
|
100
|
+
[`SystemMessage`][coco.messages.SystemMessage].
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
content: str | list[str | dict[Any, Any]]
|
|
104
|
+
"""The contents of the message."""
|
|
105
|
+
|
|
106
|
+
additional_kwargs: dict[Any, Any] = Field(default_factory=dict)
|
|
107
|
+
"""Reserved for additional payload data associated with the message.
|
|
108
|
+
|
|
109
|
+
For example, for a message from an AI, this could include tool calls as
|
|
110
|
+
encoded by the model provider.
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
response_metadata: dict[Any, Any] = Field(default_factory=dict)
|
|
115
|
+
"""Examples: response headers, logprobs, token counts, model name."""
|
|
116
|
+
|
|
117
|
+
type: str
|
|
118
|
+
"""The type of the message. Must be a string that is unique to the message type.
|
|
119
|
+
|
|
120
|
+
The purpose of this field is to allow for easy identification of the message type
|
|
121
|
+
when deserializing messages.
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
name: str | None = None
|
|
126
|
+
"""An optional name for the message.
|
|
127
|
+
|
|
128
|
+
This can be used to provide a human-readable name for the message.
|
|
129
|
+
|
|
130
|
+
Usage of this field is optional, and whether it's used or not is up to the
|
|
131
|
+
model implementation.
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
id: str | None = Field(default=None, coerce_numbers_to_str=True)
|
|
136
|
+
"""An optional unique identifier for the message.
|
|
137
|
+
|
|
138
|
+
This should ideally be provided by the provider/model which created the message.
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
model_config = ConfigDict(
|
|
143
|
+
extra="allow",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
@overload
|
|
147
|
+
def __init__(
|
|
148
|
+
self,
|
|
149
|
+
content: str | list[str | dict[Any, Any]],
|
|
150
|
+
**kwargs: Any,
|
|
151
|
+
) -> None: ...
|
|
152
|
+
|
|
153
|
+
@overload
|
|
154
|
+
def __init__(
|
|
155
|
+
self,
|
|
156
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
157
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
158
|
+
**kwargs: Any,
|
|
159
|
+
) -> None: ...
|
|
160
|
+
|
|
161
|
+
def __init__(
|
|
162
|
+
self,
|
|
163
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
164
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
165
|
+
**kwargs: Any,
|
|
166
|
+
) -> None:
|
|
167
|
+
"""Initialize a `BaseMessage`.
|
|
168
|
+
|
|
169
|
+
Specify `content` as positional arg or `content_blocks` for typing.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
content: The contents of the message.
|
|
173
|
+
content_blocks: Typed standard content.
|
|
174
|
+
**kwargs: Additional arguments to pass to the parent class.
|
|
175
|
+
"""
|
|
176
|
+
if content_blocks is not None:
|
|
177
|
+
super().__init__(content=content_blocks, **kwargs)
|
|
178
|
+
else:
|
|
179
|
+
super().__init__(content=content, **kwargs)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def is_lc_serializable(cls) -> bool:
|
|
183
|
+
"""`BaseMessage` is serializable.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
True
|
|
187
|
+
"""
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
@classmethod
|
|
191
|
+
def get_lc_namespace(cls) -> list[str]:
|
|
192
|
+
"""Get the namespace of the Coco object.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
`["coco", "schema", "messages"]`
|
|
196
|
+
"""
|
|
197
|
+
return ["coco", "schema", "messages"]
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def content_blocks(self) -> list[types.ContentBlock]:
|
|
201
|
+
r"""Load content blocks from the message content.
|
|
202
|
+
|
|
203
|
+
!!! version-added "Added in `trustcoco-core` 1.0.0"
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
# Needed here to avoid circular import, as these classes import BaseMessages
|
|
207
|
+
from coco_core.messages.block_translators.anthropic import ( # noqa: PLC0415
|
|
208
|
+
_convert_to_v1_from_anthropic_input,
|
|
209
|
+
)
|
|
210
|
+
from coco_core.messages.block_translators.bedrock_converse import ( # noqa: PLC0415
|
|
211
|
+
_convert_to_v1_from_converse_input,
|
|
212
|
+
)
|
|
213
|
+
from coco_core.messages.block_translators.google_genai import ( # noqa: PLC0415
|
|
214
|
+
_convert_to_v1_from_genai_input,
|
|
215
|
+
)
|
|
216
|
+
from coco_core.messages.block_translators.coco_v0 import ( # noqa: PLC0415
|
|
217
|
+
_convert_v0_multimodal_input_to_v1,
|
|
218
|
+
)
|
|
219
|
+
from coco_core.messages.block_translators.openai import ( # noqa: PLC0415
|
|
220
|
+
_convert_to_v1_from_chat_completions_input,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
blocks: list[types.ContentBlock] = []
|
|
224
|
+
content = (
|
|
225
|
+
# Transpose string content to list, otherwise assumed to be list
|
|
226
|
+
[self.content]
|
|
227
|
+
if isinstance(self.content, str) and self.content
|
|
228
|
+
else self.content
|
|
229
|
+
)
|
|
230
|
+
for item in content:
|
|
231
|
+
if isinstance(item, str):
|
|
232
|
+
# Plain string content is treated as a text block
|
|
233
|
+
blocks.append({"type": "text", "text": item})
|
|
234
|
+
elif isinstance(item, dict):
|
|
235
|
+
item_type = item.get("type")
|
|
236
|
+
if item_type not in types.KNOWN_BLOCK_TYPES:
|
|
237
|
+
# Handle all provider-specific or None type blocks as non-standard -
|
|
238
|
+
# we'll come back to these later
|
|
239
|
+
blocks.append({"type": "non_standard", "value": item})
|
|
240
|
+
else:
|
|
241
|
+
# Guard against v0 blocks that share the same `type` keys
|
|
242
|
+
if "source_type" in item:
|
|
243
|
+
blocks.append({"type": "non_standard", "value": item})
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
# This can't be a v0 block (since they require `source_type`),
|
|
247
|
+
# so it's a known v1 block type
|
|
248
|
+
blocks.append(cast("types.ContentBlock", item))
|
|
249
|
+
|
|
250
|
+
# Subsequent passes: attempt to unpack non-standard blocks.
|
|
251
|
+
# This is the last stop - if we can't parse it here, it is left as non-standard
|
|
252
|
+
for parsing_step in [
|
|
253
|
+
_convert_v0_multimodal_input_to_v1,
|
|
254
|
+
_convert_to_v1_from_chat_completions_input,
|
|
255
|
+
_convert_to_v1_from_anthropic_input,
|
|
256
|
+
_convert_to_v1_from_genai_input,
|
|
257
|
+
_convert_to_v1_from_converse_input,
|
|
258
|
+
]:
|
|
259
|
+
blocks = parsing_step(blocks)
|
|
260
|
+
return blocks
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def text(self) -> TextAccessor:
|
|
264
|
+
"""Get the text content of the message as a string.
|
|
265
|
+
|
|
266
|
+
Can be used as both property (`message.text`) and method (`message.text()`).
|
|
267
|
+
|
|
268
|
+
Handles both string and list content types (e.g. for content blocks). Only
|
|
269
|
+
extracts blocks with `type: 'text'`; other block types are ignored.
|
|
270
|
+
|
|
271
|
+
!!! deprecated
|
|
272
|
+
As of `trustcoco-core` 1.0.0, calling `.text()` as a method is deprecated.
|
|
273
|
+
Use `.text` as a property instead. This method will be removed in 2.0.0.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
The text content of the message.
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
if isinstance(self.content, str):
|
|
280
|
+
text_value = self.content
|
|
281
|
+
else:
|
|
282
|
+
# Must be a list
|
|
283
|
+
blocks = [
|
|
284
|
+
block
|
|
285
|
+
for block in self.content
|
|
286
|
+
if isinstance(block, str)
|
|
287
|
+
or (block.get("type") == "text" and isinstance(block.get("text"), str))
|
|
288
|
+
]
|
|
289
|
+
text_value = "".join(
|
|
290
|
+
block if isinstance(block, str) else block["text"] for block in blocks
|
|
291
|
+
)
|
|
292
|
+
return TextAccessor(text_value)
|
|
293
|
+
|
|
294
|
+
def __add__(self, other: Any) -> ChatPromptTemplate:
|
|
295
|
+
"""Concatenate this message with another message.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
other: Another message to concatenate with this one.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
A ChatPromptTemplate containing both messages.
|
|
302
|
+
"""
|
|
303
|
+
# Import locally to prevent circular imports.
|
|
304
|
+
from coco_core.prompts.chat import ChatPromptTemplate # noqa: PLC0415
|
|
305
|
+
|
|
306
|
+
prompt = ChatPromptTemplate(messages=[self])
|
|
307
|
+
return prompt.__add__(other)
|
|
308
|
+
|
|
309
|
+
def pretty_repr(
|
|
310
|
+
self,
|
|
311
|
+
html: bool = False, # noqa: FBT001,FBT002
|
|
312
|
+
) -> str:
|
|
313
|
+
"""Get a pretty representation of the message.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
html: Whether to format the message as HTML. If `True`, the message will be
|
|
317
|
+
formatted with HTML tags.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
A pretty representation of the message.
|
|
321
|
+
|
|
322
|
+
Example:
|
|
323
|
+
```python
|
|
324
|
+
from coco_core.messages import HumanMessage
|
|
325
|
+
|
|
326
|
+
msg = HumanMessage(content="What is the capital of France?")
|
|
327
|
+
print(msg.pretty_repr())
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Results in:
|
|
331
|
+
|
|
332
|
+
```txt
|
|
333
|
+
================================ Human Message =================================
|
|
334
|
+
|
|
335
|
+
What is the capital of France?
|
|
336
|
+
```
|
|
337
|
+
""" # noqa: E501
|
|
338
|
+
title = get_msg_title_repr(self.type.title() + " Message", bold=html)
|
|
339
|
+
# TODO: handle non-string content.
|
|
340
|
+
if self.name is not None:
|
|
341
|
+
title += f"\nName: {self.name}"
|
|
342
|
+
return f"{title}\n\n{self.content}"
|
|
343
|
+
|
|
344
|
+
def pretty_print(self) -> None:
|
|
345
|
+
"""Print a pretty representation of the message.
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
```python
|
|
349
|
+
from coco_core.messages import AIMessage
|
|
350
|
+
|
|
351
|
+
msg = AIMessage(content="The capital of France is Paris.")
|
|
352
|
+
msg.pretty_print()
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Results in:
|
|
356
|
+
|
|
357
|
+
```txt
|
|
358
|
+
================================== Ai Message ==================================
|
|
359
|
+
|
|
360
|
+
The capital of France is Paris.
|
|
361
|
+
```
|
|
362
|
+
""" # noqa: E501
|
|
363
|
+
print(self.pretty_repr(html=is_interactive_env())) # noqa: T201
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def merge_content(
|
|
367
|
+
first_content: str | list[str | dict[Any, Any]],
|
|
368
|
+
*contents: str | list[str | dict[Any, Any]],
|
|
369
|
+
) -> str | list[str | dict[Any, Any]]:
|
|
370
|
+
"""Merge multiple message contents.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
first_content: The first `content`. Can be a string or a list.
|
|
374
|
+
contents: The other `content`s. Can be a string or a list.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
The merged content.
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
merged: str | list[str | dict[Any, Any]]
|
|
381
|
+
merged = "" if first_content is None else first_content
|
|
382
|
+
|
|
383
|
+
for content in contents:
|
|
384
|
+
# If current is a string
|
|
385
|
+
if isinstance(merged, str):
|
|
386
|
+
# If the next chunk is also a string, then merge them naively
|
|
387
|
+
if isinstance(content, str):
|
|
388
|
+
merged += content
|
|
389
|
+
# If the next chunk is a list, add the current to the start of the list
|
|
390
|
+
else:
|
|
391
|
+
merged = [merged, *content]
|
|
392
|
+
elif isinstance(content, list):
|
|
393
|
+
# If both are lists
|
|
394
|
+
merged = merge_lists(merged, content) # type: ignore[assignment]
|
|
395
|
+
# If the first content is a list, and the second content is a string
|
|
396
|
+
# If the last element of the first content is a string
|
|
397
|
+
# Add the second content to the last element
|
|
398
|
+
elif merged and isinstance(merged[-1], str):
|
|
399
|
+
merged[-1] += content
|
|
400
|
+
# If second content is an empty string, treat as a no-op
|
|
401
|
+
elif content == "":
|
|
402
|
+
pass
|
|
403
|
+
# Otherwise, add the second content as a new element of the list
|
|
404
|
+
elif merged:
|
|
405
|
+
merged.append(content)
|
|
406
|
+
return merged
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class BaseMessageChunk(BaseMessage):
|
|
410
|
+
"""Message chunk, which can be concatenated with other Message chunks."""
|
|
411
|
+
|
|
412
|
+
def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
|
|
413
|
+
"""Message chunks support concatenation with other message chunks.
|
|
414
|
+
|
|
415
|
+
This functionality is useful to combine message chunks yielded from
|
|
416
|
+
a streaming model into a complete message.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
other: Another message chunk to concatenate with this one.
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
A new message chunk that is the concatenation of this message chunk
|
|
423
|
+
and the other message chunk.
|
|
424
|
+
|
|
425
|
+
Raises:
|
|
426
|
+
TypeError: If the other object is not a message chunk.
|
|
427
|
+
|
|
428
|
+
Example:
|
|
429
|
+
```txt
|
|
430
|
+
AIMessageChunk(content="Hello", ...)
|
|
431
|
+
+ AIMessageChunk(content=" World", ...)
|
|
432
|
+
= AIMessageChunk(content="Hello World", ...)
|
|
433
|
+
```
|
|
434
|
+
"""
|
|
435
|
+
if isinstance(other, BaseMessageChunk):
|
|
436
|
+
# If both are (subclasses of) BaseMessageChunk,
|
|
437
|
+
# concat into a single BaseMessageChunk
|
|
438
|
+
|
|
439
|
+
return self.__class__(
|
|
440
|
+
id=self.id,
|
|
441
|
+
type=self.type,
|
|
442
|
+
content=merge_content(self.content, other.content),
|
|
443
|
+
additional_kwargs=merge_dicts(
|
|
444
|
+
self.additional_kwargs, other.additional_kwargs
|
|
445
|
+
),
|
|
446
|
+
response_metadata=merge_dicts(
|
|
447
|
+
self.response_metadata, other.response_metadata
|
|
448
|
+
),
|
|
449
|
+
)
|
|
450
|
+
if isinstance(other, list) and all(
|
|
451
|
+
isinstance(o, BaseMessageChunk) for o in other
|
|
452
|
+
):
|
|
453
|
+
content = merge_content(self.content, *(o.content for o in other))
|
|
454
|
+
additional_kwargs = merge_dicts(
|
|
455
|
+
self.additional_kwargs, *(o.additional_kwargs for o in other)
|
|
456
|
+
)
|
|
457
|
+
response_metadata = merge_dicts(
|
|
458
|
+
self.response_metadata, *(o.response_metadata for o in other)
|
|
459
|
+
)
|
|
460
|
+
return self.__class__( # type: ignore[call-arg]
|
|
461
|
+
id=self.id,
|
|
462
|
+
content=content,
|
|
463
|
+
additional_kwargs=additional_kwargs,
|
|
464
|
+
response_metadata=response_metadata,
|
|
465
|
+
)
|
|
466
|
+
msg = (
|
|
467
|
+
'unsupported operand type(s) for +: "'
|
|
468
|
+
f"{self.__class__.__name__}"
|
|
469
|
+
f'" and "{other.__class__.__name__}"'
|
|
470
|
+
)
|
|
471
|
+
raise TypeError(msg)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def message_to_dict(message: BaseMessage) -> dict[str, Any]:
|
|
475
|
+
"""Convert a Message to a dictionary.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
message: Message to convert.
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
Message as a dict. The dict will have a `type` key with the message type
|
|
482
|
+
and a `data` key with the message data as a dict.
|
|
483
|
+
|
|
484
|
+
"""
|
|
485
|
+
return {"type": message.type, "data": message.model_dump()}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def messages_to_dict(messages: Sequence[BaseMessage]) -> list[dict[str, Any]]:
|
|
489
|
+
"""Convert a sequence of Messages to a list of dictionaries.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
messages: Sequence of messages (as `BaseMessage`s) to convert.
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
List of messages as dicts.
|
|
496
|
+
|
|
497
|
+
"""
|
|
498
|
+
return [message_to_dict(m) for m in messages]
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def get_msg_title_repr(title: str, *, bold: bool = False) -> str:
|
|
502
|
+
"""Get a title representation for a message.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
title: The title.
|
|
506
|
+
bold: Whether to bold the title.
|
|
507
|
+
|
|
508
|
+
Returns:
|
|
509
|
+
The title representation.
|
|
510
|
+
|
|
511
|
+
"""
|
|
512
|
+
padded = " " + title + " "
|
|
513
|
+
sep_len = (80 - len(padded)) // 2
|
|
514
|
+
sep = "=" * sep_len
|
|
515
|
+
second_sep = sep + "=" if len(padded) % 2 else sep
|
|
516
|
+
if bold:
|
|
517
|
+
padded = get_bolded_text(padded)
|
|
518
|
+
return f"{sep}{padded}{second_sep}"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Derivations of standard content blocks from provider content.
|
|
2
|
+
|
|
3
|
+
`AIMessage` will first attempt to use a provider-specific translator if
|
|
4
|
+
`model_provider` is set in `response_metadata` on the message. Consequently, each
|
|
5
|
+
provider translator must handle all possible content response types from the provider,
|
|
6
|
+
including text.
|
|
7
|
+
|
|
8
|
+
If no provider is set, or if the provider does not have a registered translator,
|
|
9
|
+
`AIMessage` will fall back to best-effort parsing of the content into blocks using
|
|
10
|
+
the implementation in `BaseMessage`.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
|
|
20
|
+
from coco_core.messages import AIMessage, AIMessageChunk
|
|
21
|
+
from coco_core.messages import content as types
|
|
22
|
+
|
|
23
|
+
# Provider to translator mapping
|
|
24
|
+
PROVIDER_TRANSLATORS: dict[str, dict[str, Callable[..., list[types.ContentBlock]]]] = {}
|
|
25
|
+
"""Map model provider names to translator functions.
|
|
26
|
+
|
|
27
|
+
The dictionary maps provider names (e.g. `'openai'`, `'anthropic'`) to another
|
|
28
|
+
dictionary with two keys:
|
|
29
|
+
- `'translate_content'`: Function to translate `AIMessage` content.
|
|
30
|
+
- `'translate_content_chunk'`: Function to translate `AIMessageChunk` content.
|
|
31
|
+
|
|
32
|
+
When calling `content_blocks` on an `AIMessage` or `AIMessageChunk`, if
|
|
33
|
+
`model_provider` is set in `response_metadata`, the corresponding translator
|
|
34
|
+
functions will be used to parse the content into blocks. Otherwise, best-effort parsing
|
|
35
|
+
in `BaseMessage` will be used.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def register_translator(
|
|
40
|
+
provider: str,
|
|
41
|
+
translate_content: Callable[[AIMessage], list[types.ContentBlock]],
|
|
42
|
+
translate_content_chunk: Callable[[AIMessageChunk], list[types.ContentBlock]],
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Register content translators for a provider in `PROVIDER_TRANSLATORS`.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
provider: The model provider name (e.g. `'openai'`, `'anthropic'`).
|
|
48
|
+
translate_content: Function to translate `AIMessage` content.
|
|
49
|
+
translate_content_chunk: Function to translate `AIMessageChunk` content.
|
|
50
|
+
"""
|
|
51
|
+
PROVIDER_TRANSLATORS[provider] = {
|
|
52
|
+
"translate_content": translate_content,
|
|
53
|
+
"translate_content_chunk": translate_content_chunk,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_translator(
|
|
58
|
+
provider: str,
|
|
59
|
+
) -> dict[str, Callable[..., list[types.ContentBlock]]] | None:
|
|
60
|
+
"""Get the translator functions for a provider.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
provider: The model provider name.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary with `'translate_content'` and `'translate_content_chunk'`
|
|
67
|
+
functions, or None if no translator is registered for the provider. In such
|
|
68
|
+
case, best-effort parsing in `BaseMessage` will be used.
|
|
69
|
+
"""
|
|
70
|
+
return PROVIDER_TRANSLATORS.get(provider)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _register_translators() -> None:
|
|
74
|
+
"""Register all translators in trustcoco-core.
|
|
75
|
+
|
|
76
|
+
A unit test ensures all modules in `block_translators` are represented here.
|
|
77
|
+
|
|
78
|
+
For translators implemented outside trustcoco-core, they can be registered by
|
|
79
|
+
calling `register_translator` from within the integration package.
|
|
80
|
+
"""
|
|
81
|
+
from coco_core.messages.block_translators.anthropic import ( # noqa: PLC0415
|
|
82
|
+
_register_anthropic_translator,
|
|
83
|
+
)
|
|
84
|
+
from coco_core.messages.block_translators.bedrock import ( # noqa: PLC0415
|
|
85
|
+
_register_bedrock_translator,
|
|
86
|
+
)
|
|
87
|
+
from coco_core.messages.block_translators.bedrock_converse import ( # noqa: PLC0415
|
|
88
|
+
_register_bedrock_converse_translator,
|
|
89
|
+
)
|
|
90
|
+
from coco_core.messages.block_translators.google_genai import ( # noqa: PLC0415
|
|
91
|
+
_register_google_genai_translator,
|
|
92
|
+
)
|
|
93
|
+
from coco_core.messages.block_translators.google_vertexai import ( # noqa: PLC0415
|
|
94
|
+
_register_google_vertexai_translator,
|
|
95
|
+
)
|
|
96
|
+
from coco_core.messages.block_translators.groq import ( # noqa: PLC0415
|
|
97
|
+
_register_groq_translator,
|
|
98
|
+
)
|
|
99
|
+
from coco_core.messages.block_translators.openai import ( # noqa: PLC0415
|
|
100
|
+
_register_openai_translator,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
_register_bedrock_translator()
|
|
104
|
+
_register_bedrock_converse_translator()
|
|
105
|
+
_register_anthropic_translator()
|
|
106
|
+
_register_google_genai_translator()
|
|
107
|
+
_register_google_vertexai_translator()
|
|
108
|
+
_register_groq_translator()
|
|
109
|
+
_register_openai_translator()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
_register_translators()
|