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,276 @@
|
|
|
1
|
+
"""Anthropic prompt caching middleware.
|
|
2
|
+
|
|
3
|
+
Requires:
|
|
4
|
+
- `coco`: For agent middleware framework
|
|
5
|
+
- `trustcoco-anthropic`: For `ChatAnthropic` model (already a dependency)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Awaitable, Callable
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
from warnings import warn
|
|
13
|
+
|
|
14
|
+
from coco_core.messages import SystemMessage
|
|
15
|
+
from coco_core.tools import BaseTool
|
|
16
|
+
|
|
17
|
+
from coco_anthropic.chat_models import ChatAnthropic
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from coco.agents.middleware.types import (
|
|
21
|
+
AgentMiddleware,
|
|
22
|
+
ModelCallResult,
|
|
23
|
+
ModelRequest,
|
|
24
|
+
ModelResponse,
|
|
25
|
+
)
|
|
26
|
+
except ImportError as e:
|
|
27
|
+
msg = (
|
|
28
|
+
"AnthropicPromptCachingMiddleware requires 'coco' to be installed. "
|
|
29
|
+
"This middleware is designed for use with Coco agents. "
|
|
30
|
+
"Install it with: pip install coco"
|
|
31
|
+
)
|
|
32
|
+
raise ImportError(msg) from e
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_trace_policy() -> Any:
|
|
36
|
+
"""Use tracing optimizations when supported by the installed Coco."""
|
|
37
|
+
try:
|
|
38
|
+
from coco.agents.middleware.types import TracePolicy, omit_payload
|
|
39
|
+
except ImportError:
|
|
40
|
+
return None
|
|
41
|
+
return TracePolicy(process_inputs=omit_payload)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_TRACE_POLICY = _get_trace_policy()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AnthropicPromptCachingMiddleware(AgentMiddleware):
|
|
48
|
+
"""Prompt Caching Middleware.
|
|
49
|
+
|
|
50
|
+
Optimizes API usage by caching conversation prefixes for Anthropic models.
|
|
51
|
+
|
|
52
|
+
Requires both `coco` and `trustcoco-anthropic` packages to be installed.
|
|
53
|
+
|
|
54
|
+
The middleware tags stable agent content and passes `cache_control` through
|
|
55
|
+
`model_settings`:
|
|
56
|
+
|
|
57
|
+
- **System message**: Tags the last content block of the system message
|
|
58
|
+
with `cache_control` so static system prompt content is cached.
|
|
59
|
+
- **Tools**: Tags the last tool definition with `cache_control`. Because
|
|
60
|
+
tool definitions are sent as one contiguous block, a single trailing
|
|
61
|
+
breakpoint caches the entire tool set across turns.
|
|
62
|
+
- **`model_settings`**: Passes `cache_control` to the chat model. The chat
|
|
63
|
+
model/provider then applies the correct message-tail and
|
|
64
|
+
provider-specific behavior at request time.
|
|
65
|
+
|
|
66
|
+
Learn more about Anthropic prompt caching
|
|
67
|
+
[here](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
trace_policy = _TRACE_POLICY
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
type: Literal["ephemeral"] = "ephemeral", # noqa: A002
|
|
75
|
+
ttl: Literal["5m", "1h"] = "5m",
|
|
76
|
+
min_messages_to_cache: int = 0,
|
|
77
|
+
unsupported_model_behavior: Literal["ignore", "warn", "raise"] = "warn",
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Initialize the middleware with cache control settings.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
type: The type of cache to use, only `'ephemeral'` is supported.
|
|
83
|
+
ttl: The time to live for the cache, only `'5m'` and `'1h'` are
|
|
84
|
+
supported.
|
|
85
|
+
min_messages_to_cache: The minimum number of messages until the
|
|
86
|
+
cache is used.
|
|
87
|
+
unsupported_model_behavior: The behavior to take when an
|
|
88
|
+
unsupported model is used.
|
|
89
|
+
|
|
90
|
+
`'ignore'` will ignore the unsupported model and continue without
|
|
91
|
+
caching.
|
|
92
|
+
|
|
93
|
+
`'warn'` will warn the user and continue without caching.
|
|
94
|
+
|
|
95
|
+
`'raise'` will raise an error and stop the agent.
|
|
96
|
+
"""
|
|
97
|
+
self.type = type
|
|
98
|
+
self.ttl = ttl
|
|
99
|
+
self.min_messages_to_cache = min_messages_to_cache
|
|
100
|
+
self.unsupported_model_behavior = unsupported_model_behavior
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def _cache_control(self) -> dict[str, str]:
|
|
104
|
+
return {"type": self.type, "ttl": self.ttl}
|
|
105
|
+
|
|
106
|
+
def _should_apply_caching(self, request: ModelRequest) -> bool:
|
|
107
|
+
"""Check if caching should be applied to the request.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
request: The model request to check.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
`True` if caching should be applied, `False` otherwise.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
ValueError: If model is unsupported and behavior is set to `'raise'`.
|
|
117
|
+
"""
|
|
118
|
+
if not isinstance(request.model, ChatAnthropic):
|
|
119
|
+
msg = (
|
|
120
|
+
"AnthropicPromptCachingMiddleware caching middleware only supports "
|
|
121
|
+
f"Anthropic models, not instances of {type(request.model)}"
|
|
122
|
+
)
|
|
123
|
+
if self.unsupported_model_behavior == "raise":
|
|
124
|
+
raise ValueError(msg)
|
|
125
|
+
if self.unsupported_model_behavior == "warn":
|
|
126
|
+
warn(msg, stacklevel=3)
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
messages_count = (
|
|
130
|
+
len(request.messages) + 1
|
|
131
|
+
if request.system_message
|
|
132
|
+
else len(request.messages)
|
|
133
|
+
)
|
|
134
|
+
return messages_count >= self.min_messages_to_cache
|
|
135
|
+
|
|
136
|
+
def _apply_caching(self, request: ModelRequest) -> ModelRequest:
|
|
137
|
+
"""Apply cache control to system message, tools, and model settings.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
request: The model request to modify.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
New request with cache control applied.
|
|
144
|
+
"""
|
|
145
|
+
overrides: dict[str, Any] = {}
|
|
146
|
+
cache_control = self._cache_control
|
|
147
|
+
|
|
148
|
+
# Always set top-level `cache_control` on model settings. The Anthropic
|
|
149
|
+
# chat model translates the kwarg to the correct wire format for the
|
|
150
|
+
# active transport: direct API receives it as-is, while Bedrock has it
|
|
151
|
+
# expanded into a block-level breakpoint by `_get_request_payload`.
|
|
152
|
+
overrides["model_settings"] = {
|
|
153
|
+
**request.model_settings,
|
|
154
|
+
"cache_control": cache_control,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
system_message = _tag_system_message(request.system_message, cache_control)
|
|
158
|
+
if system_message is not request.system_message:
|
|
159
|
+
overrides["system_message"] = system_message
|
|
160
|
+
|
|
161
|
+
tools = _tag_tools(request.tools, cache_control)
|
|
162
|
+
if tools is not request.tools:
|
|
163
|
+
overrides["tools"] = tools
|
|
164
|
+
|
|
165
|
+
return request.override(**overrides)
|
|
166
|
+
|
|
167
|
+
def wrap_model_call(
|
|
168
|
+
self,
|
|
169
|
+
request: ModelRequest,
|
|
170
|
+
handler: Callable[[ModelRequest], ModelResponse],
|
|
171
|
+
) -> ModelCallResult:
|
|
172
|
+
"""Modify the model request to add cache control blocks.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
request: The model request to potentially modify.
|
|
176
|
+
handler: The handler to execute the model request.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
The model response from the handler.
|
|
180
|
+
"""
|
|
181
|
+
if not self._should_apply_caching(request):
|
|
182
|
+
return handler(request)
|
|
183
|
+
|
|
184
|
+
return handler(self._apply_caching(request))
|
|
185
|
+
|
|
186
|
+
async def awrap_model_call(
|
|
187
|
+
self,
|
|
188
|
+
request: ModelRequest,
|
|
189
|
+
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
|
|
190
|
+
) -> ModelCallResult:
|
|
191
|
+
"""Modify the model request to add cache control blocks (async version).
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
request: The model request to potentially modify.
|
|
195
|
+
handler: The async handler to execute the model request.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
The model response from the handler.
|
|
199
|
+
"""
|
|
200
|
+
if not self._should_apply_caching(request):
|
|
201
|
+
return await handler(request)
|
|
202
|
+
|
|
203
|
+
return await handler(self._apply_caching(request))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _tag_system_message(
|
|
207
|
+
system_message: Any,
|
|
208
|
+
cache_control: dict[str, str],
|
|
209
|
+
) -> Any:
|
|
210
|
+
"""Tag the last content block of a system message with cache_control.
|
|
211
|
+
|
|
212
|
+
Returns the original system_message unchanged if there are no blocks
|
|
213
|
+
to tag.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
system_message: The system message to tag.
|
|
217
|
+
cache_control: The cache control dict to apply.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
A new SystemMessage with cache_control on the last block, or the
|
|
221
|
+
original if no modification was needed.
|
|
222
|
+
"""
|
|
223
|
+
if system_message is None:
|
|
224
|
+
return system_message
|
|
225
|
+
|
|
226
|
+
content = system_message.content
|
|
227
|
+
if isinstance(content, str):
|
|
228
|
+
if not content:
|
|
229
|
+
return system_message
|
|
230
|
+
new_content: list[str | dict[str, Any]] = [
|
|
231
|
+
{"type": "text", "text": content, "cache_control": cache_control}
|
|
232
|
+
]
|
|
233
|
+
elif isinstance(content, list):
|
|
234
|
+
if not content:
|
|
235
|
+
return system_message
|
|
236
|
+
new_content = list(content)
|
|
237
|
+
last = new_content[-1]
|
|
238
|
+
base = last if isinstance(last, dict) else {}
|
|
239
|
+
new_content[-1] = {**base, "cache_control": cache_control}
|
|
240
|
+
else:
|
|
241
|
+
return system_message
|
|
242
|
+
|
|
243
|
+
return SystemMessage(content=new_content)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _tag_tools(
|
|
247
|
+
tools: list[Any] | None,
|
|
248
|
+
cache_control: dict[str, str],
|
|
249
|
+
) -> list[Any] | None:
|
|
250
|
+
"""Tag the last tool with cache_control via its extras dict.
|
|
251
|
+
|
|
252
|
+
Only the last tool is tagged to minimize the number of explicit cache
|
|
253
|
+
breakpoints (Anthropic limits these to 4 per request). Since tool
|
|
254
|
+
definitions are sent as a contiguous block, a single breakpoint on the
|
|
255
|
+
last tool caches the entire set.
|
|
256
|
+
|
|
257
|
+
Creates a copy of the last tool with cache_control added to extras,
|
|
258
|
+
without mutating the original.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
tools: The list of tools to tag.
|
|
262
|
+
cache_control: The cache control dict to apply.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
A new list with cache_control on the last tool's extras, or the
|
|
266
|
+
original if no tools are present.
|
|
267
|
+
"""
|
|
268
|
+
if not tools:
|
|
269
|
+
return tools
|
|
270
|
+
|
|
271
|
+
last = tools[-1]
|
|
272
|
+
if not isinstance(last, BaseTool):
|
|
273
|
+
return tools
|
|
274
|
+
|
|
275
|
+
new_extras = {**(last.extras or {}), "cache_control": cache_control}
|
|
276
|
+
return [*tools[:-1], last.model_copy(update={"extras": new_extras})]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Output parsers for Anthropic tool calls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
from coco_core.messages import AIMessage, ToolCall
|
|
8
|
+
from coco_core.messages.tool import tool_call
|
|
9
|
+
from coco_core.output_parsers import BaseGenerationOutputParser
|
|
10
|
+
from coco_core.outputs import ChatGeneration, Generation
|
|
11
|
+
from pydantic import BaseModel, ConfigDict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToolsOutputParser(BaseGenerationOutputParser):
|
|
15
|
+
"""Output parser for tool calls."""
|
|
16
|
+
|
|
17
|
+
first_tool_only: bool = False
|
|
18
|
+
"""Whether to return only the first tool call."""
|
|
19
|
+
args_only: bool = False
|
|
20
|
+
"""Whether to return only the arguments of the tool calls."""
|
|
21
|
+
pydantic_schemas: list[type[BaseModel]] | None = None
|
|
22
|
+
"""Pydantic schemas to parse tool calls into."""
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(
|
|
25
|
+
extra="forbid",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
|
|
29
|
+
"""Parse a list of candidate model Generations into a specific format.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
result: A list of `Generation` to be parsed. The Generations are assumed
|
|
33
|
+
to be different candidate outputs for a single model input.
|
|
34
|
+
partial: (Not used) Whether the result is a partial result. If `True`, the
|
|
35
|
+
parser may return a partial result, which may not be complete or valid.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Structured output.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
if not result or not isinstance(result[0], ChatGeneration):
|
|
42
|
+
return None if self.first_tool_only else []
|
|
43
|
+
message = cast("AIMessage", result[0].message)
|
|
44
|
+
tool_calls: list = [
|
|
45
|
+
dict(tc) for tc in _extract_tool_calls_from_message(message)
|
|
46
|
+
]
|
|
47
|
+
if isinstance(message.content, list):
|
|
48
|
+
# Map tool call id to index
|
|
49
|
+
id_to_index = {
|
|
50
|
+
block["id"]: i
|
|
51
|
+
for i, block in enumerate(message.content)
|
|
52
|
+
if isinstance(block, dict) and block["type"] == "tool_use"
|
|
53
|
+
}
|
|
54
|
+
tool_calls = [{**tc, "index": id_to_index[tc["id"]]} for tc in tool_calls]
|
|
55
|
+
if self.pydantic_schemas:
|
|
56
|
+
tool_calls = [self._pydantic_parse(tc) for tc in tool_calls]
|
|
57
|
+
elif self.args_only:
|
|
58
|
+
tool_calls = [tc["args"] for tc in tool_calls]
|
|
59
|
+
else:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
if self.first_tool_only:
|
|
63
|
+
return tool_calls[0] if tool_calls else None
|
|
64
|
+
return list(tool_calls)
|
|
65
|
+
|
|
66
|
+
def _pydantic_parse(self, tool_call: dict) -> BaseModel:
|
|
67
|
+
cls_ = {schema.__name__: schema for schema in self.pydantic_schemas or []}[
|
|
68
|
+
tool_call["name"]
|
|
69
|
+
]
|
|
70
|
+
return cls_(**tool_call["args"])
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _extract_tool_calls_from_message(message: AIMessage) -> list[ToolCall]:
|
|
74
|
+
"""Extract tool calls from a list of content blocks."""
|
|
75
|
+
if message.tool_calls:
|
|
76
|
+
return message.tool_calls
|
|
77
|
+
return extract_tool_calls(message.content)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def extract_tool_calls(content: str | list[str | dict[str, Any]]) -> list[ToolCall]:
|
|
81
|
+
"""Extract tool calls from a list of content blocks."""
|
|
82
|
+
if isinstance(content, list):
|
|
83
|
+
tool_calls = []
|
|
84
|
+
for block in content:
|
|
85
|
+
if isinstance(block, str):
|
|
86
|
+
continue
|
|
87
|
+
if block["type"] != "tool_use":
|
|
88
|
+
continue
|
|
89
|
+
tool_calls.append(
|
|
90
|
+
tool_call(name=block["name"], args=block["input"], id=block["id"]),
|
|
91
|
+
)
|
|
92
|
+
return tool_calls
|
|
93
|
+
return []
|
coco_anthropic/py.typed
ADDED
|
File without changes
|
coco_core/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""`trustcoco-core` defines the base abstractions for the Coco ecosystem.
|
|
2
|
+
|
|
3
|
+
The interfaces for core components like chat models, LLMs, vector stores, retrievers,
|
|
4
|
+
and more are defined here. The universal invocation protocol (Runnables) along with
|
|
5
|
+
a syntax for combining components are also defined here.
|
|
6
|
+
|
|
7
|
+
**No third-party integrations are defined here.** The dependencies are kept purposefully
|
|
8
|
+
very lightweight.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from coco_core._api import (
|
|
12
|
+
surface_coco_beta_warnings,
|
|
13
|
+
surface_coco_deprecation_warnings,
|
|
14
|
+
)
|
|
15
|
+
from coco_core.version import VERSION
|
|
16
|
+
|
|
17
|
+
__version__ = VERSION
|
|
18
|
+
|
|
19
|
+
surface_coco_deprecation_warnings()
|
|
20
|
+
surface_coco_beta_warnings()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Helper functions for managing the Coco API.
|
|
2
|
+
|
|
3
|
+
This module is only relevant for Coco developers, not for users.
|
|
4
|
+
|
|
5
|
+
!!! warning
|
|
6
|
+
|
|
7
|
+
This module and its submodules are for internal use only. Do not use them in your
|
|
8
|
+
own code. We may change the API at any time with no warning.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from coco_core._import_utils import import_attr
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from coco_core._api.beta_decorator import (
|
|
17
|
+
CocoBetaWarning,
|
|
18
|
+
beta,
|
|
19
|
+
suppress_coco_beta_warning,
|
|
20
|
+
surface_coco_beta_warnings,
|
|
21
|
+
)
|
|
22
|
+
from coco_core._api.deprecation import (
|
|
23
|
+
CocoDeprecationWarning,
|
|
24
|
+
deprecated,
|
|
25
|
+
suppress_coco_deprecation_warning,
|
|
26
|
+
surface_coco_deprecation_warnings,
|
|
27
|
+
warn_deprecated,
|
|
28
|
+
)
|
|
29
|
+
from coco_core._api.path import as_import_path, get_relative_path
|
|
30
|
+
|
|
31
|
+
__all__ = (
|
|
32
|
+
"CocoBetaWarning",
|
|
33
|
+
"CocoDeprecationWarning",
|
|
34
|
+
"as_import_path",
|
|
35
|
+
"beta",
|
|
36
|
+
"deprecated",
|
|
37
|
+
"get_relative_path",
|
|
38
|
+
"suppress_coco_beta_warning",
|
|
39
|
+
"suppress_coco_deprecation_warning",
|
|
40
|
+
"surface_coco_beta_warnings",
|
|
41
|
+
"surface_coco_deprecation_warnings",
|
|
42
|
+
"warn_deprecated",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
_dynamic_imports = {
|
|
46
|
+
"CocoBetaWarning": "beta_decorator",
|
|
47
|
+
"beta": "beta_decorator",
|
|
48
|
+
"suppress_coco_beta_warning": "beta_decorator",
|
|
49
|
+
"surface_coco_beta_warnings": "beta_decorator",
|
|
50
|
+
"as_import_path": "path",
|
|
51
|
+
"get_relative_path": "path",
|
|
52
|
+
"CocoDeprecationWarning": "deprecation",
|
|
53
|
+
"deprecated": "deprecation",
|
|
54
|
+
"surface_coco_deprecation_warnings": "deprecation",
|
|
55
|
+
"suppress_coco_deprecation_warning": "deprecation",
|
|
56
|
+
"warn_deprecated": "deprecation",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def __getattr__(attr_name: str) -> object:
|
|
61
|
+
"""Dynamically import and return an attribute from a submodule.
|
|
62
|
+
|
|
63
|
+
This function enables lazy loading of API functions from submodules, reducing
|
|
64
|
+
initial import time and circular dependency issues.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
attr_name: Name of the attribute to import.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
The imported attribute object.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
AttributeError: If the attribute is not a valid dynamic import.
|
|
74
|
+
"""
|
|
75
|
+
module_name = _dynamic_imports.get(attr_name)
|
|
76
|
+
result = import_attr(attr_name, module_name, __spec__.parent)
|
|
77
|
+
globals()[attr_name] = result
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def __dir__() -> list[str]:
|
|
82
|
+
"""Return a list of available attributes for this module.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of attribute names that can be imported from this module.
|
|
86
|
+
"""
|
|
87
|
+
return list(__all__)
|