calfkit 0.5.2__tar.gz → 0.5.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.5.4/.release-please-manifest.json +3 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/CHANGELOG.md +14 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/PKG-INFO +51 -34
- {calfkit-0.5.2 → calfkit-0.5.4}/README.md +48 -33
- {calfkit-0.5.2 → calfkit-0.5.4}/ROADMAP.md +1 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/cli/__init__.py +5 -2
- calfkit-0.5.4/calfkit/cli/_loader.py +165 -0
- calfkit-0.5.4/calfkit/cli/_run.py +123 -0
- calfkit-0.5.4/calfkit/cli/run.py +117 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/cli/topics.py +4 -74
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/client.py +26 -5
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/state.py +16 -0
- calfkit-0.5.4/calfkit/nodes/_projection.py +260 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/agent.py +24 -4
- calfkit-0.5.4/docs/agent-pov-projection.md +381 -0
- calfkit-0.5.4/docs/cli.md +214 -0
- calfkit-0.5.4/examples/multi_agent_panel/README.md +29 -0
- calfkit-0.5.4/examples/multi_agent_panel/panel.py +38 -0
- calfkit-0.5.4/examples/multi_agent_panel/run.py +47 -0
- calfkit-0.5.4/examples/multi_agent_panel/service.py +23 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/pyproject.toml +8 -4
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/provisioning_cli_nodes.py +10 -0
- calfkit-0.5.4/tests/test_agent_loop_integration.py +410 -0
- calfkit-0.5.4/tests/test_client_author.py +94 -0
- calfkit-0.5.4/tests/test_projection.py +602 -0
- calfkit-0.5.4/tests/test_projection_coverage.py +124 -0
- calfkit-0.5.4/tests/test_projection_review_fixes.py +84 -0
- calfkit-0.5.4/tests/test_run_cli.py +200 -0
- calfkit-0.5.4/tests/test_run_loader.py +179 -0
- calfkit-0.5.4/tests/test_run_serve.py +247 -0
- calfkit-0.5.4/tests/test_state_stamping.py +117 -0
- calfkit-0.5.2/.release-please-manifest.json +0 -3
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/CODEOWNERS +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/dependabot.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/build.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/code-checks.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/kafka-integration.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/mcp-e2e.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/release.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/security.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.github/workflows/test.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/.gitignore +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/LICENSE +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/Makefile +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_protocol.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_types.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/LICENSE +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/__main__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_a2a.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_agent_graph.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_cli/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_cli/web.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_function_schema.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_griffe.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_instrumentation.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_json_schema.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_mcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_otel_messages.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_output.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_parts_manager.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_run_context.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_system_prompt.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_thinking_part.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_tool_manager.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/_utils.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ag_ui.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/agent/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/agent/abstract.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/agent/wrapper.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/builtin_tools.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/common_tools/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/common_tools/duckduckgo.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/common_tools/exa.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/common_tools/tavily.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/direct.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_agent.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_fastmcp_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_mcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_mcp_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_model.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/dbos/_utils.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_agent.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_cache_policies.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_function_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_mcp_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_model.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/prefect/_types.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_agent.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_dynamic_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_fastmcp_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_function_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_logfire.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_mcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_mcp_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_model.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_run_context.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_toolset.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/durable_exec/temporal/_workflow.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/base.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/cohere.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/google.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/instrumented.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/openai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/result.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/sentence_transformers.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/settings.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/test.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/voyageai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/embeddings/wrapper.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/exceptions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ext/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ext/aci.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ext/langchain.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/format_prompt.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/mcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/messages.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/anthropic.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/bedrock.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/cerebras.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/cohere.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/fallback.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/function.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/gemini.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/google.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/groq.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/huggingface.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/instrumented.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/mcp_sampling.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/mistral.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/openai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/openrouter.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/outlines.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/test.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/wrapper.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/models/xai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/output.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/amazon.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/anthropic.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/cohere.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/deepseek.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/google.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/grok.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/groq.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/harmony.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/meta.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/mistral.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/moonshotai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/openai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/qwen.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/profiles/zai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/alibaba.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/anthropic.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/azure.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/bedrock.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/cerebras.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/cohere.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/deepseek.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/fireworks.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/gateway.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/github.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/google.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/google_gla.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/google_vertex.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/grok.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/groq.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/heroku.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/huggingface.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/litellm.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/mistral.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/moonshotai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/nebius.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/ollama.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/openai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/openrouter.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/outlines.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/ovhcloud.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/sambanova.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/sentence_transformers.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/together.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/vercel.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/voyageai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/providers/xai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/py.typed +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/result.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/retries.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/run.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/settings.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/tools.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/_dynamic.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/abstract.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/approval_required.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/combined.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/external.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/fastmcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/filtered.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/function.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/prefixed.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/prepared.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/renamed.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/toolsets/wrapper.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_adapter.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_event_stream.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_messages_builder.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_web/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_web/api.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/_web/app.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/_adapter.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/_event_stream.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/ag_ui/app.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_adapter.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_event_stream.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_models.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/_utils.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/request_types.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/ui/vercel_ai/response_types.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/pydantic_ai/usage.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/_vendor/vendor.txt +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/cli/mcp.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/base.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/deserialize.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/invocation_handle.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/middleware.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/node_result.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/client/reply_dispatcher.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/exceptions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_adapt.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_bridge.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_codegen.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_config.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_dedup.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_factory.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_session.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_testing.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/_tool_def.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/exceptions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/mcp/mcp.schema.json +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/actions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/envelope.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/node_schema.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/payload.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/session_context.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/models/tool_context.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/base.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/consumer.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/node.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/nodes/tool.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/providers/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/providers/pydantic_ai/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/providers/pydantic_ai/anthropic.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/providers/pydantic_ai/model_client.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/providers/pydantic_ai/openai.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/provisioning/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/provisioning/config.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/provisioning/provisioner.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/worker/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/worker/lifecycle.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/worker/worker.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/calfkit/worker/worker_config.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/codecov.yml +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/calfkit-v1-design.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/calfkit-v1-dx-review.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/deps-as-dict-refactor.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/fire-and-forget-emit.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/hooks-design.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/mcp-discovery-rpc-design.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/mcp-json-schema-reference-design.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/mcp-overview.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/topic-provisioning.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/docs/worker-lifecycle.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/agent_dispatcher.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/chat_node.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/chat_repl_cli.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/invoke_agent.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/router_node.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/deprecated/tool_nodes.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart/agent_service.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart/emit.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart/invoke.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart/weather_sink.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart/weather_tool.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/README.md +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/agent_service.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/everything_schemas.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/invoke.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/shared.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/quickstart_mcp/tools_service.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/rpc_worker.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/examples/topic_provisioning.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/release-please-config.json +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/conftest.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/integration/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/integration/test_agent_output_types.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/integration/test_agent_workers.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/integration/test_topic_provisioning.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/conftest.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/e2e/__init__.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/e2e/test_real_mcp_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_adapt.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_baseline_schema_only_dispatch.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_bridge.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_bridge_integration.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_cli.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_codegen.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_config.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_dedup.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_exceptions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_factory.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_schema.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_server.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_session.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_testing.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_tool_def.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/mcp/test_worker_integration.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/providers.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/provisioning_cli_import_boom.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_co_tenant_tool_isolation.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_concurrent_tool_calls.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_consumer.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_fire_and_forget.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_gates.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_headers.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_instructions.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_lifecycle.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_lifecycle_e2e.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_lifecycle_resource_fields.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_lifecycle_resource_injection.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_lifecycle_review_fixes.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_model_settings.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_overrides.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_provisioning.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_provisioning_cli.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_provisioning_client.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_provisioning_worker.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_reply_dispatcher_ttl.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_serializable.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_skip_if_no_kafka_marker.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_tool_errors.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/test_worker_lifecycle.py +0 -0
- {calfkit-0.5.2 → calfkit-0.5.4}/tests/utils.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.4](https://github.com/calf-ai/calfkit-sdk/compare/v0.5.3...v0.5.4) (2026-06-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* agent-POV message-history projection for multi-agent channels ([#154](https://github.com/calf-ai/calfkit-sdk/issues/154)) ([#185](https://github.com/calf-ai/calfkit-sdk/issues/185)) ([86e69c5](https://github.com/calf-ai/calfkit-sdk/commit/86e69c5436369d088c8cf02122135adecda43759))
|
|
9
|
+
|
|
10
|
+
## [0.5.3](https://github.com/calf-ai/calfkit-sdk/compare/v0.5.2...v0.5.3) (2026-06-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* CLI add `calfkit run` to run nodes without Worker boilerplate ([#181](https://github.com/calf-ai/calfkit-sdk/issues/181)) ([6c1e38e](https://github.com/calf-ai/calfkit-sdk/commit/6c1e38e006f1481016dc132e502fa4eeb734d9ef))
|
|
16
|
+
|
|
3
17
|
## [0.5.2](https://github.com/calf-ai/calfkit-sdk/compare/v0.5.1...v0.5.2) (2026-06-06)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: calfkit
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.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/calfkit-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/calf-ai/calfkit-sdk
|
|
@@ -36,9 +36,11 @@ Requires-Dist: typing-inspection>=0.4.0
|
|
|
36
36
|
Requires-Dist: uuid-utils>=0.14.0
|
|
37
37
|
Provides-Extra: cli
|
|
38
38
|
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
39
|
+
Requires-Dist: watchfiles>=0.21; extra == 'cli'
|
|
39
40
|
Provides-Extra: dev
|
|
40
41
|
Provides-Extra: mcp-codegen
|
|
41
42
|
Requires-Dist: typer>=0.12; extra == 'mcp-codegen'
|
|
43
|
+
Requires-Dist: watchfiles>=0.21; extra == 'mcp-codegen'
|
|
42
44
|
Description-Content-Type: text/markdown
|
|
43
45
|
|
|
44
46
|
# 🐮 Calfkit SDK
|
|
@@ -100,9 +102,11 @@ Calfkit is a Python SDK that builds event-stream agents out-the-box. You get the
|
|
|
100
102
|
### 1. Install
|
|
101
103
|
|
|
102
104
|
```bash
|
|
103
|
-
pip install calfkit
|
|
105
|
+
pip install "calfkit[cli]"
|
|
104
106
|
```
|
|
105
107
|
|
|
108
|
+
The `[cli]` extra adds the `calfkit` command used below to run nodes during development. (The library itself is just `pip install calfkit`.)
|
|
109
|
+
|
|
106
110
|
<br>
|
|
107
111
|
|
|
108
112
|
### 2. Start a Calfkit Broker
|
|
@@ -140,10 +144,11 @@ You will be provided a Calfkit broker API to deploy your agents instead of setti
|
|
|
140
144
|
> created on first use. Most hardened/managed brokers have that **disabled**, in
|
|
141
145
|
> which case producers and consumers silently stall on a missing topic. Calfkit
|
|
142
146
|
> ships an **EXPERIMENTAL, opt-in** topic provisioner (off by default) for the
|
|
143
|
-
> dev/CI case
|
|
144
|
-
>
|
|
145
|
-
>
|
|
146
|
-
>
|
|
147
|
+
> dev/CI case — pass `--provision` to `calfkit run` below (or
|
|
148
|
+
> `Client.connect("localhost:9092", provisioning=ProvisioningConfig(enabled=True))`
|
|
149
|
+
> in code). It is a development convenience (`replication_factor=1`, no ACLs) —
|
|
150
|
+
> **review it before production**, where topic creation is typically ops-governed.
|
|
151
|
+
> See [`docs/topic-provisioning.md`](docs/topic-provisioning.md).
|
|
147
152
|
|
|
148
153
|
<br>
|
|
149
154
|
|
|
@@ -153,30 +158,19 @@ Define and deploy a tool as an independent service. Tools are not owned by or co
|
|
|
153
158
|
|
|
154
159
|
```python
|
|
155
160
|
# weather_tool.py
|
|
156
|
-
import asyncio
|
|
157
161
|
from calfkit.nodes import agent_tool
|
|
158
|
-
from calfkit.client import Client
|
|
159
|
-
from calfkit.worker import Worker
|
|
160
162
|
|
|
161
163
|
# Define a tool — the @agent_tool decorator turns any function into a deployable tool node
|
|
162
164
|
@agent_tool
|
|
163
165
|
def get_weather(location: str) -> str:
|
|
164
166
|
"""Get the current weather at a location"""
|
|
165
167
|
return f"It's sunny in {location}"
|
|
166
|
-
|
|
167
|
-
async def main():
|
|
168
|
-
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
169
|
-
worker = Worker(client, nodes=[get_weather]) # Initialize a worker with the tool node
|
|
170
|
-
await worker.run() # (Blocking call) Deploy the service to start serving traffic
|
|
171
|
-
|
|
172
|
-
if __name__ == "__main__":
|
|
173
|
-
asyncio.run(main())
|
|
174
168
|
```
|
|
175
169
|
|
|
176
|
-
|
|
170
|
+
Deploy the tool as a service. `calfkit run` points at a `module:attr` target and starts a worker for you — no `Client`/`Worker` wiring required:
|
|
177
171
|
|
|
178
172
|
```shell
|
|
179
|
-
|
|
173
|
+
calfkit run weather_tool:get_weather
|
|
180
174
|
```
|
|
181
175
|
|
|
182
176
|
<br>
|
|
@@ -187,11 +181,8 @@ Deploy the agent as its own service. The `Agent` handles LLM chat, tool orchestr
|
|
|
187
181
|
|
|
188
182
|
```python
|
|
189
183
|
# agent_service.py
|
|
190
|
-
import asyncio
|
|
191
184
|
from calfkit.nodes import Agent
|
|
192
185
|
from calfkit.providers import OpenAIResponsesModelClient
|
|
193
|
-
from calfkit.client import Client
|
|
194
|
-
from calfkit.worker import Worker
|
|
195
186
|
from weather_tool import get_weather # Import the tool definition (reusable)
|
|
196
187
|
|
|
197
188
|
agent = Agent(
|
|
@@ -201,14 +192,6 @@ agent = Agent(
|
|
|
201
192
|
model_client=OpenAIResponsesModelClient(model_name="gpt-5.4-nano"),
|
|
202
193
|
tools=[get_weather], # Register tool definitions with the agent
|
|
203
194
|
)
|
|
204
|
-
|
|
205
|
-
async def main():
|
|
206
|
-
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
207
|
-
worker = Worker(client, nodes=[agent]) # Initialize a worker with the agent node
|
|
208
|
-
await worker.run() # (Blocking call) Deploy the service to start serving traffic
|
|
209
|
-
|
|
210
|
-
if __name__ == "__main__":
|
|
211
|
-
asyncio.run(main())
|
|
212
195
|
```
|
|
213
196
|
|
|
214
197
|
Set your OpenAI API key:
|
|
@@ -217,10 +200,10 @@ Set your OpenAI API key:
|
|
|
217
200
|
export OPENAI_API_KEY=sk-...
|
|
218
201
|
```
|
|
219
202
|
|
|
220
|
-
|
|
203
|
+
Deploy the agent as its own service (run it alongside the tool service from step 3):
|
|
221
204
|
|
|
222
205
|
```shell
|
|
223
|
-
|
|
206
|
+
calfkit run agent_service:agent
|
|
224
207
|
```
|
|
225
208
|
|
|
226
209
|
<br>
|
|
@@ -240,7 +223,7 @@ async def main():
|
|
|
240
223
|
# Send a request and await the response
|
|
241
224
|
result = await client.execute_node(
|
|
242
225
|
"What's the weather in Tokyo?",
|
|
243
|
-
"
|
|
226
|
+
"weather_agent.input", # The topic the agent subscribes to
|
|
244
227
|
)
|
|
245
228
|
print(f"Assistant: {result.output}")
|
|
246
229
|
|
|
@@ -256,6 +239,34 @@ python invoke.py
|
|
|
256
239
|
|
|
257
240
|
<br>
|
|
258
241
|
|
|
242
|
+
### Deploying to production
|
|
243
|
+
|
|
244
|
+
`calfkit run` is a **development** convenience — it imports your module and starts a worker for you (and `--reload` restarts it on edits). For production, deploy each node with an explicit `Worker` so startup, scaling, and topic governance stay under your control:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
# serve_tool.py — deploy the tool as its own service
|
|
248
|
+
import asyncio
|
|
249
|
+
from calfkit.client import Client
|
|
250
|
+
from calfkit.worker import Worker
|
|
251
|
+
from weather_tool import get_weather
|
|
252
|
+
|
|
253
|
+
async def main():
|
|
254
|
+
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
255
|
+
worker = Worker(client, nodes=[get_weather]) # One service per node
|
|
256
|
+
await worker.run() # (Blocking) serve until stopped
|
|
257
|
+
|
|
258
|
+
if __name__ == "__main__":
|
|
259
|
+
asyncio.run(main())
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```shell
|
|
263
|
+
python serve_tool.py
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
See the **[CLI reference](docs/cli.md)** for every `calfkit run` flag (`--host`, `--provision`, `--reload`, `--app-dir`, …) and the other `calfkit` commands (`mcp`, `topics`).
|
|
267
|
+
|
|
268
|
+
<br>
|
|
269
|
+
|
|
259
270
|
### Structured Outputs (Optional)
|
|
260
271
|
|
|
261
272
|
Agents can be deployed with a `final_output_type` to enforce structured output from the LLM. The output is type-safe and deserialized automatically on the client side.
|
|
@@ -310,6 +321,8 @@ result = await client.execute_node(
|
|
|
310
321
|
)
|
|
311
322
|
```
|
|
312
323
|
|
|
324
|
+
The same `message_history` can carry turns from *multiple* agents — see [`examples/multi_agent_panel/`](examples/multi_agent_panel/) for a multi-agent discussion over one shared transcript.
|
|
325
|
+
|
|
313
326
|
**Runtime dependency injection** — pass runtime data to tools via the `deps` parameter:
|
|
314
327
|
|
|
315
328
|
```python
|
|
@@ -468,7 +481,11 @@ See [`docs/mcp-overview.md`](docs/mcp-overview.md) for the quickstart, deploymen
|
|
|
468
481
|
|
|
469
482
|
## Documentation
|
|
470
483
|
|
|
471
|
-
Full documentation is coming soon. In the meantime, this README serves as the primary reference for getting started with Calfkit.
|
|
484
|
+
Full documentation is coming soon. In the meantime, this README serves as the primary reference for getting started with Calfkit. Deeper guides live in [`docs/`](docs/):
|
|
485
|
+
|
|
486
|
+
- [CLI reference](docs/cli.md) — `calfkit run`, `mcp`, and `topics` commands
|
|
487
|
+
- [MCP adaptor](docs/mcp-overview.md) — expose MCP servers as calfkit tools
|
|
488
|
+
- [Topic provisioning](docs/topic-provisioning.md) — experimental dev topic auto-creation
|
|
472
489
|
|
|
473
490
|
Deep-dive guides:
|
|
474
491
|
|
|
@@ -57,9 +57,11 @@ Calfkit is a Python SDK that builds event-stream agents out-the-box. You get the
|
|
|
57
57
|
### 1. Install
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
pip install calfkit
|
|
60
|
+
pip install "calfkit[cli]"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
The `[cli]` extra adds the `calfkit` command used below to run nodes during development. (The library itself is just `pip install calfkit`.)
|
|
64
|
+
|
|
63
65
|
<br>
|
|
64
66
|
|
|
65
67
|
### 2. Start a Calfkit Broker
|
|
@@ -97,10 +99,11 @@ You will be provided a Calfkit broker API to deploy your agents instead of setti
|
|
|
97
99
|
> created on first use. Most hardened/managed brokers have that **disabled**, in
|
|
98
100
|
> which case producers and consumers silently stall on a missing topic. Calfkit
|
|
99
101
|
> ships an **EXPERIMENTAL, opt-in** topic provisioner (off by default) for the
|
|
100
|
-
> dev/CI case
|
|
101
|
-
>
|
|
102
|
-
>
|
|
103
|
-
>
|
|
102
|
+
> dev/CI case — pass `--provision` to `calfkit run` below (or
|
|
103
|
+
> `Client.connect("localhost:9092", provisioning=ProvisioningConfig(enabled=True))`
|
|
104
|
+
> in code). It is a development convenience (`replication_factor=1`, no ACLs) —
|
|
105
|
+
> **review it before production**, where topic creation is typically ops-governed.
|
|
106
|
+
> See [`docs/topic-provisioning.md`](docs/topic-provisioning.md).
|
|
104
107
|
|
|
105
108
|
<br>
|
|
106
109
|
|
|
@@ -110,30 +113,19 @@ Define and deploy a tool as an independent service. Tools are not owned by or co
|
|
|
110
113
|
|
|
111
114
|
```python
|
|
112
115
|
# weather_tool.py
|
|
113
|
-
import asyncio
|
|
114
116
|
from calfkit.nodes import agent_tool
|
|
115
|
-
from calfkit.client import Client
|
|
116
|
-
from calfkit.worker import Worker
|
|
117
117
|
|
|
118
118
|
# Define a tool — the @agent_tool decorator turns any function into a deployable tool node
|
|
119
119
|
@agent_tool
|
|
120
120
|
def get_weather(location: str) -> str:
|
|
121
121
|
"""Get the current weather at a location"""
|
|
122
122
|
return f"It's sunny in {location}"
|
|
123
|
-
|
|
124
|
-
async def main():
|
|
125
|
-
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
126
|
-
worker = Worker(client, nodes=[get_weather]) # Initialize a worker with the tool node
|
|
127
|
-
await worker.run() # (Blocking call) Deploy the service to start serving traffic
|
|
128
|
-
|
|
129
|
-
if __name__ == "__main__":
|
|
130
|
-
asyncio.run(main())
|
|
131
123
|
```
|
|
132
124
|
|
|
133
|
-
|
|
125
|
+
Deploy the tool as a service. `calfkit run` points at a `module:attr` target and starts a worker for you — no `Client`/`Worker` wiring required:
|
|
134
126
|
|
|
135
127
|
```shell
|
|
136
|
-
|
|
128
|
+
calfkit run weather_tool:get_weather
|
|
137
129
|
```
|
|
138
130
|
|
|
139
131
|
<br>
|
|
@@ -144,11 +136,8 @@ Deploy the agent as its own service. The `Agent` handles LLM chat, tool orchestr
|
|
|
144
136
|
|
|
145
137
|
```python
|
|
146
138
|
# agent_service.py
|
|
147
|
-
import asyncio
|
|
148
139
|
from calfkit.nodes import Agent
|
|
149
140
|
from calfkit.providers import OpenAIResponsesModelClient
|
|
150
|
-
from calfkit.client import Client
|
|
151
|
-
from calfkit.worker import Worker
|
|
152
141
|
from weather_tool import get_weather # Import the tool definition (reusable)
|
|
153
142
|
|
|
154
143
|
agent = Agent(
|
|
@@ -158,14 +147,6 @@ agent = Agent(
|
|
|
158
147
|
model_client=OpenAIResponsesModelClient(model_name="gpt-5.4-nano"),
|
|
159
148
|
tools=[get_weather], # Register tool definitions with the agent
|
|
160
149
|
)
|
|
161
|
-
|
|
162
|
-
async def main():
|
|
163
|
-
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
164
|
-
worker = Worker(client, nodes=[agent]) # Initialize a worker with the agent node
|
|
165
|
-
await worker.run() # (Blocking call) Deploy the service to start serving traffic
|
|
166
|
-
|
|
167
|
-
if __name__ == "__main__":
|
|
168
|
-
asyncio.run(main())
|
|
169
150
|
```
|
|
170
151
|
|
|
171
152
|
Set your OpenAI API key:
|
|
@@ -174,10 +155,10 @@ Set your OpenAI API key:
|
|
|
174
155
|
export OPENAI_API_KEY=sk-...
|
|
175
156
|
```
|
|
176
157
|
|
|
177
|
-
|
|
158
|
+
Deploy the agent as its own service (run it alongside the tool service from step 3):
|
|
178
159
|
|
|
179
160
|
```shell
|
|
180
|
-
|
|
161
|
+
calfkit run agent_service:agent
|
|
181
162
|
```
|
|
182
163
|
|
|
183
164
|
<br>
|
|
@@ -197,7 +178,7 @@ async def main():
|
|
|
197
178
|
# Send a request and await the response
|
|
198
179
|
result = await client.execute_node(
|
|
199
180
|
"What's the weather in Tokyo?",
|
|
200
|
-
"
|
|
181
|
+
"weather_agent.input", # The topic the agent subscribes to
|
|
201
182
|
)
|
|
202
183
|
print(f"Assistant: {result.output}")
|
|
203
184
|
|
|
@@ -213,6 +194,34 @@ python invoke.py
|
|
|
213
194
|
|
|
214
195
|
<br>
|
|
215
196
|
|
|
197
|
+
### Deploying to production
|
|
198
|
+
|
|
199
|
+
`calfkit run` is a **development** convenience — it imports your module and starts a worker for you (and `--reload` restarts it on edits). For production, deploy each node with an explicit `Worker` so startup, scaling, and topic governance stay under your control:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# serve_tool.py — deploy the tool as its own service
|
|
203
|
+
import asyncio
|
|
204
|
+
from calfkit.client import Client
|
|
205
|
+
from calfkit.worker import Worker
|
|
206
|
+
from weather_tool import get_weather
|
|
207
|
+
|
|
208
|
+
async def main():
|
|
209
|
+
client = Client.connect("localhost:9092") # Connect to Kafka broker
|
|
210
|
+
worker = Worker(client, nodes=[get_weather]) # One service per node
|
|
211
|
+
await worker.run() # (Blocking) serve until stopped
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
asyncio.run(main())
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```shell
|
|
218
|
+
python serve_tool.py
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
See the **[CLI reference](docs/cli.md)** for every `calfkit run` flag (`--host`, `--provision`, `--reload`, `--app-dir`, …) and the other `calfkit` commands (`mcp`, `topics`).
|
|
222
|
+
|
|
223
|
+
<br>
|
|
224
|
+
|
|
216
225
|
### Structured Outputs (Optional)
|
|
217
226
|
|
|
218
227
|
Agents can be deployed with a `final_output_type` to enforce structured output from the LLM. The output is type-safe and deserialized automatically on the client side.
|
|
@@ -267,6 +276,8 @@ result = await client.execute_node(
|
|
|
267
276
|
)
|
|
268
277
|
```
|
|
269
278
|
|
|
279
|
+
The same `message_history` can carry turns from *multiple* agents — see [`examples/multi_agent_panel/`](examples/multi_agent_panel/) for a multi-agent discussion over one shared transcript.
|
|
280
|
+
|
|
270
281
|
**Runtime dependency injection** — pass runtime data to tools via the `deps` parameter:
|
|
271
282
|
|
|
272
283
|
```python
|
|
@@ -425,7 +436,11 @@ See [`docs/mcp-overview.md`](docs/mcp-overview.md) for the quickstart, deploymen
|
|
|
425
436
|
|
|
426
437
|
## Documentation
|
|
427
438
|
|
|
428
|
-
Full documentation is coming soon. In the meantime, this README serves as the primary reference for getting started with Calfkit.
|
|
439
|
+
Full documentation is coming soon. In the meantime, this README serves as the primary reference for getting started with Calfkit. Deeper guides live in [`docs/`](docs/):
|
|
440
|
+
|
|
441
|
+
- [CLI reference](docs/cli.md) — `calfkit run`, `mcp`, and `topics` commands
|
|
442
|
+
- [MCP adaptor](docs/mcp-overview.md) — expose MCP servers as calfkit tools
|
|
443
|
+
- [Topic provisioning](docs/topic-provisioning.md) — experimental dev topic auto-creation
|
|
429
444
|
|
|
430
445
|
Deep-dive guides:
|
|
431
446
|
|
|
@@ -7,6 +7,7 @@ An index of potential features and changes under consideration for Calf SDK. Eac
|
|
|
7
7
|
- [Deps-as-dict refactor](docs/deps-as-dict-refactor.md) — `ctx.deps`/`result.deps` are plain dicts and `correlation_id` is a top-level context attribute (closes #144; breaking)
|
|
8
8
|
- [Fire-and-forget emit](docs/fire-and-forget-emit.md) — true one-way Client.emit_to_node; nullable callback terminal; opt-in reply TTL (closes #132)
|
|
9
9
|
- [Topic Provisioning](docs/topic-provisioning.md) — EXPERIMENTAL opt-in `ProvisioningConfig` to best-effort create Kafka topics for dev/CI (off by default; review for prod)
|
|
10
|
+
- [Agent-POV History Projection](docs/agent-pov-projection.md) — always-on POV projection over `message_history` + `ModelResponse.name` stamping for shared-channel multi-agent; portable content-prefix attribution (PR #185; closes #154)
|
|
10
11
|
|
|
11
12
|
## Proposed
|
|
12
13
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""calfkit top-level CLI entry point.
|
|
2
2
|
|
|
3
3
|
Mounts subcommands as typer sub-apps: ``mcp`` (with ``mcp codegen`` /
|
|
4
|
-
``mcp schema``) and ``topics`` (with ``topics provision``)
|
|
5
|
-
subcommands land alongside via the same
|
|
4
|
+
``mcp schema``) and ``topics`` (with ``topics provision``), plus the
|
|
5
|
+
top-level ``run`` command. Future subcommands land alongside via the same
|
|
6
|
+
mounting pattern.
|
|
6
7
|
|
|
7
8
|
Invoked via the ``calfkit`` console script registered in pyproject.toml's
|
|
8
9
|
``[project.scripts]``.
|
|
@@ -27,11 +28,13 @@ def _build_app() -> Any:
|
|
|
27
28
|
raise SystemExit("The calfkit CLI requires the 'cli' optional extra. Install with: pip install calfkit[cli]") from e
|
|
28
29
|
|
|
29
30
|
from calfkit.cli.mcp import app as mcp_app
|
|
31
|
+
from calfkit.cli.run import run as run_command
|
|
30
32
|
from calfkit.cli.topics import app as topics_app
|
|
31
33
|
|
|
32
34
|
app = typer.Typer(name="calfkit", help="Calfkit SDK command-line tools.", no_args_is_help=True)
|
|
33
35
|
app.add_typer(mcp_app, name="mcp")
|
|
34
36
|
app.add_typer(topics_app, name="topics")
|
|
37
|
+
app.command(name="run")(run_command)
|
|
35
38
|
return app
|
|
36
39
|
|
|
37
40
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Shared node loader for the calfkit CLI.
|
|
2
|
+
|
|
3
|
+
Resolves ``module:attr`` specs into a flat list of node objects. Used by both
|
|
4
|
+
``calfkit topics provision`` (``--nodes module:attr``) and ``calfkit run``
|
|
5
|
+
(positional ``module:attr`` targets).
|
|
6
|
+
|
|
7
|
+
Each ``attr`` may resolve to a single node or an iterable of nodes; iterables
|
|
8
|
+
are expanded. ``McpServer`` instances are treated as scalars even though they
|
|
9
|
+
are iterable (their ``__iter__`` yields tool defs) so a directly-resolved MCP
|
|
10
|
+
server is preserved rather than splatted.
|
|
11
|
+
|
|
12
|
+
Requires the ``cli`` optional extra (typer). If typer is not installed, the
|
|
13
|
+
import raises with a clear remediation message rather than silently failing.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import importlib
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from collections.abc import Iterable
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import typer
|
|
26
|
+
except ImportError as e: # pragma: no cover -- exercised manually
|
|
27
|
+
raise ImportError("the calfkit CLI requires the 'cli' optional extra. Install with: pip install calfkit[cli]") from e
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def resolve_specs(specs: list[str], *, app_dir: str | None = None, source_label: str = "target") -> list[Any]:
|
|
31
|
+
"""Resolve ``module:attr`` specs into a flat list of resolved objects.
|
|
32
|
+
|
|
33
|
+
Each ``attr`` may be a single object or an iterable of objects; iterables
|
|
34
|
+
(other than ``str``/``bytes`` and ``McpServer``) are expanded. Results are
|
|
35
|
+
concatenated in the order the specs were supplied.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
specs: ``module:attr`` strings.
|
|
39
|
+
app_dir: Optional directory inserted at ``sys.path[0]`` before importing
|
|
40
|
+
so targets are resolvable relative to it (e.g. the project root the
|
|
41
|
+
user runs the command from). The path is absolutised; duplicates are
|
|
42
|
+
not re-inserted.
|
|
43
|
+
source_label: How to name the spec source in error messages (e.g.
|
|
44
|
+
``"target"`` for ``calfkit run`` or ``"--nodes"`` for
|
|
45
|
+
``calfkit topics provision``).
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
typer.Exit: (code 2) on a malformed spec, an import failure, or a
|
|
49
|
+
missing attribute.
|
|
50
|
+
"""
|
|
51
|
+
from calfkit.mcp._server import McpServer
|
|
52
|
+
|
|
53
|
+
if app_dir is not None:
|
|
54
|
+
abs_dir = os.path.abspath(app_dir)
|
|
55
|
+
if abs_dir not in sys.path:
|
|
56
|
+
sys.path.insert(0, abs_dir)
|
|
57
|
+
|
|
58
|
+
resolved: list[Any] = []
|
|
59
|
+
for spec in specs:
|
|
60
|
+
module_path, sep, attr = spec.partition(":")
|
|
61
|
+
if not sep or not module_path or not attr:
|
|
62
|
+
typer.echo(
|
|
63
|
+
f"Error: {source_label} must be in 'module:attr' form, got {spec!r}.",
|
|
64
|
+
err=True,
|
|
65
|
+
)
|
|
66
|
+
raise typer.Exit(2)
|
|
67
|
+
try:
|
|
68
|
+
module = importlib.import_module(module_path)
|
|
69
|
+
except (ModuleNotFoundError, ImportError) as e:
|
|
70
|
+
# The module (or one of its imports) does not exist / cannot load.
|
|
71
|
+
typer.echo(f"Error: cannot import module {module_path!r}: {e}", err=True)
|
|
72
|
+
raise typer.Exit(2) from e
|
|
73
|
+
except Exception as e: # noqa: BLE001 -- side-effect code at import time failed
|
|
74
|
+
# The module exists but its top-level code raised when executed.
|
|
75
|
+
typer.echo(
|
|
76
|
+
f"Error: module {module_path!r} raised during import (side-effect code failed): {e}",
|
|
77
|
+
err=True,
|
|
78
|
+
)
|
|
79
|
+
raise typer.Exit(2) from e
|
|
80
|
+
try:
|
|
81
|
+
obj = getattr(module, attr)
|
|
82
|
+
except AttributeError as e:
|
|
83
|
+
typer.echo(f"Error: module {module_path!r} has no attribute {attr!r}.", err=True)
|
|
84
|
+
raise typer.Exit(2) from e
|
|
85
|
+
|
|
86
|
+
# A single node has ``subscribe_topics`` but is not itself a plain
|
|
87
|
+
# iterable of nodes. Strings/bytes are iterable too; an McpServer is
|
|
88
|
+
# iterable (its ``__iter__`` yields tool defs) but must be kept whole.
|
|
89
|
+
# Treat all three as scalars; expand everything else iterable.
|
|
90
|
+
if isinstance(obj, (str, bytes, McpServer)) or not isinstance(obj, Iterable):
|
|
91
|
+
resolved.append(obj)
|
|
92
|
+
else:
|
|
93
|
+
resolved.extend(obj)
|
|
94
|
+
return resolved
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def validate_nodes(objs: list[Any], *, source_label: str = "target") -> None:
|
|
98
|
+
"""Ensure every object is a node or an ``McpServer``.
|
|
99
|
+
|
|
100
|
+
A node is duck-typed by the attributes the worker wiring relies on
|
|
101
|
+
(``subscribe_topics`` and ``_return_topic``). ``McpServer`` instances are
|
|
102
|
+
allowed through unconditionally — the worker expands them into per-tool
|
|
103
|
+
bridges at startup.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
objs: Resolved objects to validate.
|
|
107
|
+
source_label: How to name the spec source in error messages.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
typer.Exit: (code 2) if any object is neither a node nor an McpServer.
|
|
111
|
+
"""
|
|
112
|
+
from calfkit.mcp._server import McpServer
|
|
113
|
+
|
|
114
|
+
for obj in objs:
|
|
115
|
+
if isinstance(obj, McpServer):
|
|
116
|
+
continue
|
|
117
|
+
missing = [attr for attr in ("subscribe_topics", "_return_topic") if not hasattr(obj, attr)]
|
|
118
|
+
if missing:
|
|
119
|
+
typer.echo(
|
|
120
|
+
f"Error: resolved object {obj!r} is not a node "
|
|
121
|
+
f"(missing {', '.join(missing)}). {source_label} must point at a "
|
|
122
|
+
"BaseNodeDef or McpServer (or an iterable of them).",
|
|
123
|
+
err=True,
|
|
124
|
+
)
|
|
125
|
+
raise typer.Exit(2)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def load_nodes(targets: list[str], *, app_dir: str | None = None, source_label: str = "target") -> list[Any]:
|
|
129
|
+
"""Resolve, validate, and de-duplicate ``targets`` into a non-empty node list.
|
|
130
|
+
|
|
131
|
+
The composition used by ``calfkit run`` for both the inline (no-reload) path
|
|
132
|
+
and the parent-side pre-flight before the reload supervisor starts. Raising
|
|
133
|
+
on an empty result means a misconfigured run fails fast and loud (exit 2)
|
|
134
|
+
instead of starting an idle worker.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
typer.Exit: (code 2) on a bad spec, import failure, non-node object, or
|
|
138
|
+
if ``targets`` resolve to zero nodes.
|
|
139
|
+
"""
|
|
140
|
+
objs = resolve_specs(targets, app_dir=app_dir, source_label=source_label)
|
|
141
|
+
validate_nodes(objs, source_label=source_label)
|
|
142
|
+
nodes = dedupe_by_node_id(objs)
|
|
143
|
+
if not nodes:
|
|
144
|
+
typer.echo("Error: no nodes resolved from the given target(s).", err=True)
|
|
145
|
+
raise typer.Exit(2)
|
|
146
|
+
return nodes
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def dedupe_by_node_id(objs: list[Any]) -> list[Any]:
|
|
150
|
+
"""Drop duplicate nodes by ``node_id``, preserving first-seen order.
|
|
151
|
+
|
|
152
|
+
Objects without a ``node_id`` (e.g. ``McpServer``) are keyed by identity so
|
|
153
|
+
they are always retained.
|
|
154
|
+
"""
|
|
155
|
+
seen: set[Any] = set()
|
|
156
|
+
out: list[Any] = []
|
|
157
|
+
for obj in objs:
|
|
158
|
+
key = getattr(obj, "node_id", None)
|
|
159
|
+
if key is None:
|
|
160
|
+
key = id(obj)
|
|
161
|
+
if key in seen:
|
|
162
|
+
continue
|
|
163
|
+
seen.add(key)
|
|
164
|
+
out.append(obj)
|
|
165
|
+
return out
|