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,18 @@
|
|
|
1
|
+
"""Embeddings models.
|
|
2
|
+
|
|
3
|
+
!!! warning "Modules moved"
|
|
4
|
+
|
|
5
|
+
With the release of `coco 1.0.0`, several embeddings modules were moved to
|
|
6
|
+
`trustcoco-classic`, such as `CacheBackedEmbeddings` and all community
|
|
7
|
+
embeddings. See [list](https://github.com/langchain-ai/langchain/blob/bdf1cd383ce36dc18381a3bf3fb0a579337a32b5/libs/langchain/langchain/embeddings/__init__.py)
|
|
8
|
+
of moved modules to inform your migration.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from coco_core.embeddings import Embeddings
|
|
12
|
+
|
|
13
|
+
from coco.embeddings.base import init_embeddings
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Embeddings",
|
|
17
|
+
"init_embeddings",
|
|
18
|
+
]
|
coco/embeddings/base.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""Factory functions for embeddings."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import importlib
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from coco_core.embeddings import Embeddings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _call(cls: type[Embeddings], **kwargs: Any) -> Embeddings:
|
|
12
|
+
return cls(**kwargs)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_BUILTIN_PROVIDERS: dict[str, tuple[str, str, Callable[..., Embeddings]]] = {
|
|
16
|
+
"azure_ai": ("coco_azure_ai.embeddings", "AzureAIOpenAIApiEmbeddingsModel", _call),
|
|
17
|
+
"azure_openai": ("coco_openai", "AzureOpenAIEmbeddings", _call),
|
|
18
|
+
"bedrock": (
|
|
19
|
+
"coco_aws",
|
|
20
|
+
"BedrockEmbeddings",
|
|
21
|
+
lambda cls, model, **kwargs: cls(model_id=model, **kwargs),
|
|
22
|
+
),
|
|
23
|
+
"cohere": ("coco_cohere", "CohereEmbeddings", _call),
|
|
24
|
+
"google_genai": ("coco_google_genai", "GoogleGenerativeAIEmbeddings", _call),
|
|
25
|
+
"google_vertexai": ("coco_google_vertexai", "VertexAIEmbeddings", _call),
|
|
26
|
+
"huggingface": (
|
|
27
|
+
"coco_huggingface",
|
|
28
|
+
"HuggingFaceEmbeddings",
|
|
29
|
+
lambda cls, model, **kwargs: cls(model_name=model, **kwargs),
|
|
30
|
+
),
|
|
31
|
+
"mistralai": ("coco_mistralai", "MistralAIEmbeddings", _call),
|
|
32
|
+
"ollama": ("coco_ollama", "OllamaEmbeddings", _call),
|
|
33
|
+
"openai": ("coco_openai", "OpenAIEmbeddings", _call),
|
|
34
|
+
}
|
|
35
|
+
"""Registry mapping provider names to their import configuration.
|
|
36
|
+
|
|
37
|
+
Each entry maps a provider key to a tuple of:
|
|
38
|
+
|
|
39
|
+
- `module_path`: The Python module path containing the embeddings class.
|
|
40
|
+
- `class_name`: The name of the embeddings class to import.
|
|
41
|
+
- `creator_func`: A callable that instantiates the class with provided kwargs.
|
|
42
|
+
|
|
43
|
+
!!! note
|
|
44
|
+
|
|
45
|
+
This dict is not exhaustive of all providers supported by Coco, but is
|
|
46
|
+
meant to cover the most popular ones and serve as a template for adding more
|
|
47
|
+
providers in the future. If a provider is not in this dict, it can still be
|
|
48
|
+
used with `init_chat_model` as long as its integration package is installed,
|
|
49
|
+
but the provider key will not be inferred from the model name and must be
|
|
50
|
+
specified explicitly via the `model_provider` parameter.
|
|
51
|
+
|
|
52
|
+
Refer to the Coco [integration documentation](https://docs.langchain.com/oss/python/integrations/providers/overview)
|
|
53
|
+
for a full list of supported providers and their corresponding packages.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@functools.lru_cache(maxsize=len(_BUILTIN_PROVIDERS))
|
|
58
|
+
def _get_embeddings_class_creator(provider: str) -> Callable[..., Embeddings]:
|
|
59
|
+
"""Return a factory function that creates an embeddings model for the given provider.
|
|
60
|
+
|
|
61
|
+
This function is cached to avoid repeated module imports.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
provider: The name of the model provider (e.g., `'openai'`, `'cohere'`).
|
|
65
|
+
|
|
66
|
+
Must be a key in `_BUILTIN_PROVIDERS`.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
A callable that accepts model kwargs and returns an `Embeddings` instance for
|
|
70
|
+
the specified provider.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ValueError: If the provider is not in `_BUILTIN_PROVIDERS`.
|
|
74
|
+
ImportError: If the provider's integration package is not installed.
|
|
75
|
+
"""
|
|
76
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
77
|
+
msg = (
|
|
78
|
+
f"Provider '{provider}' is not supported.\n"
|
|
79
|
+
f"Supported providers and their required packages:\n"
|
|
80
|
+
f"{_get_provider_list()}"
|
|
81
|
+
)
|
|
82
|
+
raise ValueError(msg)
|
|
83
|
+
|
|
84
|
+
module_name, class_name, creator_func = _BUILTIN_PROVIDERS[provider]
|
|
85
|
+
try:
|
|
86
|
+
module = importlib.import_module(module_name)
|
|
87
|
+
except ImportError as e:
|
|
88
|
+
top_level = module_name.split(".", maxsplit=1)[0]
|
|
89
|
+
pkg = top_level.replace("_", "-")
|
|
90
|
+
if top_level == "coco" or top_level.startswith("coco_"):
|
|
91
|
+
pkg = f"trust{pkg}"
|
|
92
|
+
msg = f"Could not import {pkg} python package. Please install it with `pip install {pkg}`"
|
|
93
|
+
raise ImportError(msg) from e
|
|
94
|
+
|
|
95
|
+
cls = getattr(module, class_name)
|
|
96
|
+
return functools.partial(creator_func, cls=cls)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _get_provider_list() -> str:
|
|
100
|
+
"""Get formatted list of providers and their packages."""
|
|
101
|
+
return "\n".join(
|
|
102
|
+
f" - {p}: {'trust' if pkg[0] == 'coco' or pkg[0].startswith('coco_') else ''}{pkg[0].replace('_', '-')}"
|
|
103
|
+
for p, pkg in _BUILTIN_PROVIDERS.items()
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _parse_model_string(model_name: str) -> tuple[str, str]:
|
|
108
|
+
"""Parse a model string into provider and model name components.
|
|
109
|
+
|
|
110
|
+
The model string should be in the format 'provider:model-name', where provider
|
|
111
|
+
is one of the supported providers.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
model_name: A model string in the format 'provider:model-name'
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
A tuple of (provider, model_name)
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```python
|
|
121
|
+
_parse_model_string("openai:text-embedding-3-small")
|
|
122
|
+
# Returns: ("openai", "text-embedding-3-small")
|
|
123
|
+
|
|
124
|
+
_parse_model_string("bedrock:amazon.titan-embed-text-v1")
|
|
125
|
+
# Returns: ("bedrock", "amazon.titan-embed-text-v1")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ValueError: If the model string is not in the correct format or
|
|
130
|
+
the provider is unsupported
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
if ":" not in model_name:
|
|
134
|
+
msg = (
|
|
135
|
+
f"Invalid model format '{model_name}'.\n"
|
|
136
|
+
f"Model name must be in format 'provider:model-name'\n"
|
|
137
|
+
f"Example valid model strings:\n"
|
|
138
|
+
f" - openai:text-embedding-3-small\n"
|
|
139
|
+
f" - bedrock:amazon.titan-embed-text-v1\n"
|
|
140
|
+
f" - cohere:embed-english-v3.0\n"
|
|
141
|
+
f"Supported providers: {_BUILTIN_PROVIDERS.keys()}"
|
|
142
|
+
)
|
|
143
|
+
raise ValueError(msg)
|
|
144
|
+
|
|
145
|
+
provider, model = model_name.split(":", 1)
|
|
146
|
+
provider = provider.lower().strip()
|
|
147
|
+
model = model.strip()
|
|
148
|
+
|
|
149
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
150
|
+
msg = (
|
|
151
|
+
f"Provider '{provider}' is not supported.\n"
|
|
152
|
+
f"Supported providers and their required packages:\n"
|
|
153
|
+
f"{_get_provider_list()}"
|
|
154
|
+
)
|
|
155
|
+
raise ValueError(msg)
|
|
156
|
+
if not model:
|
|
157
|
+
msg = "Model name cannot be empty"
|
|
158
|
+
raise ValueError(msg)
|
|
159
|
+
return provider, model
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _infer_model_and_provider(
|
|
163
|
+
model: str,
|
|
164
|
+
*,
|
|
165
|
+
provider: str | None = None,
|
|
166
|
+
) -> tuple[str, str]:
|
|
167
|
+
if not model.strip():
|
|
168
|
+
msg = "Model name cannot be empty"
|
|
169
|
+
raise ValueError(msg)
|
|
170
|
+
if provider is None and ":" in model:
|
|
171
|
+
provider, model_name = _parse_model_string(model)
|
|
172
|
+
else:
|
|
173
|
+
model_name = model
|
|
174
|
+
|
|
175
|
+
if not provider:
|
|
176
|
+
msg = (
|
|
177
|
+
"Must specify either:\n"
|
|
178
|
+
"1. A model string in format 'provider:model-name'\n"
|
|
179
|
+
" Example: 'openai:text-embedding-3-small'\n"
|
|
180
|
+
"2. Or explicitly set provider from: "
|
|
181
|
+
f"{_BUILTIN_PROVIDERS.keys()}"
|
|
182
|
+
)
|
|
183
|
+
raise ValueError(msg)
|
|
184
|
+
|
|
185
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
186
|
+
msg = (
|
|
187
|
+
f"Provider '{provider}' is not supported.\n"
|
|
188
|
+
f"Supported providers and their required packages:\n"
|
|
189
|
+
f"{_get_provider_list()}"
|
|
190
|
+
)
|
|
191
|
+
raise ValueError(msg)
|
|
192
|
+
return provider, model_name
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def init_embeddings(
|
|
196
|
+
model: str,
|
|
197
|
+
*,
|
|
198
|
+
provider: str | None = None,
|
|
199
|
+
**kwargs: Any,
|
|
200
|
+
) -> Embeddings:
|
|
201
|
+
"""Initialize an embedding model from a model name and optional provider.
|
|
202
|
+
|
|
203
|
+
!!! note
|
|
204
|
+
|
|
205
|
+
Requires the integration package for the chosen model provider to be installed.
|
|
206
|
+
|
|
207
|
+
See the `model_provider` parameter below for specific package names
|
|
208
|
+
(e.g., `pip install trustcoco-openai`).
|
|
209
|
+
|
|
210
|
+
Refer to the [provider integration's API reference](https://docs.langchain.com/oss/python/integrations/providers)
|
|
211
|
+
for supported model parameters to use as `**kwargs`.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
model: The name of the model, e.g. `'openai:text-embedding-3-small'`.
|
|
215
|
+
|
|
216
|
+
You can also specify model and model provider in a single argument using
|
|
217
|
+
`'{model_provider}:{model}'` format, e.g. `'openai:text-embedding-3-small'`.
|
|
218
|
+
provider: The model provider if not specified as part of the model arg
|
|
219
|
+
(see above).
|
|
220
|
+
|
|
221
|
+
Supported `provider` values and the corresponding integration package
|
|
222
|
+
are:
|
|
223
|
+
|
|
224
|
+
- `openai` -> [`trustcoco-openai`](https://docs.langchain.com/oss/python/integrations/providers/openai)
|
|
225
|
+
- `azure_ai` -> [`trustcoco-azure-ai`](https://docs.langchain.com/oss/python/integrations/providers/microsoft)
|
|
226
|
+
- `azure_openai` -> [`trustcoco-openai`](https://docs.langchain.com/oss/python/integrations/providers/openai)
|
|
227
|
+
- `bedrock` -> [`trustcoco-aws`](https://docs.langchain.com/oss/python/integrations/providers/aws)
|
|
228
|
+
- `cohere` -> [`trustcoco-cohere`](https://docs.langchain.com/oss/python/integrations/providers/cohere)
|
|
229
|
+
- `google_vertexai` -> [`trustcoco-google-vertexai`](https://docs.langchain.com/oss/python/integrations/providers/google)
|
|
230
|
+
- `huggingface` -> [`trustcoco-huggingface`](https://docs.langchain.com/oss/python/integrations/providers/huggingface)
|
|
231
|
+
- `mistralai` -> [`trustcoco-mistralai`](https://docs.langchain.com/oss/python/integrations/providers/mistralai)
|
|
232
|
+
- `ollama` -> [`trustcoco-ollama`](https://docs.langchain.com/oss/python/integrations/providers/ollama)
|
|
233
|
+
|
|
234
|
+
**kwargs: Additional model-specific parameters passed to the embedding model.
|
|
235
|
+
|
|
236
|
+
These vary by provider. Refer to the specific model provider's
|
|
237
|
+
[integration reference](https://reference.langchain.com/python/integrations/)
|
|
238
|
+
for all available parameters.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
An `Embeddings` instance that can generate embeddings for text.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
ValueError: If the model provider is not supported or cannot be determined
|
|
245
|
+
ImportError: If the required provider package is not installed
|
|
246
|
+
|
|
247
|
+
???+ example
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
# pip install coco trustcoco-openai
|
|
251
|
+
|
|
252
|
+
# Using a model string
|
|
253
|
+
model = init_embeddings("openai:text-embedding-3-small")
|
|
254
|
+
model.embed_query("Hello, world!")
|
|
255
|
+
|
|
256
|
+
# Using explicit provider
|
|
257
|
+
model = init_embeddings(model="text-embedding-3-small", provider="openai")
|
|
258
|
+
model.embed_documents(["Hello, world!", "Goodbye, world!"])
|
|
259
|
+
|
|
260
|
+
# With additional parameters
|
|
261
|
+
model = init_embeddings("openai:text-embedding-3-small", api_key="sk-...")
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
!!! version-added "Added in `coco` 0.3.9"
|
|
265
|
+
|
|
266
|
+
"""
|
|
267
|
+
if not model:
|
|
268
|
+
providers = _BUILTIN_PROVIDERS.keys()
|
|
269
|
+
msg = f"Must specify model name. Supported providers are: {', '.join(providers)}"
|
|
270
|
+
raise ValueError(msg)
|
|
271
|
+
|
|
272
|
+
provider, model_name = _infer_model_and_provider(model, provider=provider)
|
|
273
|
+
return _get_embeddings_class_creator(provider)(model=model_name, **kwargs)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
__all__ = [
|
|
277
|
+
"Embeddings", # This one is for backwards compatibility
|
|
278
|
+
"init_embeddings",
|
|
279
|
+
]
|
coco/mcp/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Coco MCP adapters for connecting MCP servers with Coco applications.
|
|
2
|
+
|
|
3
|
+
Interrupt-driven elicitation has its own types — the interrupt payload, the
|
|
4
|
+
answers a run resumes with, and the discriminator to recognize them by. Import
|
|
5
|
+
those from `coco.mcp.elicitation`.
|
|
6
|
+
|
|
7
|
+
!!! warning "This namespace is in beta"
|
|
8
|
+
|
|
9
|
+
`coco.mcp` is actively being worked on and its API may change. Importing
|
|
10
|
+
from it raises a `CocoBetaWarning` once per process. Silence it with
|
|
11
|
+
`warnings.filterwarnings("ignore", category=CocoBetaWarning)`, or scope
|
|
12
|
+
the suppression with `coco_core._api.suppress_coco_beta_warning()`.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
|
|
17
|
+
from coco_core._api import CocoBetaWarning
|
|
18
|
+
|
|
19
|
+
from coco.mcp.adapter import MCPAdapter
|
|
20
|
+
from coco.mcp.tools import MCPToolArtifact, as_coco_tool
|
|
21
|
+
|
|
22
|
+
# Warned on import rather than through `@beta`, which annotates a function or
|
|
23
|
+
# class and so only fires once something is called. The status belongs to the
|
|
24
|
+
# whole namespace, and a caller should learn it when they reach for it —
|
|
25
|
+
# including when they import a submodule directly, which runs this module first.
|
|
26
|
+
warnings.warn(
|
|
27
|
+
"`coco.mcp` is in beta. It is actively being worked on, so the API may change.",
|
|
28
|
+
CocoBetaWarning,
|
|
29
|
+
stacklevel=2,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"MCPAdapter",
|
|
34
|
+
"MCPToolArtifact",
|
|
35
|
+
"as_coco_tool",
|
|
36
|
+
]
|
coco/mcp/adapter.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Adapt MCP tools into Coco tools suitable for `create_agent`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Final, TypeAlias
|
|
7
|
+
|
|
8
|
+
from pydantic import AnyUrl, TypeAdapter, ValidationError
|
|
9
|
+
from typing_extensions import Self
|
|
10
|
+
|
|
11
|
+
from coco.mcp.elicitation import _arm_for_interrupts
|
|
12
|
+
from coco.mcp.tools import as_coco_tool
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from fastmcp.client import Client as FastMCPClient
|
|
16
|
+
from fastmcp.client.group import ClientGroup
|
|
17
|
+
from fastmcp.client.transports import ClientTransport
|
|
18
|
+
from fastmcp.mcp_config import MCPConfig
|
|
19
|
+
from mcp.server.mcpserver import MCPServer
|
|
20
|
+
except ImportError as _import_error:
|
|
21
|
+
msg = "Please install FastMCP to use `MCPAdapter` — `pip install fastmcp`."
|
|
22
|
+
raise ImportError(msg) from _import_error
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from types import TracebackType
|
|
27
|
+
|
|
28
|
+
from fastmcp import FastMCP
|
|
29
|
+
from coco_core.tools import BaseTool
|
|
30
|
+
|
|
31
|
+
# `CacheMode` is the MCP SDK's own type (SEP-2549); FastMCP re-imports it
|
|
32
|
+
# rather than defining its own, so this is the canonical source.
|
|
33
|
+
from mcp.client.caching import CacheMode
|
|
34
|
+
else:
|
|
35
|
+
try:
|
|
36
|
+
from fastmcp import FastMCP
|
|
37
|
+
except ImportError:
|
|
38
|
+
# In-process FastMCP servers, and multi-server `MCPConfig` targets, live
|
|
39
|
+
# in the server half of FastMCP. The `mcp` extra installs it, but a
|
|
40
|
+
# caller who reached this module through a client-only install
|
|
41
|
+
# (`fastmcp-slim[client]`) can still use every other target type — so
|
|
42
|
+
# the annotation degrades rather than the import failing, the same way
|
|
43
|
+
# FastMCP degrades it in `fastmcp.client.transports.inference`.
|
|
44
|
+
FastMCP = Any
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
MCPAdapterTarget: TypeAlias = (
|
|
48
|
+
FastMCPClient[Any]
|
|
49
|
+
| ClientGroup
|
|
50
|
+
| ClientTransport
|
|
51
|
+
| FastMCP
|
|
52
|
+
| MCPServer
|
|
53
|
+
| AnyUrl
|
|
54
|
+
| Path
|
|
55
|
+
| MCPConfig
|
|
56
|
+
| dict[str, Any]
|
|
57
|
+
| str
|
|
58
|
+
)
|
|
59
|
+
"""Anything `MCPAdapter` accepts as its `target`.
|
|
60
|
+
|
|
61
|
+
Every transport `fastmcp.Client` accepts, plus a pre-built `fastmcp.Client`.
|
|
62
|
+
|
|
63
|
+
A `str` target is the one member narrowed relative to `fastmcp.Client`: it must
|
|
64
|
+
be an `http`/`https` URL. Local servers are reached through `Path`, an explicit
|
|
65
|
+
transport, or `MCPConfig` — see `MCPAdapter`.
|
|
66
|
+
|
|
67
|
+
`ClientGroup` is the one member `fastmcp.Client` does not accept at all, since a
|
|
68
|
+
group is a peer of `Client` rather than a transport it could wrap.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
_URL_SCHEMES: Final = frozenset({"http", "https"})
|
|
73
|
+
"""Schemes a `str` target may carry.
|
|
74
|
+
|
|
75
|
+
The only schemes `fastmcp.Client` can reach a server over from a string, so
|
|
76
|
+
nothing that would have connected is forbidden here. It selects that branch on
|
|
77
|
+
a bare `str.startswith("http")` though, which also admits strings that are not
|
|
78
|
+
URLs at all — those fail on connect there, and on construction here.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
_ANY_URL: Final = TypeAdapter(AnyUrl)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _validate_url_target(target: str) -> None:
|
|
85
|
+
"""Reject a `str` target that is not an `http`/`https` URL.
|
|
86
|
+
|
|
87
|
+
`fastmcp.Client` infers a transport from a string by testing it as a
|
|
88
|
+
filesystem path *before* testing it as a URL, so a string naming an
|
|
89
|
+
existing `.py` or `.js` file launches that file as a subprocess. A string
|
|
90
|
+
that reaches an application from configuration, a request body, or a model
|
|
91
|
+
is expected to address a server over the network; letting it silently
|
|
92
|
+
select local execution instead makes the dangerous reading the implicit
|
|
93
|
+
one.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
target: The string passed as the adapter's target.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If `target` is not an `http` or `https` URL.
|
|
100
|
+
"""
|
|
101
|
+
hint = (
|
|
102
|
+
"To run a local MCP server over stdio, ask for it explicitly with "
|
|
103
|
+
"`Path('server.py')`, a `fastmcp` transport, or an `MCPConfig` — a bare "
|
|
104
|
+
"string is read as a URL so that it cannot silently become a subprocess."
|
|
105
|
+
)
|
|
106
|
+
try:
|
|
107
|
+
url = _ANY_URL.validate_python(target)
|
|
108
|
+
except ValidationError as validation_error:
|
|
109
|
+
msg = f"MCP target {target!r} is not a valid URL. {hint}"
|
|
110
|
+
raise ValueError(msg) from validation_error
|
|
111
|
+
|
|
112
|
+
# `AnyUrl` alone is not the boundary. It reads a Windows path such as
|
|
113
|
+
# `C:\server.py` as the scheme `c` and accepts it — and on Windows that
|
|
114
|
+
# path *does* resolve, so FastMCP would launch it. Schemes FastMCP cannot
|
|
115
|
+
# infer a transport from at all (`file:`, `ws:`) are refused here too, for
|
|
116
|
+
# an error that names the problem rather than "could not infer a transport".
|
|
117
|
+
if url.scheme not in _URL_SCHEMES:
|
|
118
|
+
msg = (
|
|
119
|
+
f"MCP target {target!r} has scheme {url.scheme!r}, but a string target must "
|
|
120
|
+
f"be an http or https URL. {hint}"
|
|
121
|
+
)
|
|
122
|
+
raise ValueError(msg)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class MCPAdapter:
|
|
126
|
+
"""Adapt an MCP target into Coco tools.
|
|
127
|
+
|
|
128
|
+
`MCPAdapter` uses FastMCP for protocol negotiation and connection management,
|
|
129
|
+
then converts discovered MCP tools into asynchronous Coco tools. The
|
|
130
|
+
resulting tools can be passed directly to `create_agent`.
|
|
131
|
+
|
|
132
|
+
Transport inference is delegated to `fastmcp.Client`, so a target may be a URL,
|
|
133
|
+
a local script path (launched over stdio), an in-process server, or an already
|
|
134
|
+
constructed client.
|
|
135
|
+
|
|
136
|
+
!!! warning "A string target must be an http(s) URL"
|
|
137
|
+
|
|
138
|
+
`fastmcp.Client` resolves a string by testing it as a filesystem path
|
|
139
|
+
before testing it as a URL, so a string naming an existing `.py` or `.js`
|
|
140
|
+
file launches that file as a subprocess. Because strings are the form a
|
|
141
|
+
target most often arrives in from configuration or from a model,
|
|
142
|
+
`MCPAdapter` rejects one that is not an `http` or `https` URL rather than
|
|
143
|
+
let it select local execution. Reach a local server through `Path`, a
|
|
144
|
+
`fastmcp` transport, or an `MCPConfig`, all of which say so explicitly.
|
|
145
|
+
|
|
146
|
+
A server that needs input mid-call is answered with a CocoGraph
|
|
147
|
+
`interrupt()`, so a human answers and the run resumes — see
|
|
148
|
+
`coco.mcp.elicitation`. This is the default: the adapter arms every
|
|
149
|
+
client it builds to advertise the elicitation capability and drives the
|
|
150
|
+
interrupt loop on each call. A server that never asks for input is
|
|
151
|
+
unaffected, since the loop only runs when the server returns a request.
|
|
152
|
+
|
|
153
|
+
A pre-built client (or `ClientGroup`) that already carries its own
|
|
154
|
+
elicitation handler is honored rather than overridden: its handler keeps
|
|
155
|
+
answering, and the adapter leaves the client as the caller built it. Only a
|
|
156
|
+
client with no handler is armed, and it is cloned first so the caller's own
|
|
157
|
+
object is never mutated.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
target: MCP target accepted by `fastmcp.Client`, including an existing
|
|
161
|
+
FastMCP client. A `str` must be an `http`/`https` URL.
|
|
162
|
+
|
|
163
|
+
Example:
|
|
164
|
+
```python
|
|
165
|
+
from coco.agents import create_agent
|
|
166
|
+
from coco.mcp import MCPAdapter
|
|
167
|
+
|
|
168
|
+
async with MCPAdapter("https://example.com/mcp") as adapter:
|
|
169
|
+
agent = create_agent("anthropic:claude-sonnet-5", await adapter.list_tools())
|
|
170
|
+
result = await agent.ainvoke({"messages": [{"role": "user", "content": "..."}]})
|
|
171
|
+
```
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def __init__(self, target: MCPAdapterTarget) -> None:
|
|
175
|
+
"""Initialize the adapter around a FastMCP target, client, or group.
|
|
176
|
+
|
|
177
|
+
Each underlying client is armed to answer elicitation with an interrupt,
|
|
178
|
+
unless it already carries the caller's own handler, which is honored
|
|
179
|
+
instead. A caller's client is cloned rather than mutated. See the class
|
|
180
|
+
docstring.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def armed(client: FastMCPClient[Any]) -> FastMCPClient[Any]:
|
|
184
|
+
if getattr(client, "_elicitation_callback", None) is not None:
|
|
185
|
+
return client
|
|
186
|
+
clone = client.new()
|
|
187
|
+
_arm_for_interrupts(clone)
|
|
188
|
+
return clone
|
|
189
|
+
|
|
190
|
+
if isinstance(target, ClientGroup):
|
|
191
|
+
members = {name: armed(client) for name, client in target.clients.items()}
|
|
192
|
+
self._client: FastMCPClient[Any] | ClientGroup = ClientGroup(members)
|
|
193
|
+
elif isinstance(target, FastMCPClient):
|
|
194
|
+
self._client = armed(target)
|
|
195
|
+
else:
|
|
196
|
+
if isinstance(target, str):
|
|
197
|
+
_validate_url_target(target)
|
|
198
|
+
client = FastMCPClient(target)
|
|
199
|
+
_arm_for_interrupts(client)
|
|
200
|
+
self._client = client
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def client(self) -> FastMCPClient[Any] | ClientGroup:
|
|
204
|
+
"""Return the underlying FastMCP client or group for advanced MCP operations."""
|
|
205
|
+
return self._client
|
|
206
|
+
|
|
207
|
+
async def __aenter__(self) -> Self:
|
|
208
|
+
"""Connect the underlying client or group for the duration of the context."""
|
|
209
|
+
await self._client.__aenter__()
|
|
210
|
+
return self
|
|
211
|
+
|
|
212
|
+
async def __aexit__(
|
|
213
|
+
self,
|
|
214
|
+
exc_type: type[BaseException] | None,
|
|
215
|
+
exc_value: BaseException | None,
|
|
216
|
+
traceback: TracebackType | None,
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Release this context's hold on the underlying client or group."""
|
|
219
|
+
await self._client.__aexit__(exc_type, exc_value, traceback)
|
|
220
|
+
|
|
221
|
+
async def list_tools(self, *, cache_mode: CacheMode = "use") -> list[BaseTool]:
|
|
222
|
+
"""Discover and adapt MCP tools for use with Coco.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
cache_mode: How discovery interacts with the client-side response
|
|
226
|
+
cache (SEP-2549). `use` serves a cached tool list when one is
|
|
227
|
+
present and still within the server's TTL hint, `refresh` calls
|
|
228
|
+
the server and repopulates the cache, and `bypass` skips the
|
|
229
|
+
cache entirely. The cache and its per-principal isolation are
|
|
230
|
+
configured on the client itself (`Client(cache=...)`); this only
|
|
231
|
+
selects how discovery reads it. Defaults to `use` so a
|
|
232
|
+
configured cache is honored — note this differs from a bare
|
|
233
|
+
`ClientGroup.list_tools()`, whose own default is `refresh`.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Coco tools that invoke the corresponding MCP tools
|
|
237
|
+
asynchronously. Each holds the adapter's client, so the tools
|
|
238
|
+
stay callable after this adapter's context exits.
|
|
239
|
+
"""
|
|
240
|
+
async with self:
|
|
241
|
+
remote_tools = await self._client.list_tools(cache_mode=cache_mode)
|
|
242
|
+
return [await as_coco_tool(tool, self._client) for tool in remote_tools]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
__all__ = ["MCPAdapter", "MCPAdapterTarget"]
|