-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: slack agent enpoint #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import os | ||
| from pathlib import Path | ||
|
|
||
| from dotenv import load_dotenv | ||
| from fastapi import APIRouter | ||
| from pydantic import BaseModel | ||
| from pydantic_ai import Agent | ||
| from pydantic_ai.models.openai import OpenAIChatModel | ||
| from pydantic_ai.providers.openrouter import OpenRouterProvider | ||
| from slack_sdk import WebClient | ||
|
|
||
| from app.tools import slack | ||
|
|
||
| _ = load_dotenv() | ||
|
|
||
| open_router_token = os.environ.get("OPEN_ROUTER_TOKEN", "") | ||
| slack_token = os.environ.get("SLACK_USER_TOKEN", "") | ||
| client = WebClient(token=slack_token) | ||
|
|
||
| prompt_path = Path(__file__).resolve().parents[2] / "pompts" / "slack.md" | ||
| prompt_instructions = prompt_path.read_text(encoding="utf-8") | ||
|
|
||
| model = OpenAIChatModel( | ||
| "z-ai/glm-4.5", | ||
| provider=OpenRouterProvider(api_key=open_router_token), | ||
| ) | ||
|
|
||
| agent = Agent( | ||
| model=model, | ||
| deps_type=slack.Deps, | ||
| instructions=prompt_instructions, | ||
| ) | ||
|
|
||
|
|
||
| class Prompt(BaseModel): | ||
| user_instruction: str | ||
|
|
||
|
|
||
| router = APIRouter() | ||
|
|
||
|
|
||
| @router.post("/slack", tags=["slack"]) | ||
| def prompt_slack_agent(prompt: Prompt): | ||
| try: | ||
| result = agent.run_sync( | ||
| f"{prompt.user_instruction}", | ||
| deps=slack.Deps(client=client), | ||
| toolsets=[slack.slack_toolset], # type: ignore | ||
| ) | ||
| return {"content": result.output, "status": "ok"} | ||
| except Exception as e: | ||
| return {"msg": str(e), "status": "failed"} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| from fastapi import FastAPI | ||
|
|
||
| from app.api.routers import health | ||
| from app.api.routers import health, slack_agent | ||
|
|
||
| app = FastAPI(title="AVA Agents", version="0.1.0") | ||
| app.include_router(health.router) | ||
| app.include_router(slack_agent.router) |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Role: You are “AVA Slack Operator,” an automation agent that executes Slack tasks for the user | ||
|
|
||
| ## Core objectives | ||
|
|
||
| - Carefully understand the user’s goal, constraints, and urgency. | ||
| - Plan the steps before acting; pick the minimal, correct tool(s) to complete the task. | ||
| - Execute steps in the right order. Verify results where possible. | ||
|
|
||
| ## Tool usage | ||
|
|
||
| - Use the provided toolsets to perform actions. Do not simulate results. | ||
| - When posting a message, use the tool slack.chat.postMessage with parameters: | ||
|
|
||
| ```text | ||
| channel: Slack channel name or ID (e.g., “general”, “#general”, “C123…”). Normalize “#general” to “general” unless an ID is provided. | ||
| text: The exact message to send. Preserve formatting, emojis (:rocket:), mentions, code blocks, and links exactly as the user provided unless asked to revise. | ||
| ``` | ||
|
|
||
| ## Information extraction | ||
|
|
||
| Robustly extract the target channel and message text from natural language: | ||
|
|
||
| - Examples to recognize: “post ‘hello’ in general”, “send to #general: hello”, “announce in general → hello”, “publish the following in general: …” | ||
| - If the user wraps the message in quotes, treat the quoted portion as exact text. | ||
| - If multiple channels or none are specified, ask for clarification before posting. | ||
| - Avoid adding your own commentary to the message unless explicitly requested. | ||
|
|
||
| ## Confirmation policy | ||
|
|
||
| - If the channel is ambiguous, message content is unclear, or the action seems risky (e.g., mass mentions like @channel/@here), ask for confirmation. | ||
| - If the user gives both a clear channel and exact message, proceed without extra confirmation. | ||
|
|
||
| ## Safety and etiquette | ||
|
|
||
| - Do not leak secrets or tokens. Never echo environment values or credentials. | ||
| - Respect team etiquette; avoid mass mentions unless explicitly directed. | ||
|
|
||
| ## Output style | ||
|
|
||
| - Internally plan your steps, but don’t expose internal reasoning. | ||
| - After tool execution, return a concise confirmation describing what was done and any important result data (e.g., message timestamp). | ||
|
|
||
| ## Examples | ||
|
|
||
| - User: “Post this message ‘Deployment succeeded :tada:’ in channel ‘general’.” | ||
| - Plan: Extract channel=“general”, text=“Deployment succeeded :tada:”. | ||
| - Tool: slack.chat.postMessage {channel: “general”, text: “Deployment succeeded :tada:”} | ||
| - User: “Send to #general: We’ll restart at 5pm.” | ||
| - Plan: channel=“general”, text=“We’ll restart at 5pm.” | ||
| - Tool: slack.chat.postMessage {channel: “general”, text: “We’ll restart at 5pm.”} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from fastapi import FastAPI | ||
| from fastapi.testclient import TestClient | ||
|
|
||
| from app.api.routers import slack_agent | ||
|
|
||
| app = FastAPI(title="testing API") | ||
| app.include_router(slack_agent.router) | ||
|
|
||
|
|
||
| client = TestClient(app) | ||
| instructions = ( | ||
| "Post in the general channel that our team meeting will be delayed by 30 minutes" | ||
| ) | ||
|
|
||
|
|
||
| def test_read_main(): | ||
| response = client.post("/slack", json={"user_instruction": f"{instructions}"}) | ||
| assert response.json()["status"] == "ok" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Guard Slack endpoint test behind env checks
The new integration test posts to the Slack agent and assumes a live OpenRouter/Slack configuration (
response.json()["status"] == "ok"). When theSLACK_USER_TOKENandOPEN_ROUTER_TOKENsecrets are not available—typical for local contributors—the router returns{..., "status": "failed"}and this assertion will fail, making the entire suite unusable. The other Slack tests already skip when tokens are missing; this one should do the same or mock the agent so tests can be run without external credentials.Useful? React with 👍 / 👎.