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,137 @@
|
|
|
1
|
+
"""Fake LLMs for testing purposes."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import AsyncIterator, Iterator, Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from typing_extensions import override
|
|
9
|
+
|
|
10
|
+
from coco_core.callbacks import (
|
|
11
|
+
AsyncCallbackManagerForLLMRun,
|
|
12
|
+
CallbackManagerForLLMRun,
|
|
13
|
+
)
|
|
14
|
+
from coco_core.language_models import LanguageModelInput
|
|
15
|
+
from coco_core.language_models.llms import LLM
|
|
16
|
+
from coco_core.runnables import RunnableConfig
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FakeListLLM(LLM):
|
|
20
|
+
"""Fake LLM for testing purposes."""
|
|
21
|
+
|
|
22
|
+
responses: list[str]
|
|
23
|
+
"""List of responses to return in order."""
|
|
24
|
+
# This parameter should be removed from FakeListLLM since
|
|
25
|
+
# it's only used by sub-classes.
|
|
26
|
+
sleep: float | None = None
|
|
27
|
+
"""Sleep time in seconds between responses.
|
|
28
|
+
|
|
29
|
+
Ignored by FakeListLLM, but used by sub-classes.
|
|
30
|
+
"""
|
|
31
|
+
i: int = 0
|
|
32
|
+
"""Internally incremented after every model invocation.
|
|
33
|
+
|
|
34
|
+
Useful primarily for testing purposes.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
@override
|
|
39
|
+
def _llm_type(self) -> str:
|
|
40
|
+
"""Return type of llm."""
|
|
41
|
+
return "fake-list"
|
|
42
|
+
|
|
43
|
+
@override
|
|
44
|
+
def _call(
|
|
45
|
+
self,
|
|
46
|
+
prompt: str,
|
|
47
|
+
stop: list[str] | None = None,
|
|
48
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
49
|
+
**kwargs: Any,
|
|
50
|
+
) -> str:
|
|
51
|
+
"""Return next response."""
|
|
52
|
+
response = self.responses[self.i]
|
|
53
|
+
if self.i < len(self.responses) - 1:
|
|
54
|
+
self.i += 1
|
|
55
|
+
else:
|
|
56
|
+
self.i = 0
|
|
57
|
+
return response
|
|
58
|
+
|
|
59
|
+
@override
|
|
60
|
+
async def _acall(
|
|
61
|
+
self,
|
|
62
|
+
prompt: str,
|
|
63
|
+
stop: list[str] | None = None,
|
|
64
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
65
|
+
**kwargs: Any,
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Return next response."""
|
|
68
|
+
response = self.responses[self.i]
|
|
69
|
+
if self.i < len(self.responses) - 1:
|
|
70
|
+
self.i += 1
|
|
71
|
+
else:
|
|
72
|
+
self.i = 0
|
|
73
|
+
return response
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
@override
|
|
77
|
+
def _identifying_params(self) -> Mapping[str, Any]:
|
|
78
|
+
return {"responses": self.responses}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class FakeListLLMError(Exception):
|
|
82
|
+
"""Fake error for testing purposes."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class FakeStreamingListLLM(FakeListLLM):
|
|
86
|
+
"""Fake streaming list LLM for testing purposes.
|
|
87
|
+
|
|
88
|
+
An LLM that will return responses from a list in order.
|
|
89
|
+
|
|
90
|
+
This model also supports optionally sleeping between successive
|
|
91
|
+
chunks in a streaming implementation.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
error_on_chunk_number: int | None = None
|
|
95
|
+
"""If set, will raise an exception on the specified chunk number."""
|
|
96
|
+
|
|
97
|
+
@override
|
|
98
|
+
def stream(
|
|
99
|
+
self,
|
|
100
|
+
input: LanguageModelInput,
|
|
101
|
+
config: RunnableConfig | None = None,
|
|
102
|
+
*,
|
|
103
|
+
stop: list[str] | None = None,
|
|
104
|
+
**kwargs: Any,
|
|
105
|
+
) -> Iterator[str]:
|
|
106
|
+
result = self.invoke(input, config)
|
|
107
|
+
for i_c, c in enumerate(result):
|
|
108
|
+
if self.sleep is not None:
|
|
109
|
+
time.sleep(self.sleep)
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
self.error_on_chunk_number is not None
|
|
113
|
+
and i_c == self.error_on_chunk_number
|
|
114
|
+
):
|
|
115
|
+
raise FakeListLLMError
|
|
116
|
+
yield c
|
|
117
|
+
|
|
118
|
+
@override
|
|
119
|
+
async def astream(
|
|
120
|
+
self,
|
|
121
|
+
input: LanguageModelInput,
|
|
122
|
+
config: RunnableConfig | None = None,
|
|
123
|
+
*,
|
|
124
|
+
stop: list[str] | None = None,
|
|
125
|
+
**kwargs: Any,
|
|
126
|
+
) -> AsyncIterator[str]:
|
|
127
|
+
result = await self.ainvoke(input, config)
|
|
128
|
+
for i_c, c in enumerate(result):
|
|
129
|
+
if self.sleep is not None:
|
|
130
|
+
await asyncio.sleep(self.sleep)
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
self.error_on_chunk_number is not None
|
|
134
|
+
and i_c == self.error_on_chunk_number
|
|
135
|
+
):
|
|
136
|
+
raise FakeListLLMError
|
|
137
|
+
yield c
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""Fake chat models for testing purposes."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import re
|
|
5
|
+
import time
|
|
6
|
+
from collections.abc import AsyncIterator, Iterator
|
|
7
|
+
from typing import Any, Literal, cast
|
|
8
|
+
|
|
9
|
+
from typing_extensions import override
|
|
10
|
+
|
|
11
|
+
from coco_core.callbacks import (
|
|
12
|
+
AsyncCallbackManagerForLLMRun,
|
|
13
|
+
CallbackManagerForLLMRun,
|
|
14
|
+
)
|
|
15
|
+
from coco_core.language_models.chat_models import BaseChatModel, SimpleChatModel
|
|
16
|
+
from coco_core.messages import AIMessage, AIMessageChunk, BaseMessage
|
|
17
|
+
from coco_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
|
18
|
+
from coco_core.runnables import RunnableConfig
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FakeMessagesListChatModel(BaseChatModel):
|
|
22
|
+
"""Fake chat model for testing purposes."""
|
|
23
|
+
|
|
24
|
+
responses: list[BaseMessage]
|
|
25
|
+
"""List of responses to **cycle** through in order."""
|
|
26
|
+
sleep: float | None = None
|
|
27
|
+
"""Sleep time in seconds between responses."""
|
|
28
|
+
i: int = 0
|
|
29
|
+
"""Internally incremented after every model invocation."""
|
|
30
|
+
|
|
31
|
+
@override
|
|
32
|
+
def _generate(
|
|
33
|
+
self,
|
|
34
|
+
messages: list[BaseMessage],
|
|
35
|
+
stop: list[str] | None = None,
|
|
36
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
37
|
+
**kwargs: Any,
|
|
38
|
+
) -> ChatResult:
|
|
39
|
+
if self.sleep is not None:
|
|
40
|
+
time.sleep(self.sleep)
|
|
41
|
+
response = self.responses[self.i]
|
|
42
|
+
if self.i < len(self.responses) - 1:
|
|
43
|
+
self.i += 1
|
|
44
|
+
else:
|
|
45
|
+
self.i = 0
|
|
46
|
+
generation = ChatGeneration(message=response)
|
|
47
|
+
return ChatResult(generations=[generation])
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
@override
|
|
51
|
+
def _llm_type(self) -> str:
|
|
52
|
+
return "fake-messages-list-chat-model"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FakeListChatModelError(Exception):
|
|
56
|
+
"""Fake error for testing purposes."""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class FakeListChatModel(SimpleChatModel):
|
|
60
|
+
"""Fake chat model for testing purposes."""
|
|
61
|
+
|
|
62
|
+
responses: list[str]
|
|
63
|
+
"""List of responses to **cycle** through in order."""
|
|
64
|
+
sleep: float | None = None
|
|
65
|
+
i: int = 0
|
|
66
|
+
"""Internally incremented after every model invocation."""
|
|
67
|
+
error_on_chunk_number: int | None = None
|
|
68
|
+
"""If set, raise an error on the specified chunk number during streaming."""
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
@override
|
|
72
|
+
def _llm_type(self) -> str:
|
|
73
|
+
return "fake-list-chat-model"
|
|
74
|
+
|
|
75
|
+
@override
|
|
76
|
+
def _call(
|
|
77
|
+
self,
|
|
78
|
+
*args: Any,
|
|
79
|
+
**kwargs: Any,
|
|
80
|
+
) -> str:
|
|
81
|
+
"""Return the next response in the list.
|
|
82
|
+
|
|
83
|
+
Cycle back to the start if at the end.
|
|
84
|
+
"""
|
|
85
|
+
if self.sleep is not None:
|
|
86
|
+
time.sleep(self.sleep)
|
|
87
|
+
response = self.responses[self.i]
|
|
88
|
+
if self.i < len(self.responses) - 1:
|
|
89
|
+
self.i += 1
|
|
90
|
+
else:
|
|
91
|
+
self.i = 0
|
|
92
|
+
return response
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
def _stream(
|
|
96
|
+
self,
|
|
97
|
+
messages: list[BaseMessage],
|
|
98
|
+
stop: list[str] | None = None,
|
|
99
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> Iterator[ChatGenerationChunk]:
|
|
102
|
+
response = self.responses[self.i]
|
|
103
|
+
if self.i < len(self.responses) - 1:
|
|
104
|
+
self.i += 1
|
|
105
|
+
else:
|
|
106
|
+
self.i = 0
|
|
107
|
+
for i_c, c in enumerate(response):
|
|
108
|
+
if self.sleep is not None:
|
|
109
|
+
time.sleep(self.sleep)
|
|
110
|
+
if (
|
|
111
|
+
self.error_on_chunk_number is not None
|
|
112
|
+
and i_c == self.error_on_chunk_number
|
|
113
|
+
):
|
|
114
|
+
raise FakeListChatModelError
|
|
115
|
+
|
|
116
|
+
chunk_position: Literal["last"] | None = (
|
|
117
|
+
"last" if i_c == len(response) - 1 else None
|
|
118
|
+
)
|
|
119
|
+
yield ChatGenerationChunk(
|
|
120
|
+
message=AIMessageChunk(content=c, chunk_position=chunk_position)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
async def _astream(
|
|
125
|
+
self,
|
|
126
|
+
messages: list[BaseMessage],
|
|
127
|
+
stop: list[str] | None = None,
|
|
128
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
129
|
+
**kwargs: Any,
|
|
130
|
+
) -> AsyncIterator[ChatGenerationChunk]:
|
|
131
|
+
response = self.responses[self.i]
|
|
132
|
+
if self.i < len(self.responses) - 1:
|
|
133
|
+
self.i += 1
|
|
134
|
+
else:
|
|
135
|
+
self.i = 0
|
|
136
|
+
for i_c, c in enumerate(response):
|
|
137
|
+
if self.sleep is not None:
|
|
138
|
+
await asyncio.sleep(self.sleep)
|
|
139
|
+
if (
|
|
140
|
+
self.error_on_chunk_number is not None
|
|
141
|
+
and i_c == self.error_on_chunk_number
|
|
142
|
+
):
|
|
143
|
+
raise FakeListChatModelError
|
|
144
|
+
chunk_position: Literal["last"] | None = (
|
|
145
|
+
"last" if i_c == len(response) - 1 else None
|
|
146
|
+
)
|
|
147
|
+
yield ChatGenerationChunk(
|
|
148
|
+
message=AIMessageChunk(content=c, chunk_position=chunk_position)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
@override
|
|
153
|
+
def _identifying_params(self) -> dict[str, Any]:
|
|
154
|
+
return {"responses": self.responses}
|
|
155
|
+
|
|
156
|
+
@override
|
|
157
|
+
# manually override batch to preserve batch ordering with no concurrency
|
|
158
|
+
def batch(
|
|
159
|
+
self,
|
|
160
|
+
inputs: list[Any],
|
|
161
|
+
config: RunnableConfig | list[RunnableConfig] | None = None,
|
|
162
|
+
*,
|
|
163
|
+
return_exceptions: bool = False,
|
|
164
|
+
**kwargs: Any,
|
|
165
|
+
) -> list[AIMessage]:
|
|
166
|
+
if isinstance(config, list):
|
|
167
|
+
return [
|
|
168
|
+
self.invoke(m, c, **kwargs)
|
|
169
|
+
for m, c in zip(inputs, config, strict=False)
|
|
170
|
+
]
|
|
171
|
+
return [self.invoke(m, config, **kwargs) for m in inputs]
|
|
172
|
+
|
|
173
|
+
@override
|
|
174
|
+
async def abatch(
|
|
175
|
+
self,
|
|
176
|
+
inputs: list[Any],
|
|
177
|
+
config: RunnableConfig | list[RunnableConfig] | None = None,
|
|
178
|
+
*,
|
|
179
|
+
return_exceptions: bool = False,
|
|
180
|
+
**kwargs: Any,
|
|
181
|
+
) -> list[AIMessage]:
|
|
182
|
+
if isinstance(config, list):
|
|
183
|
+
# do Not use an async iterator here because need explicit ordering
|
|
184
|
+
return [
|
|
185
|
+
await self.ainvoke(m, c, **kwargs)
|
|
186
|
+
for m, c in zip(inputs, config, strict=False)
|
|
187
|
+
]
|
|
188
|
+
# do Not use an async iterator here because need explicit ordering
|
|
189
|
+
return [await self.ainvoke(m, config, **kwargs) for m in inputs]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class FakeChatModel(SimpleChatModel):
|
|
193
|
+
"""Fake Chat Model wrapper for testing purposes."""
|
|
194
|
+
|
|
195
|
+
@override
|
|
196
|
+
def _call(
|
|
197
|
+
self,
|
|
198
|
+
messages: list[BaseMessage],
|
|
199
|
+
stop: list[str] | None = None,
|
|
200
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
201
|
+
**kwargs: Any,
|
|
202
|
+
) -> str:
|
|
203
|
+
return "fake response"
|
|
204
|
+
|
|
205
|
+
@override
|
|
206
|
+
async def _agenerate(
|
|
207
|
+
self,
|
|
208
|
+
messages: list[BaseMessage],
|
|
209
|
+
stop: list[str] | None = None,
|
|
210
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
211
|
+
**kwargs: Any,
|
|
212
|
+
) -> ChatResult:
|
|
213
|
+
output_str = "fake response"
|
|
214
|
+
message = AIMessage(content=output_str)
|
|
215
|
+
generation = ChatGeneration(message=message)
|
|
216
|
+
return ChatResult(generations=[generation])
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def _llm_type(self) -> str:
|
|
220
|
+
return "fake-chat-model"
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def _identifying_params(self) -> dict[str, Any]:
|
|
224
|
+
return {"key": "fake"}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class GenericFakeChatModel(BaseChatModel):
|
|
228
|
+
"""Generic fake chat model that can be used to test the chat model interface.
|
|
229
|
+
|
|
230
|
+
* Chat model should be usable in both sync and async tests
|
|
231
|
+
* Invokes `on_llm_new_token` to allow for testing of callback related code for new
|
|
232
|
+
tokens.
|
|
233
|
+
* Includes logic to break messages into message chunk to facilitate testing of
|
|
234
|
+
streaming.
|
|
235
|
+
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
messages: Iterator[AIMessage | str]
|
|
239
|
+
"""Get an iterator over messages.
|
|
240
|
+
|
|
241
|
+
This can be expanded to accept other types like Callables / dicts / strings
|
|
242
|
+
to make the interface more generic if needed.
|
|
243
|
+
|
|
244
|
+
!!! note
|
|
245
|
+
if you want to pass a list, you can use `iter` to convert it to an iterator.
|
|
246
|
+
|
|
247
|
+
!!! warning
|
|
248
|
+
Streaming is not implemented yet. We should try to implement it in the future by
|
|
249
|
+
delegating to invoke and then breaking the resulting output into message chunks.
|
|
250
|
+
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
@override
|
|
254
|
+
def _generate(
|
|
255
|
+
self,
|
|
256
|
+
messages: list[BaseMessage],
|
|
257
|
+
stop: list[str] | None = None,
|
|
258
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
259
|
+
**kwargs: Any,
|
|
260
|
+
) -> ChatResult:
|
|
261
|
+
message = next(self.messages)
|
|
262
|
+
message_ = AIMessage(content=message) if isinstance(message, str) else message
|
|
263
|
+
generation = ChatGeneration(message=message_)
|
|
264
|
+
return ChatResult(generations=[generation])
|
|
265
|
+
|
|
266
|
+
def _stream(
|
|
267
|
+
self,
|
|
268
|
+
messages: list[BaseMessage],
|
|
269
|
+
stop: list[str] | None = None,
|
|
270
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
271
|
+
**kwargs: Any,
|
|
272
|
+
) -> Iterator[ChatGenerationChunk]:
|
|
273
|
+
chat_result = self._generate(
|
|
274
|
+
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
275
|
+
)
|
|
276
|
+
if not isinstance(chat_result, ChatResult):
|
|
277
|
+
msg = ( # type: ignore[unreachable]
|
|
278
|
+
f"Expected generate to return a ChatResult, "
|
|
279
|
+
f"but got {type(chat_result)} instead."
|
|
280
|
+
)
|
|
281
|
+
raise ValueError(msg) # noqa: TRY004
|
|
282
|
+
|
|
283
|
+
message = chat_result.generations[0].message
|
|
284
|
+
|
|
285
|
+
if not isinstance(message, AIMessage):
|
|
286
|
+
msg = (
|
|
287
|
+
f"Expected invoke to return an AIMessage, "
|
|
288
|
+
f"but got {type(message)} instead."
|
|
289
|
+
)
|
|
290
|
+
raise ValueError(msg) # noqa: TRY004
|
|
291
|
+
|
|
292
|
+
content = message.content
|
|
293
|
+
|
|
294
|
+
if content:
|
|
295
|
+
# Use a regular expression to split on whitespace with a capture group
|
|
296
|
+
# so that we can preserve the whitespace in the output.
|
|
297
|
+
if not isinstance(content, str):
|
|
298
|
+
msg = "Expected content to be a string."
|
|
299
|
+
raise ValueError(msg)
|
|
300
|
+
|
|
301
|
+
content_chunks = cast("list[str]", re.split(r"(\s)", content))
|
|
302
|
+
|
|
303
|
+
for idx, token in enumerate(content_chunks):
|
|
304
|
+
chunk = ChatGenerationChunk(
|
|
305
|
+
message=AIMessageChunk(content=token, id=message.id)
|
|
306
|
+
)
|
|
307
|
+
if (
|
|
308
|
+
idx == len(content_chunks) - 1
|
|
309
|
+
and isinstance(chunk.message, AIMessageChunk)
|
|
310
|
+
and not message.additional_kwargs
|
|
311
|
+
):
|
|
312
|
+
chunk.message.chunk_position = "last"
|
|
313
|
+
if run_manager:
|
|
314
|
+
run_manager.on_llm_new_token(token, chunk=chunk)
|
|
315
|
+
yield chunk
|
|
316
|
+
|
|
317
|
+
if message.additional_kwargs:
|
|
318
|
+
for key, value in message.additional_kwargs.items():
|
|
319
|
+
# We should further break down the additional kwargs into chunks
|
|
320
|
+
# Special case for function call
|
|
321
|
+
if key == "function_call":
|
|
322
|
+
for fkey, fvalue in value.items():
|
|
323
|
+
if isinstance(fvalue, str):
|
|
324
|
+
# Break function call by `,`
|
|
325
|
+
fvalue_chunks = cast("list[str]", re.split(r"(,)", fvalue))
|
|
326
|
+
for fvalue_chunk in fvalue_chunks:
|
|
327
|
+
chunk = ChatGenerationChunk(
|
|
328
|
+
message=AIMessageChunk(
|
|
329
|
+
id=message.id,
|
|
330
|
+
content="",
|
|
331
|
+
additional_kwargs={
|
|
332
|
+
"function_call": {fkey: fvalue_chunk}
|
|
333
|
+
},
|
|
334
|
+
)
|
|
335
|
+
)
|
|
336
|
+
if run_manager:
|
|
337
|
+
run_manager.on_llm_new_token(
|
|
338
|
+
"",
|
|
339
|
+
chunk=chunk, # No token for function call
|
|
340
|
+
)
|
|
341
|
+
yield chunk
|
|
342
|
+
else:
|
|
343
|
+
chunk = ChatGenerationChunk(
|
|
344
|
+
message=AIMessageChunk(
|
|
345
|
+
id=message.id,
|
|
346
|
+
content="",
|
|
347
|
+
additional_kwargs={"function_call": {fkey: fvalue}},
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
if run_manager:
|
|
351
|
+
run_manager.on_llm_new_token(
|
|
352
|
+
"",
|
|
353
|
+
chunk=chunk, # No token for function call
|
|
354
|
+
)
|
|
355
|
+
yield chunk
|
|
356
|
+
else:
|
|
357
|
+
chunk = ChatGenerationChunk(
|
|
358
|
+
message=AIMessageChunk(
|
|
359
|
+
id=message.id, content="", additional_kwargs={key: value}
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
if run_manager:
|
|
363
|
+
run_manager.on_llm_new_token(
|
|
364
|
+
"",
|
|
365
|
+
chunk=chunk, # No token for function call
|
|
366
|
+
)
|
|
367
|
+
yield chunk
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def _llm_type(self) -> str:
|
|
371
|
+
return "generic-fake-chat-model"
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class ParrotFakeChatModel(BaseChatModel):
|
|
375
|
+
"""Generic fake chat model that can be used to test the chat model interface.
|
|
376
|
+
|
|
377
|
+
* Chat model should be usable in both sync and async tests
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
@override
|
|
382
|
+
def _generate(
|
|
383
|
+
self,
|
|
384
|
+
messages: list[BaseMessage],
|
|
385
|
+
stop: list[str] | None = None,
|
|
386
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
387
|
+
**kwargs: Any,
|
|
388
|
+
) -> ChatResult:
|
|
389
|
+
if not messages:
|
|
390
|
+
msg = "messages list cannot be empty."
|
|
391
|
+
raise ValueError(msg)
|
|
392
|
+
return ChatResult(generations=[ChatGeneration(message=messages[-1])])
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def _llm_type(self) -> str:
|
|
396
|
+
return "parrot-fake-chat-model"
|