calfkit 0.3.2__tar.gz → 0.3.4__tar.gz
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.
- calfkit-0.3.4/.release-please-manifest.json +3 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/CHANGELOG.md +14 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/PKG-INFO +2 -1
- {calfkit-0.3.2 → calfkit-0.3.4}/README.md +1 -0
- calfkit-0.3.4/calfkit/exceptions.py +60 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/state.py +85 -20
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/nodes/agent.py +153 -10
- calfkit-0.3.4/calfkit/nodes/tool.py +200 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/pyproject.toml +1 -1
- calfkit-0.3.4/tests/test_tool_errors.py +1492 -0
- calfkit-0.3.2/.release-please-manifest.json +0 -3
- calfkit-0.3.2/calfkit/exceptions.py +0 -2
- calfkit-0.3.2/calfkit/nodes/tool.py +0 -105
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/CODEOWNERS +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/dependabot.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/workflows/build.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/workflows/code-checks.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/workflows/release.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/workflows/security.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.github/workflows/test.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/.gitignore +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/LICENSE +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/Makefile +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/ROADMAP.md +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_protocol.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_types.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/LICENSE +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/__main__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_a2a.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_agent_graph.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_cli/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_cli/web.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_function_schema.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_griffe.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_instrumentation.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_json_schema.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_mcp.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_otel_messages.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_output.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_parts_manager.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_run_context.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_system_prompt.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_thinking_part.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_tool_manager.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/_utils.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ag_ui.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/agent/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/agent/abstract.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/agent/wrapper.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/builtin_tools.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/common_tools/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/common_tools/duckduckgo.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/common_tools/exa.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/common_tools/tavily.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/direct.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_agent.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_fastmcp_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_mcp.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_mcp_server.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_model.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_utils.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_agent.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_cache_policies.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_function_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_mcp_server.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_model.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_types.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_agent.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_dynamic_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_fastmcp_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_function_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_logfire.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_mcp.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_mcp_server.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_model.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_run_context.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_toolset.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_workflow.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/base.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/cohere.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/google.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/instrumented.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/openai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/result.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/sentence_transformers.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/settings.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/test.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/voyageai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/embeddings/wrapper.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/exceptions.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ext/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ext/aci.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ext/langchain.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/format_prompt.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/mcp.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/messages.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/anthropic.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/bedrock.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/cerebras.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/cohere.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/fallback.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/function.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/gemini.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/google.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/groq.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/huggingface.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/instrumented.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/mcp_sampling.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/mistral.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/openai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/openrouter.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/outlines.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/test.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/wrapper.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/models/xai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/output.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/amazon.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/anthropic.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/cohere.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/deepseek.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/google.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/grok.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/groq.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/harmony.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/meta.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/mistral.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/moonshotai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/openai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/qwen.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/profiles/zai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/alibaba.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/anthropic.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/azure.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/bedrock.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/cerebras.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/cohere.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/deepseek.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/fireworks.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/gateway.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/github.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/google.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/google_gla.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/google_vertex.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/grok.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/groq.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/heroku.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/huggingface.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/litellm.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/mistral.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/moonshotai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/nebius.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/ollama.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/openai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/openrouter.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/outlines.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/ovhcloud.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/sambanova.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/sentence_transformers.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/together.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/vercel.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/voyageai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/providers/xai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/py.typed +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/result.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/retries.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/run.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/settings.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/tools.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/_dynamic.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/abstract.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/approval_required.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/combined.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/external.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/fastmcp.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/filtered.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/function.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/prefixed.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/prepared.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/renamed.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/toolsets/wrapper.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_adapter.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_event_stream.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_messages_builder.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_web/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_web/api.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/_web/app.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/_adapter.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/_event_stream.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/app.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_adapter.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_event_stream.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_models.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_utils.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/request_types.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/response_types.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/pydantic_ai/usage.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/_vendor/vendor.txt +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/base.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/client.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/deserialize.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/invocation_handle.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/middleware.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/node_result.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/client/reply_dispatcher.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/actions.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/envelope.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/node_schema.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/payload.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/session_context.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/models/tool_context.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/nodes/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/nodes/base.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/nodes/consumer.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/nodes/node.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/providers/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/providers/pydantic_ai/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/providers/pydantic_ai/anthropic.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/providers/pydantic_ai/model_client.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/providers/pydantic_ai/openai.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/worker/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/worker/worker.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/calfkit/worker/worker_config.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/codecov.yml +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/docs/calfkit-v1-design.md +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/docs/calfkit-v1-dx-review.md +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/docs/hooks-design.md +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/agent_dispatcher.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/chat_node.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/chat_repl_cli.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/invoke_agent.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/router_node.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/deprecated/tool_nodes.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/quickstart/agent_service.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/quickstart/invoke.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/quickstart/weather_sink.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/quickstart/weather_tool.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/examples/rpc_worker.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/release-please-config.json +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/conftest.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/integration/__init__.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/integration/test_agent_output_types.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/integration/test_agent_workers.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/providers.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_co_tenant_tool_isolation.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_concurrent_tool_calls.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_consumer.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_gates.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_headers.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_instructions.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_model_settings.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_overrides.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/test_serializable.py +0 -0
- {calfkit-0.3.2 → calfkit-0.3.4}/tests/utils.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.4](https://github.com/calf-ai/calfkit-sdk/compare/v0.3.3...v0.3.4) (2026-05-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Documentation
|
|
7
|
+
|
|
8
|
+
* add Ask DeepWiki badge to README ([#152](https://github.com/calf-ai/calfkit-sdk/issues/152)) ([2fb90f7](https://github.com/calf-ai/calfkit-sdk/commit/2fb90f70ddf9e5363759936c385af04c58651e33))
|
|
9
|
+
|
|
10
|
+
## [0.3.3](https://github.com/calf-ai/calfkit-sdk/compare/v0.3.2...v0.3.3) (2026-05-22)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* (agent,tool) surface tool exceptions to the agent instead of hanging ([#146](https://github.com/calf-ai/calfkit-sdk/issues/146)) ([05162cc](https://github.com/calf-ai/calfkit-sdk/commit/05162cc2512285e7dfcb64a30c7c8bca2f9fe50d))
|
|
16
|
+
|
|
3
17
|
## [0.3.2](https://github.com/calf-ai/calfkit-sdk/compare/v0.3.1...v0.3.2) (2026-05-21)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: calfkit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Build AI workflows and agents as fully-distributed and event-driven microservices.
|
|
5
5
|
Project-URL: Homepage, https://github.com/calf-ai/calf-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/calf-ai/calf-sdk
|
|
@@ -38,6 +38,7 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
|
|
39
39
|
# 🐮 Calfkit SDK
|
|
40
40
|
|
|
41
|
+
[](https://deepwiki.com/calf-ai/calfkit-sdk)
|
|
41
42
|
[](https://pypi.org/project/calfkit/)
|
|
42
43
|
[](https://pepy.tech/projects/calfkit)
|
|
43
44
|
[](https://pypi.org/project/calfkit/)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# 🐮 Calfkit SDK
|
|
2
2
|
|
|
3
|
+
[](https://deepwiki.com/calf-ai/calfkit-sdk)
|
|
3
4
|
[](https://pypi.org/project/calfkit/)
|
|
4
5
|
[](https://pepy.tech/projects/calfkit)
|
|
5
6
|
[](https://pypi.org/project/calfkit/)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DeserializationError(Exception):
|
|
5
|
+
"""Raised when client-side output deserialization fails."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ToolExecutionError(Exception):
|
|
9
|
+
"""The original traceback is not preserved across the Kafka boundary; it is
|
|
10
|
+
logged at the worker that ran the tool. ``exc_type`` and ``exc_message`` are
|
|
11
|
+
the only forensic data available at the agent.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, *, tool_name: str, tool_call_id: str, exc_type: str, exc_message: str):
|
|
15
|
+
self.tool_name = tool_name
|
|
16
|
+
self.tool_call_id = tool_call_id
|
|
17
|
+
self.exc_type = exc_type
|
|
18
|
+
self.exc_message = exc_message
|
|
19
|
+
super().__init__(f"Tool {tool_name!r} (call_id={tool_call_id}) raised {exc_type}: {exc_message}")
|
|
20
|
+
|
|
21
|
+
def __reduce__(self) -> tuple[Any, ...]:
|
|
22
|
+
# Bypass keyword-only ``__init__`` during reconstruction by routing
|
|
23
|
+
# through a module-level helper that uses ``__new__``. A naive
|
|
24
|
+
# ``(self.__class__, (), state)`` reduction would call ``__init__()``
|
|
25
|
+
# with no args and fail because the keyword arguments are required.
|
|
26
|
+
return (
|
|
27
|
+
_reconstruct_tool_execution_error,
|
|
28
|
+
(),
|
|
29
|
+
{
|
|
30
|
+
"tool_name": self.tool_name,
|
|
31
|
+
"tool_call_id": self.tool_call_id,
|
|
32
|
+
"exc_type": self.exc_type,
|
|
33
|
+
"exc_message": self.exc_message,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def __setstate__(self, state: dict[str, Any] | None) -> None:
|
|
38
|
+
# ``__reduce__`` above always provides a dict, but the supertype
|
|
39
|
+
# signature allows ``None`` (Liskov); treat ``None`` as a no-op so
|
|
40
|
+
# subclasses constructed without state don't crash.
|
|
41
|
+
if state is None:
|
|
42
|
+
return
|
|
43
|
+
self.tool_name = state["tool_name"]
|
|
44
|
+
self.tool_call_id = state["tool_call_id"]
|
|
45
|
+
self.exc_type = state["exc_type"]
|
|
46
|
+
self.exc_message = state["exc_message"]
|
|
47
|
+
Exception.__init__(
|
|
48
|
+
self,
|
|
49
|
+
f"Tool {self.tool_name!r} (call_id={self.tool_call_id}) raised {self.exc_type}: {self.exc_message}",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _reconstruct_tool_execution_error() -> "ToolExecutionError":
|
|
54
|
+
"""Create a bare ``ToolExecutionError`` for reconstruction via ``__setstate__``.
|
|
55
|
+
|
|
56
|
+
Bypasses the keyword-only ``__init__`` so state restoration can populate
|
|
57
|
+
fields directly. Must be module-level (not a lambda or local closure) so
|
|
58
|
+
it can be located by fully-qualified name during deserialization.
|
|
59
|
+
"""
|
|
60
|
+
return ToolExecutionError.__new__(ToolExecutionError)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
|
-
from typing import Annotated, Any, Generic
|
|
3
|
+
from typing import Annotated, Any, ClassVar, Generic, Literal
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel, ConfigDict, Discriminator, Field
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Discriminator, Field, Tag, field_validator
|
|
6
6
|
from typing_extensions import TypeVar
|
|
7
7
|
|
|
8
|
+
from calfkit._vendor.pydantic_ai.exceptions import ModelRetry
|
|
8
9
|
from calfkit._vendor.pydantic_ai.messages import (
|
|
9
10
|
ModelMessage,
|
|
10
11
|
ModelRequest,
|
|
12
|
+
RetryPromptPart,
|
|
11
13
|
ToolCallPart,
|
|
14
|
+
ToolReturn,
|
|
12
15
|
)
|
|
13
|
-
from calfkit._vendor.pydantic_ai.tools import DeferredToolCallResult as ToolCallResult
|
|
14
16
|
from calfkit.models.node_schema import BaseToolNodeSchema
|
|
15
17
|
from calfkit.models.payload import ContentPart
|
|
16
18
|
|
|
@@ -58,28 +60,102 @@ class CoreMessageState(BaseAgentActivityState):
|
|
|
58
60
|
self.uncommitted_message = None
|
|
59
61
|
|
|
60
62
|
|
|
63
|
+
class FailedToolCall(BaseModel):
|
|
64
|
+
"""Wire-format representation of a tool that raised on the worker side.
|
|
65
|
+
|
|
66
|
+
Stored in ``State.tool_results`` in the same slot a successful ``ToolReturn``
|
|
67
|
+
would occupy; consumers must handle both shapes. The ``marker_kind`` literal
|
|
68
|
+
is the discriminator tag consulted by ``_calf_tool_result_discriminator`` so
|
|
69
|
+
this value reconstructs as a typed instance (rather than a plain ``dict``)
|
|
70
|
+
when ``State`` round-trips through JSON across the Kafka boundary.
|
|
71
|
+
|
|
72
|
+
String fields are clamped to bounded lengths (see ``_MAX_LENGTHS``) rather
|
|
73
|
+
than rejected for wire-size safety: a tool that raises with an enormous
|
|
74
|
+
``str(exc)`` must not itself cause construction to raise inside the worker's
|
|
75
|
+
``except Exception`` block (which would prevent the failure reply from ever
|
|
76
|
+
being published and hang the agent).
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
model_config = ConfigDict(extra="ignore", frozen=True)
|
|
80
|
+
tool_name: str
|
|
81
|
+
tool_call_id: str = Field(min_length=1)
|
|
82
|
+
exc_type: str
|
|
83
|
+
exc_message: str
|
|
84
|
+
marker_kind: Literal["calfkit-tool-error"] = "calfkit-tool-error"
|
|
85
|
+
|
|
86
|
+
_MAX_LENGTHS: ClassVar[dict[str, int]] = {
|
|
87
|
+
"tool_name": 256,
|
|
88
|
+
"tool_call_id": 128,
|
|
89
|
+
"exc_type": 256,
|
|
90
|
+
"exc_message": 4096,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@field_validator("tool_name", "tool_call_id", "exc_type", "exc_message", mode="before")
|
|
94
|
+
@classmethod
|
|
95
|
+
def _clamp_string_fields(cls, v: Any, info: Any) -> Any:
|
|
96
|
+
if isinstance(v, str):
|
|
97
|
+
limit = cls._MAX_LENGTHS.get(info.field_name)
|
|
98
|
+
if limit is not None and len(v) > limit:
|
|
99
|
+
return v[:limit]
|
|
100
|
+
return v
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _calf_tool_result_discriminator(x: Any) -> str | None:
|
|
104
|
+
"""Tag extractor for the ``CalfToolResult`` discriminated union.
|
|
105
|
+
|
|
106
|
+
Mirrors ``calfkit._vendor.pydantic_ai.tools._deferred_tool_call_result_discriminator``
|
|
107
|
+
with an added ``marker_kind`` branch for ``FailedToolCall``. Without this, a
|
|
108
|
+
serialized marker arrives as a plain ``dict`` after a Kafka hop because the
|
|
109
|
+
upstream discriminator only recognizes ``kind`` / ``part_kind``.
|
|
110
|
+
|
|
111
|
+
Returns only string tags; non-string values are treated as missing so the
|
|
112
|
+
union falls through to the ``Any`` arm rather than silently misrouting.
|
|
113
|
+
Keep this in sync with the upstream helper if a new pydantic-ai tag is added.
|
|
114
|
+
"""
|
|
115
|
+
for key in ("marker_kind", "kind", "part_kind"):
|
|
116
|
+
v = x.get(key) if isinstance(x, dict) else getattr(x, key, None)
|
|
117
|
+
if isinstance(v, str):
|
|
118
|
+
return v
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
CalfToolResult = Annotated[
|
|
123
|
+
Annotated[ToolReturn, Tag("tool-return")]
|
|
124
|
+
| Annotated[ModelRetry, Tag("model-retry")]
|
|
125
|
+
| Annotated[RetryPromptPart, Tag("retry-prompt")]
|
|
126
|
+
| Annotated[FailedToolCall, Tag("calfkit-tool-error")],
|
|
127
|
+
Discriminator(_calf_tool_result_discriminator),
|
|
128
|
+
]
|
|
129
|
+
"""Calfkit-side tagged union for values stored in ``State.tool_results``.
|
|
130
|
+
|
|
131
|
+
Flattens pydantic-ai's ``DeferredToolCallResult`` (``ToolReturn`` / ``ModelRetry``
|
|
132
|
+
/ ``RetryPromptPart``) and adds calfkit's ``FailedToolCall``. Flattening (rather
|
|
133
|
+
than ``DeferredToolCallResult | FailedToolCall``) is required because Pydantic's
|
|
134
|
+
discriminated-union machinery does not compose nested ``Discriminator``
|
|
135
|
+
annotations reliably.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
|
|
61
139
|
class InFlightToolsState(BaseAgentActivityState):
|
|
62
140
|
"""State of all in-progress tool calls and results.
|
|
63
141
|
Tool calls and results are in-progress when all tool calls have not been completed and committed to message history.""" # noqa: E501
|
|
64
142
|
|
|
65
143
|
model_config = ConfigDict(extra="ignore")
|
|
66
144
|
|
|
67
|
-
# Map of tool call IDS to tool calls
|
|
68
145
|
tool_calls: dict[str, ToolCallPart] = Field(default_factory=dict)
|
|
69
146
|
|
|
70
|
-
|
|
71
|
-
tool_results: dict[str, ToolCallResult | Any] = Field(default_factory=dict)
|
|
147
|
+
tool_results: dict[str, CalfToolResult | Any] = Field(default_factory=dict)
|
|
72
148
|
|
|
73
149
|
def add_tool_call(self, tool_call: ToolCallPart) -> None:
|
|
74
150
|
self.tool_calls[tool_call.tool_call_id] = tool_call
|
|
75
151
|
|
|
76
|
-
def add_tool_result(self, tool_call_id: str, tool_result:
|
|
152
|
+
def add_tool_result(self, tool_call_id: str, tool_result: CalfToolResult | Any) -> None:
|
|
77
153
|
self.tool_results[tool_call_id] = tool_result
|
|
78
154
|
|
|
79
155
|
def get_tool_call(self, tool_call_id: str) -> ToolCallPart | None:
|
|
80
156
|
return self.tool_calls.get(tool_call_id)
|
|
81
157
|
|
|
82
|
-
def get_tool_result(self, tool_call_id: str) ->
|
|
158
|
+
def get_tool_result(self, tool_call_id: str) -> CalfToolResult | Any | None:
|
|
83
159
|
return self.tool_results.get(tool_call_id)
|
|
84
160
|
|
|
85
161
|
def all_call_ids_complete(self, *call_ids: str) -> bool:
|
|
@@ -102,17 +178,6 @@ class State(CoreMessageState, InFlightToolsState):
|
|
|
102
178
|
overrides: OverridesState | None = None
|
|
103
179
|
|
|
104
180
|
|
|
105
|
-
# class State(BaseModel):
|
|
106
|
-
# """mutable data model to track state of an execution among one or more agents,
|
|
107
|
-
# sharing correlation id"""
|
|
108
|
-
|
|
109
|
-
# model_config = ConfigDict(extra="ignore")
|
|
110
|
-
# run_state: AgentActivityState = Field(default_factory=AgentActivityState)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# ---------------------------------------------------------------------------
|
|
114
|
-
# Experimental PartialState implementation
|
|
115
|
-
# ---------------------------------------------------------------------------
|
|
116
181
|
AgentStateSubsetT = TypeVar("AgentStateSubsetT", CoreMessageState, InFlightToolsState)
|
|
117
182
|
|
|
118
183
|
PartialState = Annotated[CoreMessageState | InFlightToolsState | Any, Discriminator("kind")]
|
|
@@ -135,7 +200,7 @@ class PendingToolBatch:
|
|
|
135
200
|
base_state: State
|
|
136
201
|
|
|
137
202
|
# map of tool call IDs to tool call results
|
|
138
|
-
collected_results: dict[str,
|
|
203
|
+
collected_results: dict[str, CalfToolResult | Any] = field(default_factory=dict)
|
|
139
204
|
|
|
140
205
|
@property
|
|
141
206
|
def is_complete(self) -> bool:
|
|
@@ -2,6 +2,8 @@ import logging
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from typing import Any, ClassVar, Generic, cast
|
|
4
4
|
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
5
7
|
from calfkit._protocol import NodeKind
|
|
6
8
|
from calfkit._types import AgentOutputT
|
|
7
9
|
from calfkit._vendor.pydantic_ai import Agent as InternalAgentLoop
|
|
@@ -11,19 +13,18 @@ from calfkit._vendor.pydantic_ai.output import OutputSpec
|
|
|
11
13
|
from calfkit._vendor.pydantic_ai.settings import ModelSettings
|
|
12
14
|
from calfkit._vendor.pydantic_ai.tools import DeferredToolResults
|
|
13
15
|
from calfkit._vendor.pydantic_ai.toolsets.external import ExternalToolset
|
|
16
|
+
from calfkit.exceptions import ToolExecutionError
|
|
14
17
|
from calfkit.models import Call, DataPart, NodeResult, ReturnCall, State, TailCall, TextPart
|
|
15
18
|
from calfkit.models.actions import Silent
|
|
16
19
|
from calfkit.models.node_schema import BaseToolNodeSchema
|
|
17
20
|
from calfkit.models.session_context import SessionRunContext
|
|
18
|
-
from calfkit.models.state import PendingToolBatch
|
|
21
|
+
from calfkit.models.state import FailedToolCall, PendingToolBatch
|
|
19
22
|
from calfkit.nodes.base import BaseNodeDef, GateFunction
|
|
20
|
-
from calfkit.nodes.tool import ToolNodeDef
|
|
23
|
+
from calfkit.nodes.tool import BaseToolNodeDef, ToolNodeDef, _safe_exc_message
|
|
21
24
|
from calfkit.providers.pydantic_ai.model_client import PydanticModelClient
|
|
22
25
|
|
|
23
26
|
logger = logging.getLogger(__name__)
|
|
24
27
|
|
|
25
|
-
NoneType = type(None)
|
|
26
|
-
|
|
27
28
|
|
|
28
29
|
class BaseAgentNodeDef(
|
|
29
30
|
Generic[AgentOutputT],
|
|
@@ -85,11 +86,17 @@ class BaseAgentNodeDef(
|
|
|
85
86
|
elif self.tools:
|
|
86
87
|
tools_registry = {tool.tool_schema.name: tool for tool in self.tools}
|
|
87
88
|
|
|
89
|
+
# ``latest_tool_calls()`` walks ``message_history`` in reverse on each call;
|
|
90
|
+
# cache once for all pre-model uses. The post-model use after
|
|
91
|
+
# ``result.new_messages()`` is extended into history must re-call to see
|
|
92
|
+
# the model's new tool calls.
|
|
93
|
+
latest_tool_calls = ctx.state.latest_tool_calls()
|
|
94
|
+
|
|
88
95
|
logger.debug(
|
|
89
96
|
"[%s] agent run entered node=%s pending_tool_calls=%d history_len=%d",
|
|
90
97
|
ctx.deps.correlation_id[:8],
|
|
91
98
|
self.name,
|
|
92
|
-
len(
|
|
99
|
+
len(latest_tool_calls),
|
|
93
100
|
len(ctx.state.message_history),
|
|
94
101
|
)
|
|
95
102
|
|
|
@@ -99,7 +106,61 @@ class BaseAgentNodeDef(
|
|
|
99
106
|
if batch and not batch.is_complete:
|
|
100
107
|
return Silent()
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
# Collect all FailedToolCall results in this turn so operators see every
|
|
110
|
+
# failure in a parallel batch; raise on the first after logging all.
|
|
111
|
+
# Also defend against corrupt marker dicts: a payload with the calfkit
|
|
112
|
+
# marker tag that fails ``FailedToolCall`` validation (e.g. schema drift
|
|
113
|
+
# during rolling deploy, a required field added without default, a
|
|
114
|
+
# tampered wire payload) round-trips through the union's ``| Any`` arm
|
|
115
|
+
# as a plain ``dict``. Synthesize a typed marker so the silent-failure
|
|
116
|
+
# path closes — operators still see the raise and the corrupt-keys
|
|
117
|
+
# context.
|
|
118
|
+
failed_tool_calls: list[FailedToolCall] = []
|
|
119
|
+
for tc in latest_tool_calls:
|
|
120
|
+
result = ctx.state.tool_results.get(tc.tool_call_id)
|
|
121
|
+
if isinstance(result, FailedToolCall):
|
|
122
|
+
failed_tool_calls.append(result)
|
|
123
|
+
elif isinstance(result, dict) and result.get("marker_kind") == "calfkit-tool-error":
|
|
124
|
+
logger.error(
|
|
125
|
+
"[%s] corrupt FailedToolCall marker detected node=%s tool_call_id=%s raw_keys=%s; "
|
|
126
|
+
"likely schema drift or version skew across the Kafka boundary",
|
|
127
|
+
ctx.deps.correlation_id[:8],
|
|
128
|
+
self.name,
|
|
129
|
+
tc.tool_call_id,
|
|
130
|
+
sorted(result.keys()),
|
|
131
|
+
)
|
|
132
|
+
# Synthesize a typed marker with sentinel fields known to pass
|
|
133
|
+
# FailedToolCall's validators. tc.tool_call_id is the LLM-emitted
|
|
134
|
+
# correlation key; fall back to "<missing>" if it's empty so
|
|
135
|
+
# ``min_length=1`` doesn't itself raise.
|
|
136
|
+
raw_tool_name = result.get("tool_name")
|
|
137
|
+
failed_tool_calls.append(
|
|
138
|
+
FailedToolCall(
|
|
139
|
+
tool_name=raw_tool_name if isinstance(raw_tool_name, str) and raw_tool_name else "<unknown>",
|
|
140
|
+
tool_call_id=tc.tool_call_id or "<missing>",
|
|
141
|
+
exc_type="CorruptFailedToolCallMarker",
|
|
142
|
+
exc_message=f"Marker dict failed validation as FailedToolCall (likely schema drift); raw_keys={sorted(result.keys())}",
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
if failed_tool_calls:
|
|
146
|
+
for failure in failed_tool_calls:
|
|
147
|
+
logger.error(
|
|
148
|
+
"[%s] tool execution error detected node=%s tool=%s tool_call_id=%s exc_type=%s exc_message=%s",
|
|
149
|
+
ctx.deps.correlation_id[:8],
|
|
150
|
+
self.name,
|
|
151
|
+
failure.tool_name,
|
|
152
|
+
failure.tool_call_id,
|
|
153
|
+
failure.exc_type,
|
|
154
|
+
failure.exc_message,
|
|
155
|
+
)
|
|
156
|
+
first = failed_tool_calls[0]
|
|
157
|
+
raise ToolExecutionError(
|
|
158
|
+
tool_name=first.tool_name,
|
|
159
|
+
tool_call_id=first.tool_call_id,
|
|
160
|
+
exc_type=first.exc_type,
|
|
161
|
+
exc_message=first.exc_message,
|
|
162
|
+
)
|
|
163
|
+
|
|
103
164
|
tool_results = None
|
|
104
165
|
|
|
105
166
|
if len(latest_tool_calls) > 0:
|
|
@@ -141,14 +202,13 @@ class BaseAgentNodeDef(
|
|
|
141
202
|
model_settings=run_model_settings,
|
|
142
203
|
)
|
|
143
204
|
if isinstance(result.output, DeferredToolRequests):
|
|
144
|
-
# The LLM called one or more tools
|
|
145
205
|
logger.debug(
|
|
146
206
|
"[%s] model returned DeferredToolRequests tool_count=%d node=%s",
|
|
147
207
|
ctx.deps.correlation_id[:8],
|
|
148
208
|
len(result.output.calls),
|
|
149
209
|
self.name,
|
|
150
210
|
)
|
|
151
|
-
messages = result.new_messages()
|
|
211
|
+
messages = result.new_messages()
|
|
152
212
|
ctx.state.message_history.extend(messages)
|
|
153
213
|
latest_tool_calls = ctx.state.latest_tool_calls()
|
|
154
214
|
|
|
@@ -166,7 +226,9 @@ class BaseAgentNodeDef(
|
|
|
166
226
|
tool_call_id=tool_call.tool_call_id,
|
|
167
227
|
),
|
|
168
228
|
)
|
|
169
|
-
|
|
229
|
+
continue
|
|
230
|
+
|
|
231
|
+
if tool_node.subscribe_topics is None:
|
|
170
232
|
logger.error(
|
|
171
233
|
"tool=%s is unreachable. No subscribe topics were provided for the tool node.",
|
|
172
234
|
tool_call.tool_name,
|
|
@@ -179,8 +241,89 @@ class BaseAgentNodeDef(
|
|
|
179
241
|
tool_call_id=tool_call.tool_call_id,
|
|
180
242
|
),
|
|
181
243
|
)
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
# Parse args from the LLM's emission. Applies to ALL dispatch
|
|
247
|
+
# paths so that malformed-JSON args from override (schema-only)
|
|
248
|
+
# tools are also surfaced as RetryPromptPart instead of escaping
|
|
249
|
+
# to the worker's hard FailedToolCall path.
|
|
250
|
+
#
|
|
251
|
+
# ``args_as_dict()`` can raise more than just ValueError /
|
|
252
|
+
# AssertionError: ``pydantic_core.from_json`` raises TypeError
|
|
253
|
+
# when ``args`` is a non-string/non-bytes value (e.g. an int or
|
|
254
|
+
# list emitted by an off-spec provider). Catch broadly so any
|
|
255
|
+
# parse failure surfaces as an LLM-retryable RetryPromptPart
|
|
256
|
+
# rather than escaping ``run()`` and hanging the caller.
|
|
257
|
+
try:
|
|
258
|
+
args = tool_call.args_as_dict()
|
|
259
|
+
except Exception as e:
|
|
260
|
+
content = f"Malformed tool arguments: {type(e).__name__}: {_safe_exc_message(e)}"
|
|
261
|
+
logger.warning(
|
|
262
|
+
"[%s] tool=%s args parse failed at dispatch: %s",
|
|
263
|
+
ctx.deps.correlation_id[:8],
|
|
264
|
+
tool_call.tool_name,
|
|
265
|
+
content,
|
|
266
|
+
exc_info=True,
|
|
267
|
+
)
|
|
268
|
+
ctx.state.add_tool_result(
|
|
269
|
+
tool_call.tool_call_id,
|
|
270
|
+
RetryPromptPart(
|
|
271
|
+
content=content,
|
|
272
|
+
tool_name=tool_call.tool_name,
|
|
273
|
+
tool_call_id=tool_call.tool_call_id,
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
continue
|
|
277
|
+
|
|
278
|
+
# Validate against the schema if we have a runtime validator.
|
|
279
|
+
# Override-mode schemas (BaseToolNodeSchema-only) skip this step
|
|
280
|
+
# and dispatch unvalidated; this is the documented carve-out.
|
|
281
|
+
if isinstance(tool_node, BaseToolNodeDef):
|
|
282
|
+
try:
|
|
283
|
+
tool_node.validate_call_args(args)
|
|
284
|
+
except ValidationError as e:
|
|
285
|
+
validation_errors = e.errors(include_url=False, include_context=False)
|
|
286
|
+
logger.warning(
|
|
287
|
+
"[%s] tool=%s arg validation failed at dispatch: %s",
|
|
288
|
+
ctx.deps.correlation_id[:8],
|
|
289
|
+
tool_call.tool_name,
|
|
290
|
+
validation_errors,
|
|
291
|
+
)
|
|
292
|
+
ctx.state.add_tool_result(
|
|
293
|
+
tool_call.tool_call_id,
|
|
294
|
+
RetryPromptPart(
|
|
295
|
+
content=validation_errors,
|
|
296
|
+
tool_name=tool_call.tool_name,
|
|
297
|
+
tool_call_id=tool_call.tool_call_id,
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
continue
|
|
301
|
+
except Exception as e:
|
|
302
|
+
# A user-authored Pydantic ``field_validator`` raised
|
|
303
|
+
# something other than ``ValidationError`` (e.g.
|
|
304
|
+
# ``RuntimeError``, ``TypeError``, a custom exception).
|
|
305
|
+
# Surface as ``RetryPromptPart`` so the LLM can retry
|
|
306
|
+
# rather than letting the exception escape ``run()`` and
|
|
307
|
+
# silently hang the caller.
|
|
308
|
+
validator_content = f"Tool argument validator raised {type(e).__name__}: {_safe_exc_message(e)}"
|
|
309
|
+
logger.warning(
|
|
310
|
+
"[%s] tool=%s arg validator raised %s; surfacing as RetryPromptPart",
|
|
311
|
+
ctx.deps.correlation_id[:8],
|
|
312
|
+
tool_call.tool_name,
|
|
313
|
+
type(e).__name__,
|
|
314
|
+
exc_info=True,
|
|
315
|
+
)
|
|
316
|
+
ctx.state.add_tool_result(
|
|
317
|
+
tool_call.tool_call_id,
|
|
318
|
+
RetryPromptPart(
|
|
319
|
+
content=validator_content,
|
|
320
|
+
tool_name=tool_call.tool_name,
|
|
321
|
+
tool_call_id=tool_call.tool_call_id,
|
|
322
|
+
),
|
|
323
|
+
)
|
|
324
|
+
continue
|
|
182
325
|
|
|
183
|
-
if ctx.state.all_call_ids_complete(*[tc.tool_call_id for tc in latest_tool_calls]):
|
|
326
|
+
if ctx.state.all_call_ids_complete(*[tc.tool_call_id for tc in latest_tool_calls]):
|
|
184
327
|
# TODO: maybe consider a node retry return type that doesn't require round trip to itself.
|
|
185
328
|
# Tailcall to itself is a roundtrip.
|
|
186
329
|
logger.debug("[%s] all tool calls invalid, TailCall retry node=%s", ctx.deps.correlation_id[:8], self.name)
|