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,123 @@
|
|
|
1
|
+
"""Callback handler that prints to std out."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from typing_extensions import override
|
|
8
|
+
|
|
9
|
+
from coco_core.callbacks.base import BaseCallbackHandler
|
|
10
|
+
from coco_core.utils import print_text
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from coco_core.agents import AgentAction, AgentFinish
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StdOutCallbackHandler(BaseCallbackHandler):
|
|
17
|
+
"""Callback handler that prints to std out."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, color: str | None = None) -> None:
|
|
20
|
+
"""Initialize callback handler.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
color: The color to use for the text.
|
|
24
|
+
"""
|
|
25
|
+
self.color = color
|
|
26
|
+
|
|
27
|
+
@override
|
|
28
|
+
def on_chain_start(
|
|
29
|
+
self, serialized: dict[str, Any], inputs: dict[str, Any], **kwargs: Any
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Print out that we are entering a chain.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
serialized: The serialized chain.
|
|
35
|
+
inputs: The inputs to the chain.
|
|
36
|
+
**kwargs: Additional keyword arguments.
|
|
37
|
+
"""
|
|
38
|
+
if "name" in kwargs:
|
|
39
|
+
name = kwargs["name"]
|
|
40
|
+
elif serialized:
|
|
41
|
+
name = serialized.get("name", serialized.get("id", ["<unknown>"])[-1])
|
|
42
|
+
else:
|
|
43
|
+
name = "<unknown>"
|
|
44
|
+
print(f"\n\n\033[1m> Entering new {name} chain...\033[0m") # noqa: T201
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
def on_chain_end(self, outputs: dict[str, Any], **kwargs: Any) -> None:
|
|
48
|
+
"""Print out that we finished a chain.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
outputs: The outputs of the chain.
|
|
52
|
+
**kwargs: Additional keyword arguments.
|
|
53
|
+
"""
|
|
54
|
+
print("\n\033[1m> Finished chain.\033[0m") # noqa: T201
|
|
55
|
+
|
|
56
|
+
@override
|
|
57
|
+
def on_agent_action(
|
|
58
|
+
self, action: AgentAction, color: str | None = None, **kwargs: Any
|
|
59
|
+
) -> Any:
|
|
60
|
+
"""Run on agent action.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
action: The agent action.
|
|
64
|
+
color: The color to use for the text.
|
|
65
|
+
**kwargs: Additional keyword arguments.
|
|
66
|
+
"""
|
|
67
|
+
print_text(action.log, color=color or self.color)
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
def on_tool_end(
|
|
71
|
+
self,
|
|
72
|
+
output: Any,
|
|
73
|
+
color: str | None = None,
|
|
74
|
+
observation_prefix: str | None = None,
|
|
75
|
+
llm_prefix: str | None = None,
|
|
76
|
+
**kwargs: Any,
|
|
77
|
+
) -> None:
|
|
78
|
+
"""If not the final action, print out observation.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
output: The output to print.
|
|
82
|
+
color: The color to use for the text.
|
|
83
|
+
observation_prefix: The observation prefix.
|
|
84
|
+
llm_prefix: The LLM prefix.
|
|
85
|
+
**kwargs: Additional keyword arguments.
|
|
86
|
+
"""
|
|
87
|
+
output = str(output)
|
|
88
|
+
if observation_prefix is not None:
|
|
89
|
+
print_text(f"\n{observation_prefix}")
|
|
90
|
+
print_text(output, color=color or self.color)
|
|
91
|
+
if llm_prefix is not None:
|
|
92
|
+
print_text(f"\n{llm_prefix}")
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
def on_text(
|
|
96
|
+
self,
|
|
97
|
+
text: str,
|
|
98
|
+
color: str | None = None,
|
|
99
|
+
end: str = "",
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Run when the agent ends.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
text: The text to print.
|
|
106
|
+
color: The color to use for the text.
|
|
107
|
+
end: The end character to use.
|
|
108
|
+
**kwargs: Additional keyword arguments.
|
|
109
|
+
"""
|
|
110
|
+
print_text(text, color=color or self.color, end=end)
|
|
111
|
+
|
|
112
|
+
@override
|
|
113
|
+
def on_agent_finish(
|
|
114
|
+
self, finish: AgentFinish, color: str | None = None, **kwargs: Any
|
|
115
|
+
) -> None:
|
|
116
|
+
"""Run on the agent end.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
finish: The agent finish.
|
|
120
|
+
color: The color to use for the text.
|
|
121
|
+
**kwargs: Additional keyword arguments.
|
|
122
|
+
"""
|
|
123
|
+
print_text(finish.log, color=color or self.color, end="\n")
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Callback Handler streams to stdout on new llm token."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from typing_extensions import override
|
|
9
|
+
|
|
10
|
+
from coco_core.callbacks.base import BaseCallbackHandler
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from coco_core.agents import AgentAction, AgentFinish
|
|
14
|
+
from coco_core.messages import BaseMessage
|
|
15
|
+
from coco_core.outputs import LLMResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StreamingStdOutCallbackHandler(BaseCallbackHandler):
|
|
19
|
+
"""Callback handler for streaming.
|
|
20
|
+
|
|
21
|
+
!!! warning "Only works with LLMs that support streaming."
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def on_llm_start(
|
|
25
|
+
self, serialized: dict[str, Any], prompts: list[str], **kwargs: Any
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Run when LLM starts running.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
serialized: The serialized LLM.
|
|
31
|
+
prompts: The prompts to run.
|
|
32
|
+
**kwargs: Additional keyword arguments.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def on_chat_model_start(
|
|
36
|
+
self,
|
|
37
|
+
serialized: dict[str, Any],
|
|
38
|
+
messages: list[list[BaseMessage]],
|
|
39
|
+
**kwargs: Any,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Run when LLM starts running.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
serialized: The serialized LLM.
|
|
45
|
+
messages: The messages to run.
|
|
46
|
+
**kwargs: Additional keyword arguments.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
@override
|
|
50
|
+
def on_llm_new_token(
|
|
51
|
+
self, token: str | list[str | dict[str, Any]], **kwargs: Any
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Run on new LLM token. Only available when streaming is enabled.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
token: The new token, or a list of content blocks.
|
|
57
|
+
**kwargs: Additional keyword arguments.
|
|
58
|
+
"""
|
|
59
|
+
sys.stdout.write(str(token))
|
|
60
|
+
sys.stdout.flush()
|
|
61
|
+
|
|
62
|
+
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
63
|
+
"""Run when LLM ends running.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
response: The response from the LLM.
|
|
67
|
+
**kwargs: Additional keyword arguments.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def on_llm_error(self, error: BaseException, **kwargs: Any) -> None:
|
|
71
|
+
"""Run when LLM errors.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
error: The error that occurred.
|
|
75
|
+
**kwargs: Additional keyword arguments.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def on_chain_start(
|
|
79
|
+
self, serialized: dict[str, Any], inputs: dict[str, Any], **kwargs: Any
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Run when a chain starts running.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
serialized: The serialized chain.
|
|
85
|
+
inputs: The inputs to the chain.
|
|
86
|
+
**kwargs: Additional keyword arguments.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def on_chain_end(self, outputs: dict[str, Any], **kwargs: Any) -> None:
|
|
90
|
+
"""Run when a chain ends running.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
outputs: The outputs of the chain.
|
|
94
|
+
**kwargs: Additional keyword arguments.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def on_chain_error(self, error: BaseException, **kwargs: Any) -> None:
|
|
98
|
+
"""Run when chain errors.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
error: The error that occurred.
|
|
102
|
+
**kwargs: Additional keyword arguments.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def on_tool_start(
|
|
106
|
+
self, serialized: dict[str, Any], input_str: str, **kwargs: Any
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Run when the tool starts running.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
serialized: The serialized tool.
|
|
112
|
+
input_str: The input string.
|
|
113
|
+
**kwargs: Additional keyword arguments.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
|
|
117
|
+
"""Run on agent action.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
action: The agent action.
|
|
121
|
+
**kwargs: Additional keyword arguments.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def on_tool_end(self, output: Any, **kwargs: Any) -> None:
|
|
125
|
+
"""Run when tool ends running.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
output: The output of the tool.
|
|
129
|
+
**kwargs: Additional keyword arguments.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def on_tool_error(self, error: BaseException, **kwargs: Any) -> None:
|
|
133
|
+
"""Run when tool errors.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
error: The error that occurred.
|
|
137
|
+
**kwargs: Additional keyword arguments.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def on_text(self, text: str, **kwargs: Any) -> None:
|
|
141
|
+
"""Run on an arbitrary text.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
text: The text to print.
|
|
145
|
+
**kwargs: Additional keyword arguments.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> None:
|
|
149
|
+
"""Run on the agent end.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
finish: The agent finish.
|
|
153
|
+
**kwargs: Additional keyword arguments.
|
|
154
|
+
"""
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Callback Handler that tracks `AIMessage.usage_metadata`."""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from collections.abc import Generator
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from contextvars import ContextVar
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from typing_extensions import override
|
|
10
|
+
|
|
11
|
+
from coco_core.callbacks import BaseCallbackHandler
|
|
12
|
+
from coco_core.messages import AIMessage
|
|
13
|
+
from coco_core.messages.ai import UsageMetadata, add_usage
|
|
14
|
+
from coco_core.outputs import ChatGeneration, LLMResult
|
|
15
|
+
from coco_core.tracers.context import register_configure_hook
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UsageMetadataCallbackHandler(BaseCallbackHandler):
|
|
19
|
+
"""Callback Handler that tracks `AIMessage.usage_metadata`.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
```python
|
|
23
|
+
from coco.chat_models import init_chat_model
|
|
24
|
+
from coco_core.callbacks import UsageMetadataCallbackHandler
|
|
25
|
+
|
|
26
|
+
llm_1 = init_chat_model(model="openai:gpt-5.5")
|
|
27
|
+
llm_2 = init_chat_model(model="anthropic:claude-haiku-4-5-20251001")
|
|
28
|
+
|
|
29
|
+
callback = UsageMetadataCallbackHandler()
|
|
30
|
+
result_1 = llm_1.invoke("Hello", config={"callbacks": [callback]})
|
|
31
|
+
result_2 = llm_2.invoke("Hello", config={"callbacks": [callback]})
|
|
32
|
+
callback.usage_metadata
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
!!! version-added "Added in `trustcoco-core` 0.3.49"
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self) -> None:
|
|
40
|
+
"""Initialize the `UsageMetadataCallbackHandler`."""
|
|
41
|
+
super().__init__()
|
|
42
|
+
self._lock = threading.Lock()
|
|
43
|
+
self.usage_metadata: dict[str, UsageMetadata] = {}
|
|
44
|
+
|
|
45
|
+
@override
|
|
46
|
+
def __repr__(self) -> str:
|
|
47
|
+
return str(self.usage_metadata)
|
|
48
|
+
|
|
49
|
+
@override
|
|
50
|
+
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
|
51
|
+
"""Collect token usage."""
|
|
52
|
+
# Check for usage_metadata (trustcoco-core >= 0.2.2)
|
|
53
|
+
try:
|
|
54
|
+
generation = response.generations[0][0]
|
|
55
|
+
except IndexError:
|
|
56
|
+
generation = None
|
|
57
|
+
|
|
58
|
+
usage_metadata = None
|
|
59
|
+
model_name = None
|
|
60
|
+
if isinstance(generation, ChatGeneration):
|
|
61
|
+
try:
|
|
62
|
+
message = generation.message
|
|
63
|
+
if isinstance(message, AIMessage):
|
|
64
|
+
usage_metadata = message.usage_metadata
|
|
65
|
+
model_name = message.response_metadata.get("model_name")
|
|
66
|
+
except AttributeError:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
# update shared state behind lock
|
|
70
|
+
if usage_metadata and model_name:
|
|
71
|
+
with self._lock:
|
|
72
|
+
if model_name not in self.usage_metadata:
|
|
73
|
+
self.usage_metadata[model_name] = usage_metadata
|
|
74
|
+
else:
|
|
75
|
+
self.usage_metadata[model_name] = add_usage(
|
|
76
|
+
self.usage_metadata[model_name], usage_metadata
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@contextmanager
|
|
81
|
+
def get_usage_metadata_callback(
|
|
82
|
+
name: str = "usage_metadata_callback",
|
|
83
|
+
) -> Generator[UsageMetadataCallbackHandler, None, None]:
|
|
84
|
+
"""Get usage metadata callback.
|
|
85
|
+
|
|
86
|
+
Get context manager for tracking usage metadata across chat model calls using
|
|
87
|
+
[`AIMessage.usage_metadata`][coco.messages.AIMessage.usage_metadata].
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
name: The name of the context variable.
|
|
91
|
+
|
|
92
|
+
Yields:
|
|
93
|
+
The usage metadata callback.
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
```python
|
|
97
|
+
from coco.chat_models import init_chat_model
|
|
98
|
+
from coco_core.callbacks import get_usage_metadata_callback
|
|
99
|
+
|
|
100
|
+
llm_1 = init_chat_model(model="openai:gpt-5.5")
|
|
101
|
+
llm_2 = init_chat_model(model="anthropic:claude-haiku-4-5-20251001")
|
|
102
|
+
|
|
103
|
+
with get_usage_metadata_callback() as cb:
|
|
104
|
+
llm_1.invoke("Hello")
|
|
105
|
+
llm_2.invoke("Hello")
|
|
106
|
+
print(cb.usage_metadata)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
!!! version-added "Added in `trustcoco-core` 0.3.49"
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
usage_metadata_callback_var: ContextVar[UsageMetadataCallbackHandler | None] = (
|
|
113
|
+
ContextVar(name, default=None)
|
|
114
|
+
)
|
|
115
|
+
register_configure_hook(usage_metadata_callback_var, inheritable=True)
|
|
116
|
+
cb = UsageMetadataCallbackHandler()
|
|
117
|
+
token = usage_metadata_callback_var.set(cb)
|
|
118
|
+
try:
|
|
119
|
+
yield cb
|
|
120
|
+
finally:
|
|
121
|
+
# Always clear the context var, including when the with-block raises.
|
|
122
|
+
# Without finally, post-block model calls keep accumulating into cb
|
|
123
|
+
# (see #38989).
|
|
124
|
+
usage_metadata_callback_var.reset(token)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Chat message history stores a history of the message interactions in a chat."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from coco_core._api import deprecated
|
|
11
|
+
from coco_core.messages import (
|
|
12
|
+
AIMessage,
|
|
13
|
+
BaseMessage,
|
|
14
|
+
HumanMessage,
|
|
15
|
+
get_buffer_string,
|
|
16
|
+
)
|
|
17
|
+
from coco_core.runnables.config import run_in_executor
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from collections.abc import Sequence
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@deprecated(
|
|
24
|
+
since="1.6.4",
|
|
25
|
+
removal="2.0.0",
|
|
26
|
+
addendum=(
|
|
27
|
+
"See the short-term memory documentation for recommended alternatives: "
|
|
28
|
+
"https://docs.langchain.com/oss/python/langchain/short-term-memory"
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
class BaseChatMessageHistory(ABC):
|
|
32
|
+
"""Abstract base class for storing chat message history.
|
|
33
|
+
|
|
34
|
+
Implementations guidelines:
|
|
35
|
+
|
|
36
|
+
Implementations are expected to over-ride all or some of the following methods:
|
|
37
|
+
|
|
38
|
+
* `add_messages`: sync variant for bulk addition of messages
|
|
39
|
+
* `aadd_messages`: async variant for bulk addition of messages
|
|
40
|
+
* `messages`: sync variant for getting messages
|
|
41
|
+
* `aget_messages`: async variant for getting messages
|
|
42
|
+
* `clear`: sync variant for clearing messages
|
|
43
|
+
* `aclear`: async variant for clearing messages
|
|
44
|
+
|
|
45
|
+
`add_messages` contains a default implementation that calls `add_message`
|
|
46
|
+
for each message in the sequence. This is provided for backwards compatibility
|
|
47
|
+
with existing implementations which only had `add_message`.
|
|
48
|
+
|
|
49
|
+
Async variants all have default implementations that call the sync variants.
|
|
50
|
+
Implementers can choose to override the async implementations to provide
|
|
51
|
+
truly async implementations.
|
|
52
|
+
|
|
53
|
+
Usage guidelines:
|
|
54
|
+
|
|
55
|
+
When used for updating history, users should favor usage of `add_messages`
|
|
56
|
+
over `add_message` or other variants like `add_user_message` and `add_ai_message`
|
|
57
|
+
to avoid unnecessary round-trips to the underlying persistence layer.
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
```python
|
|
61
|
+
import json
|
|
62
|
+
import os
|
|
63
|
+
from coco_core.messages import messages_from_dict, message_to_dict
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FileChatMessageHistory(BaseChatMessageHistory):
|
|
67
|
+
storage_path: str
|
|
68
|
+
session_id: str
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def messages(self) -> list[BaseMessage]:
|
|
72
|
+
try:
|
|
73
|
+
with open(
|
|
74
|
+
os.path.join(self.storage_path, self.session_id),
|
|
75
|
+
"r",
|
|
76
|
+
encoding="utf-8",
|
|
77
|
+
) as f:
|
|
78
|
+
messages_data = json.load(f)
|
|
79
|
+
return messages_from_dict(messages_data)
|
|
80
|
+
except FileNotFoundError:
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
def add_messages(self, messages: Sequence[BaseMessage]) -> None:
|
|
84
|
+
all_messages = list(self.messages) # Existing messages
|
|
85
|
+
all_messages.extend(messages) # Add new messages
|
|
86
|
+
|
|
87
|
+
serialized = [message_to_dict(message) for message in all_messages]
|
|
88
|
+
file_path = os.path.join(self.storage_path, self.session_id)
|
|
89
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
90
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
91
|
+
json.dump(serialized, f)
|
|
92
|
+
|
|
93
|
+
def clear(self) -> None:
|
|
94
|
+
file_path = os.path.join(self.storage_path, self.session_id)
|
|
95
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
96
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
97
|
+
json.dump([], f)
|
|
98
|
+
```
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
messages: list[BaseMessage]
|
|
102
|
+
"""A property or attribute that returns a list of messages.
|
|
103
|
+
|
|
104
|
+
In general, getting the messages may involve IO to the underlying persistence
|
|
105
|
+
layer, so this operation is expected to incur some latency.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
109
|
+
"""Initialize cooperatively to preserve multiple inheritance."""
|
|
110
|
+
super().__init__(*args, **kwargs)
|
|
111
|
+
|
|
112
|
+
async def aget_messages(self) -> list[BaseMessage]:
|
|
113
|
+
"""Async version of getting messages.
|
|
114
|
+
|
|
115
|
+
Can over-ride this method to provide an efficient async implementation.
|
|
116
|
+
|
|
117
|
+
In general, fetching messages may involve IO to the underlying persistence
|
|
118
|
+
layer.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
The messages.
|
|
122
|
+
"""
|
|
123
|
+
return await run_in_executor(None, lambda: self.messages)
|
|
124
|
+
|
|
125
|
+
def add_user_message(self, message: HumanMessage | str) -> None:
|
|
126
|
+
"""Convenience method for adding a human message string to the store.
|
|
127
|
+
|
|
128
|
+
!!! note
|
|
129
|
+
|
|
130
|
+
This is a convenience method. Code should favor the bulk `add_messages`
|
|
131
|
+
interface instead to save on round-trips to the persistence layer.
|
|
132
|
+
|
|
133
|
+
This method may be deprecated in a future release.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
message: The `HumanMessage` to add to the store.
|
|
137
|
+
"""
|
|
138
|
+
if isinstance(message, HumanMessage):
|
|
139
|
+
self.add_message(message)
|
|
140
|
+
else:
|
|
141
|
+
self.add_message(HumanMessage(content=message))
|
|
142
|
+
|
|
143
|
+
def add_ai_message(self, message: AIMessage | str) -> None:
|
|
144
|
+
"""Convenience method for adding an `AIMessage` string to the store.
|
|
145
|
+
|
|
146
|
+
!!! note
|
|
147
|
+
|
|
148
|
+
This is a convenience method. Code should favor the bulk `add_messages`
|
|
149
|
+
interface instead to save on round-trips to the persistence layer.
|
|
150
|
+
|
|
151
|
+
This method may be deprecated in a future release.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
message: The `AIMessage` to add.
|
|
155
|
+
"""
|
|
156
|
+
if isinstance(message, AIMessage):
|
|
157
|
+
self.add_message(message)
|
|
158
|
+
else:
|
|
159
|
+
self.add_message(AIMessage(content=message))
|
|
160
|
+
|
|
161
|
+
def add_message(self, message: BaseMessage) -> None:
|
|
162
|
+
"""Add a Message object to the store.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
message: A `BaseMessage` object to store.
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
NotImplementedError: If the sub-class has not implemented an efficient
|
|
169
|
+
`add_messages` method.
|
|
170
|
+
"""
|
|
171
|
+
if type(self).add_messages != BaseChatMessageHistory.add_messages:
|
|
172
|
+
# This means that the sub-class has implemented an efficient add_messages
|
|
173
|
+
# method, so we should use it.
|
|
174
|
+
self.add_messages([message])
|
|
175
|
+
else:
|
|
176
|
+
msg = (
|
|
177
|
+
"add_message is not implemented for this class. "
|
|
178
|
+
"Please implement add_message or add_messages."
|
|
179
|
+
)
|
|
180
|
+
raise NotImplementedError(msg)
|
|
181
|
+
|
|
182
|
+
def add_messages(self, messages: Sequence[BaseMessage]) -> None:
|
|
183
|
+
"""Add a list of messages.
|
|
184
|
+
|
|
185
|
+
Implementations should over-ride this method to handle bulk addition of messages
|
|
186
|
+
in an efficient manner to avoid unnecessary round-trips to the underlying store.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
messages: A sequence of `BaseMessage` objects to store.
|
|
190
|
+
"""
|
|
191
|
+
for message in messages:
|
|
192
|
+
self.add_message(message)
|
|
193
|
+
|
|
194
|
+
async def aadd_messages(self, messages: Sequence[BaseMessage]) -> None:
|
|
195
|
+
"""Async add a list of messages.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
messages: A sequence of `BaseMessage` objects to store.
|
|
199
|
+
"""
|
|
200
|
+
await run_in_executor(None, self.add_messages, messages)
|
|
201
|
+
|
|
202
|
+
@abstractmethod
|
|
203
|
+
def clear(self) -> None:
|
|
204
|
+
"""Remove all messages from the store."""
|
|
205
|
+
|
|
206
|
+
async def aclear(self) -> None:
|
|
207
|
+
"""Async remove all messages from the store."""
|
|
208
|
+
await run_in_executor(None, self.clear)
|
|
209
|
+
|
|
210
|
+
def __str__(self) -> str:
|
|
211
|
+
"""Return a string representation of the chat history."""
|
|
212
|
+
return get_buffer_string(self.messages)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@deprecated(
|
|
216
|
+
since="1.6.4",
|
|
217
|
+
removal="2.0.0",
|
|
218
|
+
addendum=(
|
|
219
|
+
"See the short-term memory documentation for recommended alternatives: "
|
|
220
|
+
"https://docs.langchain.com/oss/python/langchain/short-term-memory"
|
|
221
|
+
),
|
|
222
|
+
)
|
|
223
|
+
class InMemoryChatMessageHistory(BaseChatMessageHistory, BaseModel):
|
|
224
|
+
"""In memory implementation of chat message history.
|
|
225
|
+
|
|
226
|
+
Stores messages in a memory list.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
messages: list[BaseMessage] = Field(default_factory=list)
|
|
230
|
+
"""A list of messages stored in memory."""
|
|
231
|
+
|
|
232
|
+
async def aget_messages(self) -> list[BaseMessage]:
|
|
233
|
+
"""Async version of getting messages.
|
|
234
|
+
|
|
235
|
+
Can over-ride this method to provide an efficient async implementation.
|
|
236
|
+
|
|
237
|
+
In general, fetching messages may involve IO to the underlying persistence
|
|
238
|
+
layer.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
List of messages.
|
|
242
|
+
"""
|
|
243
|
+
return self.messages
|
|
244
|
+
|
|
245
|
+
def add_message(self, message: BaseMessage) -> None:
|
|
246
|
+
"""Add a self-created message to the store.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
message: The message to add.
|
|
250
|
+
"""
|
|
251
|
+
self.messages.append(message)
|
|
252
|
+
|
|
253
|
+
async def aadd_messages(self, messages: Sequence[BaseMessage]) -> None:
|
|
254
|
+
"""Async add messages to the store.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
messages: The messages to add.
|
|
258
|
+
"""
|
|
259
|
+
self.add_messages(messages)
|
|
260
|
+
|
|
261
|
+
def clear(self) -> None:
|
|
262
|
+
"""Clear all messages from the store."""
|
|
263
|
+
self.messages = []
|
|
264
|
+
|
|
265
|
+
async def aclear(self) -> None:
|
|
266
|
+
"""Async clear all messages from the store."""
|
|
267
|
+
self.clear()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Chat loaders."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from collections.abc import Iterator
|
|
5
|
+
|
|
6
|
+
from coco_core.chat_sessions import ChatSession
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseChatLoader(ABC):
|
|
10
|
+
"""Base class for chat loaders."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def lazy_load(self) -> Iterator[ChatSession]:
|
|
14
|
+
"""Lazy load the chat sessions.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
An iterator of chat sessions.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def load(self) -> list[ChatSession]:
|
|
21
|
+
"""Eagerly load the chat sessions into memory.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A list of chat sessions.
|
|
25
|
+
"""
|
|
26
|
+
return list(self.lazy_load())
|