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,383 @@
|
|
|
1
|
+
"""Parse tools for OpenAI tools output."""
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from json import JSONDecodeError
|
|
7
|
+
from typing import Annotated, Any
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, SkipValidation, ValidationError
|
|
10
|
+
from pydantic.v1 import BaseModel as BaseModelV1
|
|
11
|
+
|
|
12
|
+
from coco_core.exceptions import OutputParserException
|
|
13
|
+
from coco_core.messages import AIMessage, InvalidToolCall
|
|
14
|
+
from coco_core.messages.tool import invalid_tool_call
|
|
15
|
+
from coco_core.messages.tool import tool_call as create_tool_call
|
|
16
|
+
from coco_core.output_parsers.transform import BaseCumulativeTransformOutputParser
|
|
17
|
+
from coco_core.outputs import ChatGeneration, Generation
|
|
18
|
+
from coco_core.utils.json import parse_partial_json
|
|
19
|
+
from coco_core.utils.pydantic import (
|
|
20
|
+
TypeBaseModel,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def parse_tool_call(
|
|
27
|
+
raw_tool_call: dict[str, Any],
|
|
28
|
+
*,
|
|
29
|
+
partial: bool = False,
|
|
30
|
+
strict: bool = False,
|
|
31
|
+
return_id: bool = True,
|
|
32
|
+
) -> dict[str, Any] | None:
|
|
33
|
+
"""Parse a single tool call.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
raw_tool_call: The raw tool call to parse.
|
|
37
|
+
partial: Whether to parse partial JSON.
|
|
38
|
+
strict: Whether to allow non-JSON-compliant strings.
|
|
39
|
+
return_id: Whether to return the tool call id.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The parsed tool call.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
OutputParserException: If the tool call is not valid JSON.
|
|
46
|
+
"""
|
|
47
|
+
if "function" not in raw_tool_call:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
arguments = raw_tool_call["function"]["arguments"]
|
|
51
|
+
|
|
52
|
+
if partial:
|
|
53
|
+
try:
|
|
54
|
+
function_args = parse_partial_json(arguments, strict=strict)
|
|
55
|
+
except (JSONDecodeError, TypeError): # None args raise TypeError
|
|
56
|
+
return None
|
|
57
|
+
# Handle None or empty string arguments for parameter-less tools
|
|
58
|
+
elif not arguments:
|
|
59
|
+
function_args = {}
|
|
60
|
+
else:
|
|
61
|
+
try:
|
|
62
|
+
function_args = json.loads(arguments, strict=strict)
|
|
63
|
+
except JSONDecodeError as e:
|
|
64
|
+
msg = (
|
|
65
|
+
f"Function {raw_tool_call['function']['name']} arguments:\n\n"
|
|
66
|
+
f"{arguments}\n\nare not valid JSON. "
|
|
67
|
+
f"Received JSONDecodeError {e}"
|
|
68
|
+
)
|
|
69
|
+
raise OutputParserException(msg) from e
|
|
70
|
+
parsed = {
|
|
71
|
+
"name": raw_tool_call["function"]["name"] or "",
|
|
72
|
+
"args": function_args or {},
|
|
73
|
+
}
|
|
74
|
+
if return_id:
|
|
75
|
+
parsed["id"] = raw_tool_call.get("id")
|
|
76
|
+
parsed = create_tool_call(**parsed) # type: ignore[assignment,arg-type]
|
|
77
|
+
return parsed
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def make_invalid_tool_call(
|
|
81
|
+
raw_tool_call: dict[str, Any],
|
|
82
|
+
error_msg: str | None,
|
|
83
|
+
) -> InvalidToolCall:
|
|
84
|
+
"""Create an `InvalidToolCall` from a raw tool call.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
raw_tool_call: The raw tool call.
|
|
88
|
+
error_msg: The error message.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
An `InvalidToolCall` instance with the error message.
|
|
92
|
+
"""
|
|
93
|
+
return invalid_tool_call(
|
|
94
|
+
name=raw_tool_call["function"]["name"],
|
|
95
|
+
args=raw_tool_call["function"]["arguments"],
|
|
96
|
+
id=raw_tool_call.get("id"),
|
|
97
|
+
error=error_msg,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def parse_tool_calls(
|
|
102
|
+
raw_tool_calls: list[dict[str, Any]],
|
|
103
|
+
*,
|
|
104
|
+
partial: bool = False,
|
|
105
|
+
strict: bool = False,
|
|
106
|
+
return_id: bool = True,
|
|
107
|
+
) -> list[dict[str, Any]]:
|
|
108
|
+
"""Parse a list of tool calls.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
raw_tool_calls: The raw tool calls to parse.
|
|
112
|
+
partial: Whether to parse partial JSON.
|
|
113
|
+
strict: Whether to allow non-JSON-compliant strings.
|
|
114
|
+
return_id: Whether to return the tool call id.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
The parsed tool calls.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
OutputParserException: If any of the tool calls are not valid JSON.
|
|
121
|
+
"""
|
|
122
|
+
final_tools: list[dict[str, Any]] = []
|
|
123
|
+
exceptions = []
|
|
124
|
+
for tool_call in raw_tool_calls:
|
|
125
|
+
try:
|
|
126
|
+
parsed = parse_tool_call(
|
|
127
|
+
tool_call, partial=partial, strict=strict, return_id=return_id
|
|
128
|
+
)
|
|
129
|
+
if parsed:
|
|
130
|
+
final_tools.append(parsed)
|
|
131
|
+
except OutputParserException as e:
|
|
132
|
+
exceptions.append(str(e))
|
|
133
|
+
continue
|
|
134
|
+
if exceptions:
|
|
135
|
+
raise OutputParserException("\n\n".join(exceptions))
|
|
136
|
+
return final_tools
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class JsonOutputToolsParser(BaseCumulativeTransformOutputParser[Any]):
|
|
140
|
+
"""Parse tools from OpenAI response."""
|
|
141
|
+
|
|
142
|
+
strict: bool = False
|
|
143
|
+
"""Whether to allow non-JSON-compliant strings.
|
|
144
|
+
|
|
145
|
+
See: https://docs.python.org/3/library/json.html#encoders-and-decoders
|
|
146
|
+
|
|
147
|
+
Useful when the parsed output may include unicode characters or new lines.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
return_id: bool = False
|
|
151
|
+
"""Whether to return the tool call id."""
|
|
152
|
+
|
|
153
|
+
first_tool_only: bool = False
|
|
154
|
+
"""Whether to return only the first tool call.
|
|
155
|
+
|
|
156
|
+
If `False`, the result will be a list of tool calls, or an empty list if no tool
|
|
157
|
+
calls are found.
|
|
158
|
+
|
|
159
|
+
If `True`, and multiple tool calls are found, only the first one will be returned,
|
|
160
|
+
and the other tool calls will be ignored.
|
|
161
|
+
|
|
162
|
+
If no tool calls are found, `None` will be returned.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
|
|
166
|
+
"""Parse the result of an LLM call to a list of tool calls.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
result: The result of the LLM call.
|
|
170
|
+
partial: Whether to parse partial JSON.
|
|
171
|
+
|
|
172
|
+
If `True`, the output will be a JSON object containing
|
|
173
|
+
all the keys that have been returned so far.
|
|
174
|
+
|
|
175
|
+
If `False`, the output will be the full JSON object.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
The parsed tool calls.
|
|
179
|
+
|
|
180
|
+
Raises:
|
|
181
|
+
OutputParserException: If the output is not valid JSON.
|
|
182
|
+
"""
|
|
183
|
+
generation = result[0]
|
|
184
|
+
if not isinstance(generation, ChatGeneration):
|
|
185
|
+
msg = "This output parser can only be used with a chat generation."
|
|
186
|
+
raise OutputParserException(msg)
|
|
187
|
+
message = generation.message
|
|
188
|
+
if isinstance(message, AIMessage) and message.tool_calls:
|
|
189
|
+
tool_calls = [dict(tc) for tc in message.tool_calls]
|
|
190
|
+
for tool_call in tool_calls:
|
|
191
|
+
if not self.return_id:
|
|
192
|
+
_ = tool_call.pop("id")
|
|
193
|
+
else:
|
|
194
|
+
try:
|
|
195
|
+
raw_tool_calls = copy.deepcopy(message.additional_kwargs["tool_calls"])
|
|
196
|
+
except KeyError:
|
|
197
|
+
return []
|
|
198
|
+
tool_calls = parse_tool_calls(
|
|
199
|
+
raw_tool_calls,
|
|
200
|
+
partial=partial,
|
|
201
|
+
strict=self.strict,
|
|
202
|
+
return_id=self.return_id,
|
|
203
|
+
)
|
|
204
|
+
# for backwards compatibility
|
|
205
|
+
for tc in tool_calls:
|
|
206
|
+
tc["type"] = tc.pop("name")
|
|
207
|
+
|
|
208
|
+
if self.first_tool_only:
|
|
209
|
+
return tool_calls[0] if tool_calls else None
|
|
210
|
+
return tool_calls
|
|
211
|
+
|
|
212
|
+
def parse(self, text: str) -> Any:
|
|
213
|
+
"""Parse the output of an LLM call to a list of tool calls.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
text: The output of the LLM call.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
The parsed tool calls.
|
|
220
|
+
"""
|
|
221
|
+
raise NotImplementedError
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class JsonOutputKeyToolsParser(JsonOutputToolsParser):
|
|
225
|
+
"""Parse tools from OpenAI response."""
|
|
226
|
+
|
|
227
|
+
key_name: str
|
|
228
|
+
"""The type of tools to return."""
|
|
229
|
+
|
|
230
|
+
def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
|
|
231
|
+
"""Parse the result of an LLM call to a list of tool calls.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
result: The result of the LLM call.
|
|
235
|
+
partial: Whether to parse partial JSON.
|
|
236
|
+
If `True`, the output will be a JSON object containing
|
|
237
|
+
all the keys that have been returned so far.
|
|
238
|
+
If `False`, the output will be the full JSON object.
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
OutputParserException: If the generation is not a chat generation.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
The parsed tool calls.
|
|
245
|
+
"""
|
|
246
|
+
generation = result[0]
|
|
247
|
+
if not isinstance(generation, ChatGeneration):
|
|
248
|
+
msg = "This output parser can only be used with a chat generation."
|
|
249
|
+
raise OutputParserException(msg)
|
|
250
|
+
message = generation.message
|
|
251
|
+
if isinstance(message, AIMessage) and message.tool_calls:
|
|
252
|
+
parsed_tool_calls = [dict(tc) for tc in message.tool_calls]
|
|
253
|
+
for tool_call in parsed_tool_calls:
|
|
254
|
+
if not self.return_id:
|
|
255
|
+
_ = tool_call.pop("id")
|
|
256
|
+
else:
|
|
257
|
+
try:
|
|
258
|
+
# This exists purely for backward compatibility / cached messages
|
|
259
|
+
# All new messages should use `message.tool_calls`
|
|
260
|
+
raw_tool_calls = copy.deepcopy(message.additional_kwargs["tool_calls"])
|
|
261
|
+
except KeyError:
|
|
262
|
+
if self.first_tool_only:
|
|
263
|
+
return None
|
|
264
|
+
return []
|
|
265
|
+
parsed_tool_calls = parse_tool_calls(
|
|
266
|
+
raw_tool_calls,
|
|
267
|
+
partial=partial,
|
|
268
|
+
strict=self.strict,
|
|
269
|
+
return_id=self.return_id,
|
|
270
|
+
)
|
|
271
|
+
# For backwards compatibility
|
|
272
|
+
for tc in parsed_tool_calls:
|
|
273
|
+
tc["type"] = tc.pop("name")
|
|
274
|
+
if self.first_tool_only:
|
|
275
|
+
parsed_result = list(
|
|
276
|
+
filter(lambda x: x["type"] == self.key_name, parsed_tool_calls)
|
|
277
|
+
)
|
|
278
|
+
single_result = (
|
|
279
|
+
parsed_result[0]
|
|
280
|
+
if parsed_result and parsed_result[0]["type"] == self.key_name
|
|
281
|
+
else None
|
|
282
|
+
)
|
|
283
|
+
if self.return_id:
|
|
284
|
+
return single_result
|
|
285
|
+
if single_result:
|
|
286
|
+
return single_result["args"]
|
|
287
|
+
return None
|
|
288
|
+
return (
|
|
289
|
+
[res for res in parsed_tool_calls if res["type"] == self.key_name]
|
|
290
|
+
if self.return_id
|
|
291
|
+
else [
|
|
292
|
+
res["args"] for res in parsed_tool_calls if res["type"] == self.key_name
|
|
293
|
+
]
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# Common cause of ValidationError is truncated output due to max_tokens.
|
|
298
|
+
_MAX_TOKENS_ERROR = (
|
|
299
|
+
"Output parser received a `max_tokens` stop reason. "
|
|
300
|
+
"The output is likely incomplete—please increase `max_tokens` "
|
|
301
|
+
"or shorten your prompt."
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class PydanticToolsParser(JsonOutputToolsParser):
|
|
306
|
+
"""Parse tools from OpenAI response."""
|
|
307
|
+
|
|
308
|
+
tools: Annotated[list[TypeBaseModel], SkipValidation()]
|
|
309
|
+
"""The tools to parse."""
|
|
310
|
+
|
|
311
|
+
# TODO: Support more granular streaming of objects.
|
|
312
|
+
# Currently only streams once all Pydantic object fields are present.
|
|
313
|
+
def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
|
|
314
|
+
"""Parse the result of an LLM call to a list of Pydantic objects.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
result: The result of the LLM call.
|
|
318
|
+
partial: Whether to parse partial JSON.
|
|
319
|
+
|
|
320
|
+
If `True`, the output will be a JSON object containing all the keys that
|
|
321
|
+
have been returned so far.
|
|
322
|
+
|
|
323
|
+
If `False`, the output will be the full JSON object.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
The parsed Pydantic objects.
|
|
327
|
+
|
|
328
|
+
Raises:
|
|
329
|
+
ValueError: If the tool call arguments are not a dict.
|
|
330
|
+
ValidationError: If the tool call arguments do not conform to the Pydantic
|
|
331
|
+
model.
|
|
332
|
+
"""
|
|
333
|
+
json_results = super().parse_result(result, partial=partial)
|
|
334
|
+
if not json_results:
|
|
335
|
+
return None if self.first_tool_only else []
|
|
336
|
+
|
|
337
|
+
json_results = [json_results] if self.first_tool_only else json_results
|
|
338
|
+
name_dict_v2: dict[str, TypeBaseModel] = {
|
|
339
|
+
tool.model_config.get("title") or tool.__name__: tool
|
|
340
|
+
for tool in self.tools
|
|
341
|
+
if issubclass(tool, BaseModel)
|
|
342
|
+
}
|
|
343
|
+
name_dict_v1: dict[str, TypeBaseModel] = {
|
|
344
|
+
tool.__name__: tool for tool in self.tools if issubclass(tool, BaseModelV1)
|
|
345
|
+
}
|
|
346
|
+
name_dict: dict[str, TypeBaseModel] = {**name_dict_v2, **name_dict_v1}
|
|
347
|
+
pydantic_objects = []
|
|
348
|
+
for res in json_results:
|
|
349
|
+
if not isinstance(res["args"], dict):
|
|
350
|
+
if partial:
|
|
351
|
+
continue
|
|
352
|
+
msg = (
|
|
353
|
+
f"Tool arguments must be specified as a dict, received: "
|
|
354
|
+
f"{res['args']}"
|
|
355
|
+
)
|
|
356
|
+
raise ValueError(msg)
|
|
357
|
+
|
|
358
|
+
try:
|
|
359
|
+
tool = name_dict[res["type"]]
|
|
360
|
+
except KeyError as e:
|
|
361
|
+
available = ", ".join(name_dict.keys()) or "<no_tools>"
|
|
362
|
+
msg = (
|
|
363
|
+
f"Unknown tool type: {res['type']!r}. Available tools: {available}"
|
|
364
|
+
)
|
|
365
|
+
raise OutputParserException(msg) from e
|
|
366
|
+
|
|
367
|
+
try:
|
|
368
|
+
pydantic_objects.append(tool(**res["args"]))
|
|
369
|
+
except (ValidationError, ValueError):
|
|
370
|
+
if partial:
|
|
371
|
+
continue
|
|
372
|
+
has_max_tokens_stop_reason = any(
|
|
373
|
+
generation.message.response_metadata.get("stop_reason")
|
|
374
|
+
== "max_tokens"
|
|
375
|
+
for generation in result
|
|
376
|
+
if isinstance(generation, ChatGeneration)
|
|
377
|
+
)
|
|
378
|
+
if has_max_tokens_stop_reason:
|
|
379
|
+
logger.exception(_MAX_TOKENS_ERROR)
|
|
380
|
+
raise
|
|
381
|
+
if self.first_tool_only:
|
|
382
|
+
return pydantic_objects[0] if pydantic_objects else None
|
|
383
|
+
return pydantic_objects
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Output parsers using Pydantic."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Annotated, Any, Generic, Literal, overload
|
|
5
|
+
|
|
6
|
+
import pydantic
|
|
7
|
+
from pydantic import SkipValidation
|
|
8
|
+
from typing_extensions import override
|
|
9
|
+
|
|
10
|
+
from coco_core.exceptions import OutputParserException
|
|
11
|
+
from coco_core.output_parsers import JsonOutputParser
|
|
12
|
+
from coco_core.outputs import Generation
|
|
13
|
+
from coco_core.utils.pydantic import (
|
|
14
|
+
PydanticBaseModel,
|
|
15
|
+
TBaseModel,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PydanticOutputParser(JsonOutputParser, Generic[TBaseModel]):
|
|
20
|
+
"""Parse an output using a Pydantic model."""
|
|
21
|
+
|
|
22
|
+
pydantic_object: Annotated[type[TBaseModel], SkipValidation()]
|
|
23
|
+
"""The Pydantic model to parse."""
|
|
24
|
+
|
|
25
|
+
def _parse_obj(self, obj: Any) -> TBaseModel:
|
|
26
|
+
try:
|
|
27
|
+
if issubclass(self.pydantic_object, pydantic.BaseModel):
|
|
28
|
+
return self.pydantic_object.model_validate(obj)
|
|
29
|
+
if issubclass(self.pydantic_object, pydantic.v1.BaseModel):
|
|
30
|
+
return self.pydantic_object.parse_obj(obj)
|
|
31
|
+
msg = ( # type: ignore[unreachable]
|
|
32
|
+
"Unsupported model version for PydanticOutputParser: "
|
|
33
|
+
f"{self.pydantic_object.__class__}"
|
|
34
|
+
)
|
|
35
|
+
raise OutputParserException(msg)
|
|
36
|
+
except (pydantic.ValidationError, pydantic.v1.ValidationError) as e:
|
|
37
|
+
raise self._parser_exception(e, obj) from e
|
|
38
|
+
|
|
39
|
+
def _parser_exception(
|
|
40
|
+
self, e: Exception, json_object: Any
|
|
41
|
+
) -> OutputParserException:
|
|
42
|
+
json_string = json.dumps(json_object, ensure_ascii=False)
|
|
43
|
+
name = self.pydantic_object.__name__
|
|
44
|
+
msg = f"Failed to parse {name} from completion {json_string}. Got: {e}"
|
|
45
|
+
return OutputParserException(msg, llm_output=json_string)
|
|
46
|
+
|
|
47
|
+
@overload
|
|
48
|
+
def parse_result(
|
|
49
|
+
self, result: list[Generation], *, partial: Literal[False] = False
|
|
50
|
+
) -> TBaseModel: ...
|
|
51
|
+
|
|
52
|
+
@overload
|
|
53
|
+
def parse_result(
|
|
54
|
+
self, result: list[Generation], *, partial: bool = False
|
|
55
|
+
) -> TBaseModel | None: ...
|
|
56
|
+
|
|
57
|
+
def parse_result(
|
|
58
|
+
self, result: list[Generation], *, partial: bool = False
|
|
59
|
+
) -> TBaseModel | None:
|
|
60
|
+
"""Parse the result of an LLM call to a Pydantic object.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
result: The result of the LLM call.
|
|
64
|
+
partial: Whether to parse partial JSON objects.
|
|
65
|
+
|
|
66
|
+
If `True`, the output will be a JSON object containing all the keys that
|
|
67
|
+
have been returned so far.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
OutputParserException: If the result is not valid JSON or does not conform
|
|
71
|
+
to the Pydantic model.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
The parsed Pydantic object.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
json_object = super().parse_result(result, partial=partial)
|
|
78
|
+
return self._parse_obj(json_object)
|
|
79
|
+
except OutputParserException:
|
|
80
|
+
if partial:
|
|
81
|
+
return None
|
|
82
|
+
raise
|
|
83
|
+
|
|
84
|
+
def parse(self, text: str) -> TBaseModel:
|
|
85
|
+
"""Parse the output of an LLM call to a Pydantic object.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
text: The output of the LLM call.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
The parsed Pydantic object.
|
|
92
|
+
"""
|
|
93
|
+
return self.parse_result([Generation(text=text)])
|
|
94
|
+
|
|
95
|
+
def get_format_instructions(self) -> str:
|
|
96
|
+
"""Return the format instructions for the JSON output.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
The format instructions for the JSON output.
|
|
100
|
+
"""
|
|
101
|
+
# Copy schema to avoid altering original Pydantic schema.
|
|
102
|
+
schema = dict(self._get_schema(self.pydantic_object).items())
|
|
103
|
+
|
|
104
|
+
# Remove extraneous fields.
|
|
105
|
+
reduced_schema = schema
|
|
106
|
+
if "title" in reduced_schema:
|
|
107
|
+
del reduced_schema["title"]
|
|
108
|
+
if "type" in reduced_schema:
|
|
109
|
+
del reduced_schema["type"]
|
|
110
|
+
# Ensure json in context is well-formed with double quotes.
|
|
111
|
+
schema_str = json.dumps(reduced_schema, ensure_ascii=False)
|
|
112
|
+
|
|
113
|
+
return _PYDANTIC_FORMAT_INSTRUCTIONS.format(schema=schema_str)
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def _type(self) -> str:
|
|
117
|
+
return "pydantic"
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
@override
|
|
121
|
+
def OutputType(self) -> type[TBaseModel]:
|
|
122
|
+
"""Return the Pydantic model."""
|
|
123
|
+
return self.pydantic_object
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_PYDANTIC_FORMAT_INSTRUCTIONS = """The output should be formatted as a JSON instance that conforms to the JSON schema below.
|
|
127
|
+
|
|
128
|
+
As an example, for the schema {{"properties": {{"foo": {{"title": "Foo", "description": "a list of strings", "type": "array", "items": {{"type": "string"}}}}}}, "required": ["foo"]}}
|
|
129
|
+
the object {{"foo": ["bar", "baz"]}} is a well-formatted instance of the schema. The object {{"properties": {{"foo": ["bar", "baz"]}}}} is not well-formatted.
|
|
130
|
+
|
|
131
|
+
Here is the output schema:
|
|
132
|
+
```
|
|
133
|
+
{schema}
|
|
134
|
+
```""" # noqa: E501
|
|
135
|
+
|
|
136
|
+
# Re-exporting types for backwards compatibility
|
|
137
|
+
__all__ = [
|
|
138
|
+
"PydanticBaseModel",
|
|
139
|
+
"PydanticOutputParser",
|
|
140
|
+
"TBaseModel",
|
|
141
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""String output parser."""
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
5
|
+
from coco_core.output_parsers.transform import BaseTransformOutputParser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StrOutputParser(BaseTransformOutputParser[str]):
|
|
9
|
+
"""Extract text content from model outputs as a string.
|
|
10
|
+
|
|
11
|
+
Converts model outputs (such as `AIMessage` or `AIMessageChunk` objects) into plain
|
|
12
|
+
text strings. It's the simplest output parser and is useful when you need string
|
|
13
|
+
responses for downstream processing, display, or storage.
|
|
14
|
+
|
|
15
|
+
Supports streaming, yielding text chunks as they're generated by the model.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
```python
|
|
19
|
+
from coco_core.output_parsers import StrOutputParser
|
|
20
|
+
from coco_openai import ChatOpenAI
|
|
21
|
+
|
|
22
|
+
model = ChatOpenAI(model="gpt-5.5")
|
|
23
|
+
parser = StrOutputParser()
|
|
24
|
+
|
|
25
|
+
# Get string output from a model
|
|
26
|
+
message = model.invoke("Tell me a joke")
|
|
27
|
+
result = parser.invoke(message)
|
|
28
|
+
print(result) # plain string
|
|
29
|
+
|
|
30
|
+
# With streaming - use transform() to process a stream
|
|
31
|
+
stream = model.stream("Tell me a story")
|
|
32
|
+
for chunk in parser.transform(stream):
|
|
33
|
+
print(chunk, end="", flush=True)
|
|
34
|
+
```
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def is_lc_serializable(cls) -> bool:
|
|
39
|
+
"""`StrOutputParser` is serializable.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
`True`
|
|
43
|
+
"""
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def get_lc_namespace(cls) -> list[str]:
|
|
48
|
+
"""Get the namespace of the Coco object.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
`["coco", "schema", "output_parser"]`
|
|
52
|
+
"""
|
|
53
|
+
return ["coco", "schema", "output_parser"]
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def _type(self) -> str:
|
|
57
|
+
"""Return the output parser type for serialization."""
|
|
58
|
+
return "default"
|
|
59
|
+
|
|
60
|
+
@override
|
|
61
|
+
def parse(self, text: str) -> str:
|
|
62
|
+
"""Returns the input text with no changes."""
|
|
63
|
+
return text
|