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,62 @@
|
|
|
1
|
+
"""Function Message."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from coco_core.messages.base import (
|
|
8
|
+
BaseMessage,
|
|
9
|
+
BaseMessageChunk,
|
|
10
|
+
merge_content,
|
|
11
|
+
)
|
|
12
|
+
from coco_core.utils._merge import merge_dicts
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FunctionMessage(BaseMessage):
|
|
16
|
+
"""Message for passing the result of executing a tool back to a model.
|
|
17
|
+
|
|
18
|
+
`FunctionMessage` are an older version of the `ToolMessage` schema, and
|
|
19
|
+
do not contain the `tool_call_id` field.
|
|
20
|
+
|
|
21
|
+
The `tool_call_id` field is used to associate the tool call request with the
|
|
22
|
+
tool call response. Useful in situations where a chat model is able
|
|
23
|
+
to request multiple tool calls in parallel.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
"""The name of the function that was executed."""
|
|
29
|
+
|
|
30
|
+
type: Literal["function"] = "function"
|
|
31
|
+
"""The type of the message (used for serialization)."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FunctionMessageChunk(FunctionMessage, BaseMessageChunk):
|
|
35
|
+
"""Function Message chunk."""
|
|
36
|
+
|
|
37
|
+
# Ignoring mypy re-assignment here since we're overriding the value
|
|
38
|
+
# to make sure that the chunk variant can be discriminated from the
|
|
39
|
+
# non-chunk variant.
|
|
40
|
+
type: Literal["FunctionMessageChunk"] = "FunctionMessageChunk" # type: ignore[assignment]
|
|
41
|
+
"""The type of the message (used for serialization)."""
|
|
42
|
+
|
|
43
|
+
@override
|
|
44
|
+
def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
|
|
45
|
+
if isinstance(other, FunctionMessageChunk):
|
|
46
|
+
if self.name != other.name:
|
|
47
|
+
msg = "Cannot concatenate FunctionMessageChunks with different names."
|
|
48
|
+
raise ValueError(msg)
|
|
49
|
+
|
|
50
|
+
return self.__class__(
|
|
51
|
+
name=self.name,
|
|
52
|
+
content=merge_content(self.content, other.content),
|
|
53
|
+
additional_kwargs=merge_dicts(
|
|
54
|
+
self.additional_kwargs, other.additional_kwargs
|
|
55
|
+
),
|
|
56
|
+
response_metadata=merge_dicts(
|
|
57
|
+
self.response_metadata, other.response_metadata
|
|
58
|
+
),
|
|
59
|
+
id=self.id,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return super().__add__(other)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Human message."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, cast, overload
|
|
4
|
+
|
|
5
|
+
from coco_core.messages import content as types
|
|
6
|
+
from coco_core.messages.base import BaseMessage, BaseMessageChunk
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HumanMessage(BaseMessage):
|
|
10
|
+
"""Message from the user.
|
|
11
|
+
|
|
12
|
+
A `HumanMessage` is a message that is passed in from a user to the model.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
```python
|
|
16
|
+
from coco_core.messages import HumanMessage, SystemMessage
|
|
17
|
+
|
|
18
|
+
messages = [
|
|
19
|
+
SystemMessage(content="You are a helpful assistant! Your name is Bob."),
|
|
20
|
+
HumanMessage(content="What is your name?"),
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# Instantiate a chat model and invoke it with the messages
|
|
24
|
+
model = ...
|
|
25
|
+
print(model.invoke(messages))
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
type: Literal["human"] = "human"
|
|
30
|
+
"""The type of the message (used for serialization)."""
|
|
31
|
+
|
|
32
|
+
@overload
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
content: str | list[str | dict[Any, Any]],
|
|
36
|
+
**kwargs: Any,
|
|
37
|
+
) -> None: ...
|
|
38
|
+
|
|
39
|
+
@overload
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
43
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> None: ...
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
50
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
51
|
+
**kwargs: Any,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Specify `content` as positional arg or `content_blocks` for typing."""
|
|
54
|
+
if content_blocks is not None:
|
|
55
|
+
super().__init__(
|
|
56
|
+
content=cast("list[str | dict[Any, Any]]", content_blocks),
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
super().__init__(content=content, **kwargs)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class HumanMessageChunk(HumanMessage, BaseMessageChunk):
|
|
64
|
+
"""Human Message chunk."""
|
|
65
|
+
|
|
66
|
+
# Ignoring mypy re-assignment here since we're overriding the value
|
|
67
|
+
# to make sure that the chunk variant can be discriminated from the
|
|
68
|
+
# non-chunk variant.
|
|
69
|
+
type: Literal["HumanMessageChunk"] = "HumanMessageChunk" # type: ignore[assignment]
|
|
70
|
+
"""The type of the message (used for serialization)."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Message responsible for deleting other messages."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from coco_core.messages.base import BaseMessage
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RemoveMessage(BaseMessage):
|
|
9
|
+
"""Message responsible for deleting other messages."""
|
|
10
|
+
|
|
11
|
+
type: Literal["remove"] = "remove"
|
|
12
|
+
"""The type of the message (used for serialization)."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
id: str,
|
|
17
|
+
**kwargs: Any,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Create a RemoveMessage.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
id: The ID of the message to remove.
|
|
23
|
+
**kwargs: Additional fields to pass to the message.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
ValueError: If the 'content' field is passed in kwargs.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
if kwargs.pop("content", None):
|
|
30
|
+
msg = "RemoveMessage does not support 'content' field."
|
|
31
|
+
raise ValueError(msg)
|
|
32
|
+
|
|
33
|
+
super().__init__("", id=id, **kwargs)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""System message."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, cast, overload
|
|
4
|
+
|
|
5
|
+
from coco_core.messages import content as types
|
|
6
|
+
from coco_core.messages.base import BaseMessage, BaseMessageChunk
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SystemMessage(BaseMessage):
|
|
10
|
+
"""Message for priming AI behavior.
|
|
11
|
+
|
|
12
|
+
The system message is usually passed in as the first of a sequence
|
|
13
|
+
of input messages.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
```python
|
|
17
|
+
from coco_core.messages import HumanMessage, SystemMessage
|
|
18
|
+
|
|
19
|
+
messages = [
|
|
20
|
+
SystemMessage(content="You are a helpful assistant! Your name is Bob."),
|
|
21
|
+
HumanMessage(content="What is your name?"),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# Define a chat model and invoke it with the messages
|
|
25
|
+
print(model.invoke(messages))
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
type: Literal["system"] = "system"
|
|
30
|
+
"""The type of the message (used for serialization)."""
|
|
31
|
+
|
|
32
|
+
@overload
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
content: str | list[str | dict[Any, Any]],
|
|
36
|
+
**kwargs: Any,
|
|
37
|
+
) -> None: ...
|
|
38
|
+
|
|
39
|
+
@overload
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
43
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
) -> None: ...
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
50
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
51
|
+
**kwargs: Any,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Specify `content` as positional arg or `content_blocks` for typing."""
|
|
54
|
+
if content_blocks is not None:
|
|
55
|
+
super().__init__(
|
|
56
|
+
content=cast("list[str | dict[Any, Any]]", content_blocks),
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
super().__init__(content=content, **kwargs)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SystemMessageChunk(SystemMessage, BaseMessageChunk):
|
|
64
|
+
"""System Message chunk."""
|
|
65
|
+
|
|
66
|
+
# Ignoring mypy re-assignment here since we're overriding the value
|
|
67
|
+
# to make sure that the chunk variant can be discriminated from the
|
|
68
|
+
# non-chunk variant.
|
|
69
|
+
type: Literal["SystemMessageChunk"] = "SystemMessageChunk" # type: ignore[assignment]
|
|
70
|
+
"""The type of the message (used for serialization)."""
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""Messages for tools."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Literal, cast, overload
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import Field, model_validator
|
|
8
|
+
from typing_extensions import NotRequired, TypedDict, override
|
|
9
|
+
|
|
10
|
+
from coco_core.messages import content as types
|
|
11
|
+
from coco_core.messages.base import BaseMessage, BaseMessageChunk, merge_content
|
|
12
|
+
from coco_core.messages.content import InvalidToolCall
|
|
13
|
+
from coco_core.utils._merge import merge_dicts, merge_obj
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolOutputMixin:
|
|
17
|
+
"""Mixin for objects that tools can return directly.
|
|
18
|
+
|
|
19
|
+
If a custom BaseTool is invoked with a `ToolCall` and the output of custom code is
|
|
20
|
+
not an instance of `ToolOutputMixin`, the output will automatically be coerced to
|
|
21
|
+
a string and wrapped in a `ToolMessage`.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolMessage(BaseMessage, ToolOutputMixin):
|
|
27
|
+
"""Message for passing the result of executing a tool back to a model.
|
|
28
|
+
|
|
29
|
+
`ToolMessage` objects contain the result of a tool invocation. Typically, the result
|
|
30
|
+
is encoded inside the `content` field.
|
|
31
|
+
|
|
32
|
+
`tool_call_id` is used to associate the tool call request with the tool call
|
|
33
|
+
response. Useful in situations where a chat model is able to request multiple tool
|
|
34
|
+
calls in parallel.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
A `ToolMessage` representing a result of `42` from a tool call with id
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from coco_core.messages import ToolMessage
|
|
41
|
+
|
|
42
|
+
ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
A `ToolMessage` where only part of the tool output is sent to the model
|
|
47
|
+
and the full output is passed in to artifact.
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from coco_core.messages import ToolMessage
|
|
51
|
+
|
|
52
|
+
tool_output = {
|
|
53
|
+
"stdout": "From the graph we can see that the correlation between "
|
|
54
|
+
"x and y is ...",
|
|
55
|
+
"stderr": None,
|
|
56
|
+
"artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ToolMessage(
|
|
60
|
+
content=tool_output["stdout"],
|
|
61
|
+
artifact=tool_output,
|
|
62
|
+
tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL",
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
tool_call_id: str
|
|
68
|
+
"""Tool call that this message is responding to."""
|
|
69
|
+
|
|
70
|
+
type: Literal["tool"] = "tool"
|
|
71
|
+
"""The type of the message (used for serialization)."""
|
|
72
|
+
|
|
73
|
+
artifact: Any = None
|
|
74
|
+
"""Artifact of the Tool execution which is not meant to be sent to the model.
|
|
75
|
+
|
|
76
|
+
Should only be specified if it is different from the message content, e.g. if only
|
|
77
|
+
a subset of the full tool output is being passed as message content but the full
|
|
78
|
+
output is needed in other parts of the code.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
status: Literal["success", "error"] = "success"
|
|
82
|
+
"""Status of the tool invocation."""
|
|
83
|
+
|
|
84
|
+
additional_kwargs: dict[Any, Any] = Field(default_factory=dict, repr=False)
|
|
85
|
+
"""Currently inherited from `BaseMessage`, but not used."""
|
|
86
|
+
|
|
87
|
+
response_metadata: dict[Any, Any] = Field(default_factory=dict, repr=False)
|
|
88
|
+
"""Currently inherited from `BaseMessage`, but not used."""
|
|
89
|
+
|
|
90
|
+
@model_validator(mode="before")
|
|
91
|
+
@classmethod
|
|
92
|
+
def coerce_args(cls, values: dict[str, Any]) -> dict[str, Any]:
|
|
93
|
+
"""Coerce the model arguments to the correct types.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
values: The model arguments.
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
content = values["content"]
|
|
100
|
+
if isinstance(content, tuple):
|
|
101
|
+
content = list(content)
|
|
102
|
+
|
|
103
|
+
if not isinstance(content, (str, list)):
|
|
104
|
+
try:
|
|
105
|
+
values["content"] = str(content)
|
|
106
|
+
except ValueError as e:
|
|
107
|
+
msg = (
|
|
108
|
+
"ToolMessage content should be a string or a list of string/dicts. "
|
|
109
|
+
f"Received:\n\n{content=}\n\n which could not be coerced into a "
|
|
110
|
+
"string."
|
|
111
|
+
)
|
|
112
|
+
raise ValueError(msg) from e
|
|
113
|
+
elif isinstance(content, list):
|
|
114
|
+
values["content"] = []
|
|
115
|
+
for i, x in enumerate(content):
|
|
116
|
+
if not isinstance(x, (str, dict)):
|
|
117
|
+
try:
|
|
118
|
+
values["content"].append(str(x))
|
|
119
|
+
except ValueError as e:
|
|
120
|
+
msg = (
|
|
121
|
+
"ToolMessage content should be a string or a list of "
|
|
122
|
+
"string/dicts. Received a list but "
|
|
123
|
+
f"element ToolMessage.content[{i}] is not a dict and could "
|
|
124
|
+
f"not be coerced to a string.:\n\n{x}"
|
|
125
|
+
)
|
|
126
|
+
raise ValueError(msg) from e
|
|
127
|
+
else:
|
|
128
|
+
values["content"].append(x)
|
|
129
|
+
|
|
130
|
+
tool_call_id = values["tool_call_id"]
|
|
131
|
+
if isinstance(tool_call_id, (UUID, int, float)):
|
|
132
|
+
values["tool_call_id"] = str(tool_call_id)
|
|
133
|
+
return values
|
|
134
|
+
|
|
135
|
+
@overload
|
|
136
|
+
def __init__(
|
|
137
|
+
self,
|
|
138
|
+
content: str | list[str | dict[Any, Any]],
|
|
139
|
+
**kwargs: Any,
|
|
140
|
+
) -> None: ...
|
|
141
|
+
|
|
142
|
+
@overload
|
|
143
|
+
def __init__(
|
|
144
|
+
self,
|
|
145
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
146
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
147
|
+
**kwargs: Any,
|
|
148
|
+
) -> None: ...
|
|
149
|
+
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
content: str | list[str | dict[Any, Any]] | None = None,
|
|
153
|
+
content_blocks: list[types.ContentBlock] | None = None,
|
|
154
|
+
**kwargs: Any,
|
|
155
|
+
) -> None:
|
|
156
|
+
"""Initialize a `ToolMessage`.
|
|
157
|
+
|
|
158
|
+
Specify `content` as positional arg or `content_blocks` for typing.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
content: The contents of the message.
|
|
162
|
+
content_blocks: Typed standard content.
|
|
163
|
+
**kwargs: Additional fields.
|
|
164
|
+
"""
|
|
165
|
+
if content_blocks is not None:
|
|
166
|
+
super().__init__(
|
|
167
|
+
content=cast("list[str | dict[Any, Any]]", content_blocks),
|
|
168
|
+
**kwargs,
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
super().__init__(content=content, **kwargs)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class ToolMessageChunk(ToolMessage, BaseMessageChunk):
|
|
175
|
+
"""Tool Message chunk."""
|
|
176
|
+
|
|
177
|
+
# Ignoring mypy re-assignment here since we're overriding the value
|
|
178
|
+
# to make sure that the chunk variant can be discriminated from the
|
|
179
|
+
# non-chunk variant.
|
|
180
|
+
type: Literal["ToolMessageChunk"] = "ToolMessageChunk" # type: ignore[assignment]
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
|
|
184
|
+
if isinstance(other, ToolMessageChunk):
|
|
185
|
+
if self.tool_call_id != other.tool_call_id:
|
|
186
|
+
msg = "Cannot concatenate ToolMessageChunks with different names."
|
|
187
|
+
raise ValueError(msg)
|
|
188
|
+
|
|
189
|
+
return self.__class__(
|
|
190
|
+
tool_call_id=self.tool_call_id,
|
|
191
|
+
content=merge_content(self.content, other.content),
|
|
192
|
+
artifact=merge_obj(self.artifact, other.artifact),
|
|
193
|
+
additional_kwargs=merge_dicts(
|
|
194
|
+
self.additional_kwargs, other.additional_kwargs
|
|
195
|
+
),
|
|
196
|
+
response_metadata=merge_dicts(
|
|
197
|
+
self.response_metadata, other.response_metadata
|
|
198
|
+
),
|
|
199
|
+
id=self.id,
|
|
200
|
+
status=_merge_status(self.status, other.status),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return super().__add__(other)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class ToolCall(TypedDict):
|
|
207
|
+
"""Represents an AI's request to call a tool.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
```python
|
|
211
|
+
{"name": "foo", "args": {"a": 1}, "id": "123"}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This represents a request to call the tool named `'foo'` with arguments
|
|
215
|
+
`{"a": 1}` and an identifier of `'123'`.
|
|
216
|
+
|
|
217
|
+
!!! note "Factory function"
|
|
218
|
+
|
|
219
|
+
`tool_call` may also be used as a factory to create a `ToolCall`. Benefits
|
|
220
|
+
include:
|
|
221
|
+
|
|
222
|
+
* Required arguments strictly validated at creation time
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
name: str
|
|
226
|
+
"""The name of the tool to be called."""
|
|
227
|
+
|
|
228
|
+
args: dict[str, Any]
|
|
229
|
+
"""The arguments to the tool call as a dictionary."""
|
|
230
|
+
|
|
231
|
+
id: str | None
|
|
232
|
+
"""An identifier associated with the tool call.
|
|
233
|
+
|
|
234
|
+
An identifier is needed to associate a tool call request with a tool
|
|
235
|
+
call result in events when multiple concurrent tool calls are made.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
type: NotRequired[Literal["tool_call"]]
|
|
239
|
+
"""Used for discrimination."""
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def tool_call(
|
|
243
|
+
*,
|
|
244
|
+
name: str,
|
|
245
|
+
args: dict[str, Any],
|
|
246
|
+
id: str | None,
|
|
247
|
+
) -> ToolCall:
|
|
248
|
+
"""Create a tool call.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
name: The name of the tool to be called.
|
|
252
|
+
args: The arguments to the tool call as a dictionary.
|
|
253
|
+
id: An identifier associated with the tool call.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
The created tool call.
|
|
257
|
+
"""
|
|
258
|
+
return ToolCall(name=name, args=args, id=id, type="tool_call")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class ToolCallChunk(TypedDict):
|
|
262
|
+
"""A chunk of a tool call (yielded when streaming).
|
|
263
|
+
|
|
264
|
+
When merging `ToolCallChunk` objects (e.g., via `AIMessageChunk.__add__`), all
|
|
265
|
+
string attributes are concatenated. Chunks are only merged if their values of
|
|
266
|
+
`index` are equal and not `None`.
|
|
267
|
+
|
|
268
|
+
Example:
|
|
269
|
+
```python
|
|
270
|
+
left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)]
|
|
271
|
+
right_chunks = [ToolCallChunk(name=None, args="1}", index=0)]
|
|
272
|
+
|
|
273
|
+
(
|
|
274
|
+
AIMessageChunk(content="", tool_call_chunks=left_chunks)
|
|
275
|
+
+ AIMessageChunk(content="", tool_call_chunks=right_chunks)
|
|
276
|
+
).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)]
|
|
277
|
+
```
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
name: str | None
|
|
281
|
+
"""The name of the tool to be called."""
|
|
282
|
+
|
|
283
|
+
args: str | None
|
|
284
|
+
"""The arguments to the tool call as a JSON-parseable string."""
|
|
285
|
+
|
|
286
|
+
id: str | None
|
|
287
|
+
"""An identifier associated with the tool call.
|
|
288
|
+
|
|
289
|
+
An identifier is needed to associate a tool call request with a tool
|
|
290
|
+
call result in events when multiple concurrent tool calls are made.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
index: int | None
|
|
294
|
+
"""The index of the tool call in a sequence.
|
|
295
|
+
|
|
296
|
+
Used for merging chunks.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
type: NotRequired[Literal["tool_call_chunk"]]
|
|
300
|
+
"""Used for discrimination."""
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def tool_call_chunk(
|
|
304
|
+
*,
|
|
305
|
+
name: str | None = None,
|
|
306
|
+
args: str | None = None,
|
|
307
|
+
id: str | None = None,
|
|
308
|
+
index: int | None = None,
|
|
309
|
+
) -> ToolCallChunk:
|
|
310
|
+
"""Create a tool call chunk.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
name: The name of the tool to be called.
|
|
314
|
+
args: The arguments to the tool call as a JSON string.
|
|
315
|
+
id: An identifier associated with the tool call.
|
|
316
|
+
index: The index of the tool call in a sequence.
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
The created tool call chunk.
|
|
320
|
+
"""
|
|
321
|
+
return ToolCallChunk(
|
|
322
|
+
name=name, args=args, id=id, index=index, type="tool_call_chunk"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def invalid_tool_call(
|
|
327
|
+
*,
|
|
328
|
+
name: str | None = None,
|
|
329
|
+
args: str | None = None,
|
|
330
|
+
id: str | None = None,
|
|
331
|
+
error: str | None = None,
|
|
332
|
+
) -> InvalidToolCall:
|
|
333
|
+
"""Create an invalid tool call.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
name: The name of the tool to be called.
|
|
337
|
+
args: The arguments to the tool call as a JSON string.
|
|
338
|
+
id: An identifier associated with the tool call.
|
|
339
|
+
error: An error message associated with the tool call.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
The created invalid tool call.
|
|
343
|
+
"""
|
|
344
|
+
return InvalidToolCall(
|
|
345
|
+
name=name, args=args, id=id, error=error, type="invalid_tool_call"
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def default_tool_parser(
|
|
350
|
+
raw_tool_calls: list[dict[str, Any]],
|
|
351
|
+
) -> tuple[list[ToolCall], list[InvalidToolCall]]:
|
|
352
|
+
"""Best-effort parsing of tools.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
raw_tool_calls: List of raw tool call dicts to parse.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
A list of tool calls and invalid tool calls.
|
|
359
|
+
"""
|
|
360
|
+
tool_calls = []
|
|
361
|
+
invalid_tool_calls = []
|
|
362
|
+
for raw_tool_call in raw_tool_calls:
|
|
363
|
+
if "function" not in raw_tool_call:
|
|
364
|
+
continue
|
|
365
|
+
function_name = raw_tool_call["function"]["name"]
|
|
366
|
+
try:
|
|
367
|
+
function_args = json.loads(raw_tool_call["function"]["arguments"])
|
|
368
|
+
parsed = tool_call(
|
|
369
|
+
name=function_name or "",
|
|
370
|
+
args=function_args or {},
|
|
371
|
+
id=raw_tool_call.get("id"),
|
|
372
|
+
)
|
|
373
|
+
tool_calls.append(parsed)
|
|
374
|
+
except json.JSONDecodeError:
|
|
375
|
+
invalid_tool_calls.append(
|
|
376
|
+
invalid_tool_call(
|
|
377
|
+
name=function_name,
|
|
378
|
+
args=raw_tool_call["function"]["arguments"],
|
|
379
|
+
id=raw_tool_call.get("id"),
|
|
380
|
+
error=None,
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
return tool_calls, invalid_tool_calls
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def default_tool_chunk_parser(
|
|
387
|
+
raw_tool_calls: list[dict[str, Any]],
|
|
388
|
+
) -> list[ToolCallChunk]:
|
|
389
|
+
"""Best-effort parsing of tool chunks.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
raw_tool_calls: List of raw tool call dicts to parse.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
List of parsed ToolCallChunk objects.
|
|
396
|
+
"""
|
|
397
|
+
tool_call_chunks = []
|
|
398
|
+
for tool_call in raw_tool_calls:
|
|
399
|
+
if "function" not in tool_call:
|
|
400
|
+
function_args = None
|
|
401
|
+
function_name = None
|
|
402
|
+
else:
|
|
403
|
+
function_args = tool_call["function"]["arguments"]
|
|
404
|
+
function_name = tool_call["function"]["name"]
|
|
405
|
+
parsed = tool_call_chunk(
|
|
406
|
+
name=function_name,
|
|
407
|
+
args=function_args,
|
|
408
|
+
id=tool_call.get("id"),
|
|
409
|
+
index=tool_call.get("index"),
|
|
410
|
+
)
|
|
411
|
+
tool_call_chunks.append(parsed)
|
|
412
|
+
return tool_call_chunks
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _merge_status(
|
|
416
|
+
left: Literal["success", "error"], right: Literal["success", "error"]
|
|
417
|
+
) -> Literal["success", "error"]:
|
|
418
|
+
return "error" if "error" in {left, right} else "success"
|