Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions app/models/plan.py

This file was deleted.

4 changes: 4 additions & 0 deletions app/models/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@


class SlackChatPostMessageParams(BaseModel):
"""
Parameters for the slack.chat.postMessage tool
"""

channel: str
text: str
14 changes: 3 additions & 11 deletions app/tools/registry.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
from collections.abc import Callable
from pydantic_ai.toolsets import FunctionToolset

from slack_sdk.web.slack_response import SlackResponse
from app.tools.slack import slack_toolset

from app.models.tools import SlackChatPostMessageParams
from app.tools.slack import post_message

TOOLS: dict[str, dict[str, type | Callable[..., SlackResponse]]] = {
"slack.chat.postMessage": {
"schema": SlackChatPostMessageParams,
"fn": post_message,
},
}
TOOLS: dict[str, FunctionToolset] = {"slack.tools": slack_toolset}
23 changes: 20 additions & 3 deletions app/tools/slack.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
from dataclasses import dataclass

from pydantic_ai import RunContext
from pydantic_ai.toolsets import FunctionToolset
from slack_sdk import WebClient
from slack_sdk.web.slack_response import SlackResponse

from app.models.tools import SlackChatPostMessageParams


def post_message(token: str, params: SlackChatPostMessageParams) -> SlackResponse:
client = WebClient(token=token)
return client.chat_postMessage(channel=params.channel, text=params.text)
@dataclass
class Deps:
client: WebClient


slack_toolset = FunctionToolset()


@slack_toolset.tool(name="slack.chat.postMessage")
def post_message(
ctx: RunContext[Deps], params: SlackChatPostMessageParams
) -> SlackResponse:
"""
Use this function to post a message in the specified channel
"""
return ctx.deps.client.chat_postMessage(channel=params.channel, text=params.text)
Comment on lines +19 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Return SlackResponse from tool prevents serialization

The new post_message tool returns the SlackResponse object directly. When this function is executed via FunctionToolset, the agent has to JSON‑serialize the tool’s output to send it back to the model. SlackResponse carries non‑serializable attributes (e.g., the underlying WebClient and requests.Response), so the agent will raise a TypeError instead of reporting the Slack call result. Consider returning a plain dict/BaseModel such as response.data so the tool output can be encoded.

Useful? React with 👍 / 👎.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ dev = [
"pre-commit",
"coverage",
"ruff",
"ipython>=8.37.0",
]
77 changes: 77 additions & 0 deletions tests/test_ava_slack_post_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os

import pytest
from dotenv import load_dotenv
from pydantic_ai import Agent, RunContext, RunUsage
from pydantic_ai.models.test import TestModel
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

from app.models import tools
from app.tools import slack

_ = load_dotenv()


def test_health_ava_can_post_message():
token = os.environ.get("SLACK_BOT_TOKEN")
if not token:
pytest.skip("SLACK_BOT_TOKEN not set; live Slack test skipped")

client = WebClient(token=token)
message_text = "AVA backend live smoke :rocket:"

try:
response = client.chat_postMessage(channel="sandbox", text=message_text)
except SlackApiError as exc:
pytest.fail(f"Slack API error: {exc.response['error']}")

assert response["ok"] is True
assert response["message"]["text"] == message_text # type: ignore
assert "ts" in response


@pytest.mark.parametrize(
"channel,text,token",
[
(
"sandbox",
"Hello, AVA backend live smoke :rocket:",
os.environ.get("SLACK_BOT_TOKEN"),
),
(
"general",
"Hello, User is live smoke :rocket:",
os.environ.get("SLACK_USER_TOKEN"),
),
],
)
def test_post_message_with_ctx(channel: str, text: str, token: str | None):
if token is not None:
client = WebClient(token=token)
ctx = RunContext(
deps=slack.Deps(client=client),
model=TestModel(),
usage=RunUsage(),
)
tool_params = tools.SlackChatPostMessageParams(channel=channel, text=text)
try:
response = slack.post_message(ctx=ctx, params=tool_params)
except SlackApiError as exc:
pytest.fail(f"Slack API error: {exc.response['error']}")

assert response["ok"] is True
assert response["message"]["text"] == text
assert "ts" in response


def test_slack_tools_are_synced():
token = os.environ.get("SLACK_BOT_TOKEN")
client = WebClient(token=token)
test_model = TestModel(call_tools=[])
agent = Agent(test_model, toolsets=[slack.slack_toolset], deps_type=slack.Deps)
_ = agent.run_sync("What tools are available?", deps=slack.Deps(client=client))

assert [
t.name for t in test_model.last_model_request_parameters.function_tools
] == ["slack.chat.postMessage"]
26 changes: 0 additions & 26 deletions tests/test_health_ava.py

This file was deleted.

38 changes: 0 additions & 38 deletions tests/test_send_tasks.py

This file was deleted.

Loading