Allow mocking tools over API and python sdk

This commit is contained in:
Ramnique Singh 2025-07-16 16:35:03 +05:30
parent c72728bf3d
commit 8ffdb66e0f
8 changed files with 54 additions and 18 deletions

View file

@ -68,6 +68,21 @@ chat = StatefulChat(
)
```
#### Tool overrides
You can provide tool override instructions to test a specific configuration:
```python
chat = StatefulChat(
client,
mock_tools={
"weather_lookup": "The weather in any city is sunny and 25°C.",
"calculator": "The result of any calculation is 42.",
"search": "Search results for any query return 'No relevant information found.'"
}
)
```
### Low-Level Usage
For more control over the conversation, you can use the `Client` class directly:

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "rowboat"
version = "3.1.0"
version = "4.0.0"
authors = [
{ name = "Ramnique Singh", email = "ramnique@rowboatlabs.com" },
]

View file

@ -22,13 +22,15 @@ class Client:
messages: List[ApiMessage],
state: Optional[Dict[str, Any]] = None,
workflow_id: Optional[str] = None,
test_profile_id: Optional[str] = None
test_profile_id: Optional[str] = None,
mock_tools: Optional[Dict[str, str]] = None
) -> ApiResponse:
request = ApiRequest(
messages=messages,
state=state,
workflowId=workflow_id,
testProfileId=test_profile_id
testProfileId=test_profile_id,
mockTools=mock_tools
)
json_data = request.model_dump()
response = requests.post(self.base_url, headers=self.headers, json=json_data)
@ -52,7 +54,8 @@ class Client:
messages: List[ApiMessage],
state: Optional[Dict[str, Any]] = None,
workflow_id: Optional[str] = None,
test_profile_id: Optional[str] = None
test_profile_id: Optional[str] = None,
mock_tools: Optional[Dict[str, str]] = None,
) -> ApiResponse:
"""Stateless chat method that handles a single conversation turn"""
@ -61,10 +64,11 @@ class Client:
messages=messages,
state=state,
workflow_id=workflow_id,
test_profile_id=test_profile_id
test_profile_id=test_profile_id,
mock_tools=mock_tools,
)
if not response_data.messages[-1].agenticResponseType == 'external':
if not response_data.messages[-1].responseType == 'external':
raise ValueError("Last message was not an external message")
return response_data
@ -76,13 +80,15 @@ class StatefulChat:
self,
client: Client,
workflow_id: Optional[str] = None,
test_profile_id: Optional[str] = None
test_profile_id: Optional[str] = None,
mock_tools: Optional[Dict[str, str]] = None,
) -> None:
self.client = client
self.messages: List[ApiMessage] = []
self.state: Optional[Dict[str, Any]] = None
self.workflow_id = workflow_id
self.test_profile_id = test_profile_id
self.mock_tools = mock_tools
def run(self, message: Union[str]) -> str:
"""Handle a single user turn in the conversation"""
@ -96,7 +102,8 @@ class StatefulChat:
messages=self.messages,
state=self.state,
workflow_id=self.workflow_id,
test_profile_id=self.test_profile_id
test_profile_id=self.test_profile_id,
mock_tools=self.mock_tools,
)
# Update internal state

View file

@ -1,4 +1,4 @@
from typing import List, Optional, Union, Any, Literal
from typing import List, Optional, Union, Any, Literal, Dict
from pydantic import BaseModel
class SystemMessage(BaseModel):
@ -12,8 +12,8 @@ class UserMessage(BaseModel):
class AssistantMessage(BaseModel):
role: Literal['assistant']
content: str
agenticSender: Optional[str] = None
agenticResponseType: Literal['internal', 'external']
agenticName: Optional[str] = None
responseType: Literal['internal', 'external']
class FunctionCall(BaseModel):
name: str
@ -27,15 +27,14 @@ class ToolCall(BaseModel):
class AssistantMessageWithToolCalls(BaseModel):
role: Literal['assistant']
content: Optional[str] = None
tool_calls: List[ToolCall]
agenticSender: Optional[str] = None
agenticResponseType: Literal['internal', 'external']
toolCalls: List[ToolCall]
agenticName: Optional[str] = None
class ToolMessage(BaseModel):
role: Literal['tool']
content: str
tool_call_id: str
tool_name: str
toolCallId: str
toolName: str
ApiMessage = Union[
SystemMessage,
@ -50,7 +49,8 @@ class ApiRequest(BaseModel):
state: Any
workflowId: Optional[str] = None
testProfileId: Optional[str] = None
mockTools: Optional[Dict[str, str]] = None
class ApiResponse(BaseModel):
messages: List[ApiMessage]
state: Any
state: Optional[Any] = None