calfkit 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- calfkit/__init__.py +4 -0
- calfkit/broker/__init__.py +3 -0
- calfkit/broker/broker.py +34 -0
- calfkit/broker/deployable.py +6 -0
- calfkit/broker/middleware.py +15 -0
- calfkit/experimental/rpc_worker.py +51 -0
- calfkit/messages/__init__.py +9 -0
- calfkit/messages/util.py +99 -0
- calfkit/models/event_envelope.py +43 -0
- calfkit/models/types.py +65 -0
- calfkit/nodes/__init__.py +16 -0
- calfkit/nodes/agent_router_node.py +194 -0
- calfkit/nodes/base_node.py +62 -0
- calfkit/nodes/base_tool_node.py +55 -0
- calfkit/nodes/chat_node.py +48 -0
- calfkit/nodes/registrator.py +11 -0
- calfkit/providers/__init__.py +5 -0
- calfkit/providers/pydantic_ai/__init__.py +3 -0
- calfkit/providers/pydantic_ai/openai.py +61 -0
- calfkit/runners/__init__.py +14 -0
- calfkit/runners/node_runner.py +38 -0
- calfkit/stores/__init__.py +38 -0
- calfkit/stores/base.py +60 -0
- calfkit/stores/in_memory.py +29 -0
- calfkit-0.1.1.dist-info/METADATA +129 -0
- calfkit-0.1.1.dist-info/RECORD +28 -0
- calfkit-0.1.1.dist-info/WHEEL +4 -0
- calfkit-0.1.1.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from typing import Any, cast
|
|
3
|
+
|
|
4
|
+
from pydantic_ai import ModelResponse, ModelSettings
|
|
5
|
+
from pydantic_ai.direct import model_request
|
|
6
|
+
from pydantic_ai.models import Model, ModelRequestParameters
|
|
7
|
+
|
|
8
|
+
from calfkit.models.event_envelope import EventEnvelope
|
|
9
|
+
from calfkit.nodes.base_node import BaseNode, publish_to, subscribe_to
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ChatNode(BaseNode, ABC):
|
|
13
|
+
"""Node defining the llm chat node internal wiring.
|
|
14
|
+
Separate from any logic for LLM persona or behaviour."""
|
|
15
|
+
|
|
16
|
+
_on_enter_topic_name = "ai_prompted"
|
|
17
|
+
_post_to_topic_name = "ai_generated"
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
model_client: Model | None = None,
|
|
22
|
+
*,
|
|
23
|
+
request_parameters: ModelRequestParameters | None = None,
|
|
24
|
+
**kwargs: Any,
|
|
25
|
+
):
|
|
26
|
+
self.model_client = model_client
|
|
27
|
+
self.request_parameters = request_parameters
|
|
28
|
+
super().__init__(**kwargs)
|
|
29
|
+
|
|
30
|
+
@subscribe_to(_on_enter_topic_name)
|
|
31
|
+
@publish_to(_post_to_topic_name)
|
|
32
|
+
async def _call_llm(self, event_envelope: EventEnvelope) -> EventEnvelope:
|
|
33
|
+
if self.model_client is None:
|
|
34
|
+
raise RuntimeError("Unable to handle incoming request because Model client is None.")
|
|
35
|
+
if event_envelope.latest_message_in_history is None:
|
|
36
|
+
raise RuntimeError("latest message must not be None")
|
|
37
|
+
request_parameters = event_envelope.patch_model_request_params or self.request_parameters
|
|
38
|
+
patch_model_settings = event_envelope.patch_model_settings
|
|
39
|
+
model_response: ModelResponse = await model_request(
|
|
40
|
+
model=self.model_client,
|
|
41
|
+
messages=event_envelope.message_history,
|
|
42
|
+
model_settings=cast(ModelSettings | None, patch_model_settings),
|
|
43
|
+
model_request_parameters=request_parameters,
|
|
44
|
+
)
|
|
45
|
+
return_envelope = event_envelope.model_copy(
|
|
46
|
+
update={"kind": "ai_response", "incoming_node_messages": [model_response]}
|
|
47
|
+
)
|
|
48
|
+
return return_envelope
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from calfkit.broker.broker import Broker
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Registrator(ABC):
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def register_on(self, broker: Broker, *args: Any, **kwargs: Any) -> None:
|
|
10
|
+
"""Function to fluently register a node's handlers onto a broker to serve traffic"""
|
|
11
|
+
pass
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from httpx import Timeout
|
|
4
|
+
from pydantic_ai.models.openai import OpenAIChatModel, OpenAIChatModelSettings
|
|
5
|
+
from pydantic_ai.providers.openai import OpenAIProvider
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OpenAIModelClient(OpenAIChatModel):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
model_name: str,
|
|
12
|
+
*,
|
|
13
|
+
base_url: str | None = None,
|
|
14
|
+
api_key: str | None = None,
|
|
15
|
+
reasoning_effort: str | None = None,
|
|
16
|
+
max_tokens: int | None = None,
|
|
17
|
+
temperature: float | None = None,
|
|
18
|
+
top_p: float | None = None,
|
|
19
|
+
timeout: float | Timeout | None = None,
|
|
20
|
+
parallel_tool_calls: bool | None = None,
|
|
21
|
+
seed: int | None = None,
|
|
22
|
+
presence_penalty: float | None = None,
|
|
23
|
+
frequency_penalty: float | None = None,
|
|
24
|
+
logit_bias: dict[str, int] | None = None,
|
|
25
|
+
stop_sequences: list[str] | None = None,
|
|
26
|
+
extra_headers: dict[str, str] | None = None,
|
|
27
|
+
extra_body: object | None = None,
|
|
28
|
+
**kwargs: Any,
|
|
29
|
+
):
|
|
30
|
+
settings_kwargs: dict[str, object] = {}
|
|
31
|
+
if reasoning_effort is not None:
|
|
32
|
+
settings_kwargs["openai_reasoning_effort"] = reasoning_effort
|
|
33
|
+
if max_tokens is not None:
|
|
34
|
+
settings_kwargs["max_tokens"] = max_tokens
|
|
35
|
+
if temperature is not None:
|
|
36
|
+
settings_kwargs["temperature"] = temperature
|
|
37
|
+
if top_p is not None:
|
|
38
|
+
settings_kwargs["top_p"] = top_p
|
|
39
|
+
if timeout is not None:
|
|
40
|
+
settings_kwargs["timeout"] = timeout
|
|
41
|
+
if parallel_tool_calls is not None:
|
|
42
|
+
settings_kwargs["parallel_tool_calls"] = parallel_tool_calls
|
|
43
|
+
if seed is not None:
|
|
44
|
+
settings_kwargs["seed"] = seed
|
|
45
|
+
if presence_penalty is not None:
|
|
46
|
+
settings_kwargs["presence_penalty"] = presence_penalty
|
|
47
|
+
if frequency_penalty is not None:
|
|
48
|
+
settings_kwargs["frequency_penalty"] = frequency_penalty
|
|
49
|
+
if logit_bias is not None:
|
|
50
|
+
settings_kwargs["logit_bias"] = logit_bias
|
|
51
|
+
if stop_sequences is not None:
|
|
52
|
+
settings_kwargs["stop_sequences"] = stop_sequences
|
|
53
|
+
if extra_headers is not None:
|
|
54
|
+
settings_kwargs["extra_headers"] = extra_headers
|
|
55
|
+
if extra_body is not None:
|
|
56
|
+
settings_kwargs["extra_body"] = extra_body
|
|
57
|
+
model_settings: OpenAIChatModelSettings = OpenAIChatModelSettings(**settings_kwargs) # type: ignore[typeddict-item]
|
|
58
|
+
|
|
59
|
+
openai_client = OpenAIProvider(base_url=base_url, api_key=api_key)
|
|
60
|
+
self.model_settings = model_settings
|
|
61
|
+
super().__init__(model_name, provider=openai_client, settings=model_settings)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Calf Agent System.
|
|
2
|
+
|
|
3
|
+
This module provides runners for deploying agent nodes to a message broker.
|
|
4
|
+
Runners handle the registration and lifecycle of agent nodes within the broker system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from calfkit.runners.node_runner import AgentRouterRunner, ChatRunner, NodeRunner, ToolRunner
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"NodeRunner",
|
|
11
|
+
"ChatRunner",
|
|
12
|
+
"ToolRunner",
|
|
13
|
+
"AgentRouterRunner",
|
|
14
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Any, TypeAlias
|
|
2
|
+
|
|
3
|
+
from calfkit.broker.broker import Broker
|
|
4
|
+
from calfkit.nodes.base_node import BaseNode
|
|
5
|
+
from calfkit.nodes.registrator import Registrator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NodeRunner(Registrator):
|
|
9
|
+
"""The NodeRunner makes node logic deployable after registering on a broker. (Control plane)"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, node: BaseNode, *args: Any, **kwargs: Any):
|
|
12
|
+
self.node = node
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
|
|
15
|
+
def register_on(
|
|
16
|
+
self,
|
|
17
|
+
broker: Broker,
|
|
18
|
+
*,
|
|
19
|
+
max_workers: int | None = None,
|
|
20
|
+
# group_id explicitly set as to avoid duplicated processing for separate deployments
|
|
21
|
+
group_id: str = "default",
|
|
22
|
+
extra_publish_kwargs: dict[str, Any] = {},
|
|
23
|
+
extra_subscribe_kwargs: dict[str, Any] = {},
|
|
24
|
+
) -> None:
|
|
25
|
+
for handler_fn, topics_dict in self.node.bound_registry.items():
|
|
26
|
+
pub = topics_dict.get("publish_topic")
|
|
27
|
+
sub = topics_dict.get("subscribe_topic")
|
|
28
|
+
if sub is not None:
|
|
29
|
+
handler_fn = broker.subscriber(
|
|
30
|
+
sub, max_workers=max_workers, group_id=group_id, **extra_subscribe_kwargs
|
|
31
|
+
)(handler_fn)
|
|
32
|
+
if pub is not None:
|
|
33
|
+
handler_fn = broker.publisher(pub, **extra_publish_kwargs)(handler_fn)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
ChatRunner: TypeAlias = NodeRunner
|
|
37
|
+
ToolRunner: TypeAlias = NodeRunner
|
|
38
|
+
AgentRouterRunner: TypeAlias = NodeRunner
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Calf Message History Store System.
|
|
2
|
+
|
|
3
|
+
The message history store provides persistent storage for conversation messages
|
|
4
|
+
in event-driven agent systems. It follows these principles:
|
|
5
|
+
|
|
6
|
+
* Thread-based: Conversations are identified by a thread_id
|
|
7
|
+
* Strong consistency: Messages are persisted atomically as they flow through the system
|
|
8
|
+
* Pluggable backends: Swap between in-memory, PostgreSQL, Redis, etc.
|
|
9
|
+
* Auto-create: New threads are created automatically on first append
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
from calfkit.stores import MemoryMessageHistoryStore
|
|
13
|
+
|
|
14
|
+
# Deployment-time configuration
|
|
15
|
+
store = MemoryMessageHistoryStore()
|
|
16
|
+
|
|
17
|
+
# Inject into router node
|
|
18
|
+
router_node = AgentRouterNode(
|
|
19
|
+
chat_node=chat_node,
|
|
20
|
+
tool_nodes=[get_weather],
|
|
21
|
+
message_store=store,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Invocation-time - specify which conversation
|
|
25
|
+
await router_node.invoke(
|
|
26
|
+
user_prompt="What's the weather?",
|
|
27
|
+
broker=broker,
|
|
28
|
+
thread_id="user-123-conv-456",
|
|
29
|
+
)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from calfkit.stores.base import MessageHistoryStore
|
|
33
|
+
from calfkit.stores.in_memory import InMemoryMessageHistoryStore
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"MessageHistoryStore",
|
|
37
|
+
"InMemoryMessageHistoryStore",
|
|
38
|
+
]
|
calfkit/stores/base.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
|
|
4
|
+
from pydantic_ai.messages import ModelMessage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MessageHistoryStore(ABC):
|
|
8
|
+
"""Abstract store for conversation message history.
|
|
9
|
+
|
|
10
|
+
Designed for strong consistency in event-driven agent systems.
|
|
11
|
+
Messages are persisted atomically as they flow through the agent graph.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
async def get(self, thread_id: str) -> list[ModelMessage]:
|
|
16
|
+
"""Load message history for a thread.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
thread_id: Unique identifier for the conversation thread.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List of messages in the thread. Returns empty list if thread
|
|
23
|
+
doesn't exist (auto-create behavior).
|
|
24
|
+
"""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
async def append(self, thread_id: str, message: ModelMessage) -> None:
|
|
29
|
+
"""Append a single message to history.
|
|
30
|
+
|
|
31
|
+
This is the primary method for strong consistency - each message
|
|
32
|
+
is persisted immediately before the next step in the agentic loop.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
thread_id: Unique identifier for the conversation thread.
|
|
36
|
+
message: The message to append.
|
|
37
|
+
"""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
async def append_many(self, thread_id: str, messages: Sequence[ModelMessage]) -> None:
|
|
41
|
+
"""Append multiple messages to history.
|
|
42
|
+
|
|
43
|
+
Default implementation calls append() for each message.
|
|
44
|
+
Override for batch optimization if needed.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
thread_id: Unique identifier for the conversation thread.
|
|
48
|
+
messages: List of messages to append.
|
|
49
|
+
"""
|
|
50
|
+
for message in messages:
|
|
51
|
+
await self.append(thread_id, message)
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
async def delete(self, thread_id: str) -> None:
|
|
55
|
+
"""Delete all messages for a thread.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
thread_id: Unique identifier for the conversation thread.
|
|
59
|
+
"""
|
|
60
|
+
...
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
|
|
3
|
+
from pydantic_ai.messages import ModelMessage
|
|
4
|
+
|
|
5
|
+
from calfkit.stores.base import MessageHistoryStore
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InMemoryMessageHistoryStore(MessageHistoryStore):
|
|
9
|
+
"""In-memory message history store.
|
|
10
|
+
|
|
11
|
+
Useful for testing and development. Not suitable for production
|
|
12
|
+
as data is lost when the process exits.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
"""Initialize an empty in-memory store."""
|
|
17
|
+
self._messages: dict[str, list[ModelMessage]] = defaultdict(list)
|
|
18
|
+
|
|
19
|
+
async def get(self, thread_id: str) -> list[ModelMessage]:
|
|
20
|
+
"""Load message history for a thread."""
|
|
21
|
+
return list(self._messages.get(thread_id, []))
|
|
22
|
+
|
|
23
|
+
async def append(self, thread_id: str, message: ModelMessage) -> None:
|
|
24
|
+
"""Append a single message to history."""
|
|
25
|
+
self._messages[thread_id].append(message)
|
|
26
|
+
|
|
27
|
+
async def delete(self, thread_id: str) -> None:
|
|
28
|
+
"""Delete all messages for a thread."""
|
|
29
|
+
self._messages.pop(thread_id, None)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: calfkit
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Event-driven SDK for building AI workflows on Kafka
|
|
5
|
+
Project-URL: Homepage, https://github.com/calf-ai/calf-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/calf-ai/calf-sdk
|
|
7
|
+
Project-URL: Issues, https://github.com/calf-ai/calf-sdk/issues
|
|
8
|
+
Author: Ryan Yu
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,event-driven,kafka,llm,workflows
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: faststream[cli]>=0.6.0
|
|
22
|
+
Requires-Dist: pydantic-ai>=1.47.0
|
|
23
|
+
Requires-Dist: pydantic>=2.12.5
|
|
24
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
25
|
+
Requires-Dist: uuid-utils>=0.14.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Calf SDK
|
|
30
|
+
|
|
31
|
+
Event-driven AI agents that scale like microservices. Build loosely-coupled, distributed agent systems where tools, chats, and workflows each run as independent services—communicating through events.
|
|
32
|
+
|
|
33
|
+
## Why Event-Driven Agents?
|
|
34
|
+
|
|
35
|
+
Building agents like traditional web applications—with tight coupling and direct API calls—creates the same scalability problems that plagued early microservices.
|
|
36
|
+
|
|
37
|
+
When agents connect through APIs and RPC:
|
|
38
|
+
- **Tight coupling** — Changing one tool breaks dependent agents
|
|
39
|
+
- **Scaling bottlenecks** — Everything must scale together
|
|
40
|
+
- **Siloed outputs** — Agent responses stay trapped in your AI layer
|
|
41
|
+
|
|
42
|
+
Event-driven architecture provides the solution. Instead of direct API calls between components, agents interact through asynchronous event streams. Each component runs independently, scales horizontally, and outputs can flow anywhere—CRMs, data warehouses, analytics platforms, other agents.
|
|
43
|
+
|
|
44
|
+
## What Calf Gives You
|
|
45
|
+
|
|
46
|
+
Calf is a Python SDK that makes event-driven agents simple. You get the benefits of a distributed system—loose coupling, horizontal scalability, durability—without the complexity of managing Kafka infrastructure yourself.
|
|
47
|
+
|
|
48
|
+
| Benefit | What It Means for You |
|
|
49
|
+
|---------|----------------------|
|
|
50
|
+
| **Add tools without touching existing code** | Deploy new capabilities as independent services that other agents discover automatically |
|
|
51
|
+
| **Scale what you need, when you need it** | Chat handling, tool execution, and routing each scale independently based on demand |
|
|
52
|
+
| **Nothing gets lost** | Event persistence ensures reliable message delivery—even during failures or restarts |
|
|
53
|
+
| **Real-time responses** | Low-latency event processing enables agents to react instantly to incoming data |
|
|
54
|
+
| **Team independence** | Different teams can develop and deploy chat, tools, and routing concurrently without coordination overhead |
|
|
55
|
+
| **Universal data flow** | Decoupling enables data to flow freely in both directions. Downstream, agent outputs integrate with any system (CRMs, CDPs, warehouses). Upstream, tools wrap data sources and deploy independently—no coordination needed. |
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Kafka (single command via Docker)
|
|
63
|
+
docker run -d -p 9092:9092 apache/kafka:latest
|
|
64
|
+
|
|
65
|
+
# Python 3.10+
|
|
66
|
+
python --version
|
|
67
|
+
|
|
68
|
+
# OpenAI API key
|
|
69
|
+
export OPENAI_API_KEY=sk-...
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Install
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install git+https://github.com/calf-ai/calf-sdk.git
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Create and Run an Agent
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import asyncio
|
|
82
|
+
from calfkit.nodes import agent_tool, AgentRouterNode, ChatNode
|
|
83
|
+
from calfkit.providers import OpenAIModelClient
|
|
84
|
+
from calfkit.stores import InMemoryMessageHistoryStore
|
|
85
|
+
from calfkit.broker import Broker
|
|
86
|
+
from calfkit.runners import ChatRunner, ToolRunner, AgentRouterRunner
|
|
87
|
+
|
|
88
|
+
# 1. Define a tool
|
|
89
|
+
@agent_tool
|
|
90
|
+
def get_weather(location: str) -> str:
|
|
91
|
+
"""Get the current weather at a location"""
|
|
92
|
+
return f"It's sunny in {location}"
|
|
93
|
+
|
|
94
|
+
# 2. Setup broker and nodes
|
|
95
|
+
async def main():
|
|
96
|
+
broker = Broker(bootstrap_servers="localhost:9092")
|
|
97
|
+
model_client = OpenAIModelClient(model_name="gpt-4o")
|
|
98
|
+
|
|
99
|
+
# Deploy chat node
|
|
100
|
+
chat_node = ChatNode(model_client)
|
|
101
|
+
ChatRunner(chat_node).register_on(broker)
|
|
102
|
+
|
|
103
|
+
# Deploy tool node
|
|
104
|
+
ToolRunner(get_weather).register_on(broker)
|
|
105
|
+
|
|
106
|
+
# Deploy router node
|
|
107
|
+
router_node = AgentRouterNode(
|
|
108
|
+
chat_node=ChatNode(model_client),
|
|
109
|
+
tool_nodes=[get_weather],
|
|
110
|
+
system_prompt="You are a helpful assistant",
|
|
111
|
+
message_history_store=InMemoryMessageHistoryStore(),
|
|
112
|
+
)
|
|
113
|
+
AgentRouterRunner(router_node).register_on(broker)
|
|
114
|
+
|
|
115
|
+
# Run broker and invoke
|
|
116
|
+
await broker.run_app()
|
|
117
|
+
correlation_id = await router_node.invoke(
|
|
118
|
+
user_prompt="What's the weather in Tokyo?",
|
|
119
|
+
broker=broker,
|
|
120
|
+
final_response_topic="final_response",
|
|
121
|
+
)
|
|
122
|
+
print(f"Request started: {correlation_id}")
|
|
123
|
+
|
|
124
|
+
asyncio.run(main())
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
Apache-2.0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
calfkit/__init__.py,sha256=TNjZ-vfuYWSFldFQniKQvb0XRB55YzNqgLHyAKQcYrg,99
|
|
2
|
+
calfkit/broker/__init__.py,sha256=35HCFxzWmmh0hhBf7bf7Ix7XPUJaS-GQjAKwDpZ-EnI,63
|
|
3
|
+
calfkit/broker/broker.py,sha256=9s8RbkxItA5NcuQX6U7lXqaL2WYrQ8JIZAusD_YARxs,996
|
|
4
|
+
calfkit/broker/deployable.py,sha256=mzvYllCbh4m3lMCdbMsVlOyQ2jTHlx92RYjkHgry1zQ,122
|
|
5
|
+
calfkit/broker/middleware.py,sha256=X9Ta4gLYkivOTTNjLzDXNIJSwLPUOlojvz2SmxgIHco,458
|
|
6
|
+
calfkit/experimental/rpc_worker.py,sha256=_FeUmcS4aFY4IRaz-qrMelFXCGNKdEySE0BSWTOzudE,1439
|
|
7
|
+
calfkit/messages/__init__.py,sha256=4Wv45OrQMI0opPK70D1IyysbjxwdxJP4y56PkkEEtq0,309
|
|
8
|
+
calfkit/messages/util.py,sha256=EamqjS74LlHRBzOgER5o2VWwQFkcxt9Kwtd6QE4pqNw,3295
|
|
9
|
+
calfkit/models/event_envelope.py,sha256=zd10sQ51LgqxroZsxrm3kNkB5hWRtou73iLRdWeig6Y,1607
|
|
10
|
+
calfkit/models/types.py,sha256=PQBJFeiwsoY5najanNkL6IymM3mZYv2voFQR9smcObs,1989
|
|
11
|
+
calfkit/nodes/__init__.py,sha256=nWU-r76xj_cxQ9W6RXs5IDhvXAwsMDAVrx_ZuxRmLgI,457
|
|
12
|
+
calfkit/nodes/agent_router_node.py,sha256=Zo1jApqLdnm2BPQD101Ay9UUVwSg2ADBRFti9V5Ts8A,7362
|
|
13
|
+
calfkit/nodes/base_node.py,sha256=sEynWeQejGV-jmConBTY09fMIfTsnYnvtCIYZOp-vlk,2147
|
|
14
|
+
calfkit/nodes/base_tool_node.py,sha256=t1epxURAkGiqSCcnmPp1f41TacF9mwYnuShngX_7hfQ,2000
|
|
15
|
+
calfkit/nodes/chat_node.py,sha256=F3pBvJxiKcWNdviGtpYtWZFQn3ffc4Ka-mh8yIBw_PQ,1943
|
|
16
|
+
calfkit/nodes/registrator.py,sha256=WJLiZ-aIJ8w9nFHNd0ya6pux4zKaZASDT6MIRCXLpa8,331
|
|
17
|
+
calfkit/providers/__init__.py,sha256=NwS6wYfpJXledImfWVQKAgDsrNJAxzJYde3avdNiBDE,126
|
|
18
|
+
calfkit/providers/pydantic_ai/__init__.py,sha256=5zVl-r7Cj7nQUnIQ41zSDzRYCKi8GwDgT-PXNw8M4OQ,100
|
|
19
|
+
calfkit/providers/pydantic_ai/openai.py,sha256=8fKLvYpiqUzDOBrZxTdxtsB0tDhCUniv_N-FRFeBNvE,2604
|
|
20
|
+
calfkit/runners/__init__.py,sha256=4CHTwdPpkFa-Z3vA1PR1S17F9phsaBRXs_SyFO-9Xgo,379
|
|
21
|
+
calfkit/runners/node_runner.py,sha256=bJ8wk4gPADX2ctmyoXgslwGItOxG2fH2MKk3ORPtLNI,1411
|
|
22
|
+
calfkit/stores/__init__.py,sha256=JIaqYv5OnddLdf3l3KO6ET2Wsd0sOGwxIFiUPRjKn_U,1167
|
|
23
|
+
calfkit/stores/base.py,sha256=hPR0V2HAzrOGO5HNUfZSQ82YEoCKKHfY3EQVpHODpa0,1900
|
|
24
|
+
calfkit/stores/in_memory.py,sha256=ZKGfPdLUxri8Q3otzVsWqaDYFYvDpBjYgWRDRoF2C70,1002
|
|
25
|
+
calfkit-0.1.1.dist-info/METADATA,sha256=tG8BXjPadC5KFUz8vsLzEQYxzH0jxGKAe_5TYxs0WY4,5018
|
|
26
|
+
calfkit-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
27
|
+
calfkit-0.1.1.dist-info/licenses/LICENSE,sha256=nSECZEo9kht4X50TajtlnrVg21bXN4PVs5mSi5ihAGs,11338
|
|
28
|
+
calfkit-0.1.1.dist-info/RECORD,,
|