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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
label: project_directory_file_contents
file:
directory: "${PROJECT_DIR}"

meta:
mcp:
enabled: true
description: "the project directory where the user stores files"
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
validate_file_path = mapping("""
let file = content().string()
root.file = if $file.contains("..") {
throw("invalid file path")
} else {
$file
}
""")

mcp_tool(
label = "list_files",
description = "List files in a directory. Use `.` for the current directory.",
processor = processors(
attempt(
validate_file_path,
command(
name="ls",
args_mapping='[["{}", this.file].filepath_join()]'.format(secret("PROJECT_DIR")),
),
mapping("""
root.files = content().split("\\n")
if root.files == [""] {
root = "no files found"
}
""")
),
catch(
mapping(
"""root = ["Directory does not exist"]""",
)
),
)
)

mcp_tool(
label = "lint_connect_configuration_file",
description = "Lint a Connect configuration file, the `value` should be a path to a configuration file.",
processor = processors(
attempt(
validate_file_path,
command(
name="rpk",
args_mapping="""[
"connect",
"lint",
["{}", this.file].filepath_join(),
]""".format(secret("PROJECT_DIR")),
),
mapping('root = "lint success"'),
),
catch(
mapping("root.error = error()"),
)
)
)

mcp_tool(
label = "ask_docs_agent",
description = " ".join([
"Ask the documentation agent for help.",
"This agent has access to the full documentation for Redpanda.",
"The `value` should be the question and mention that's it's for Redpanda Connect specifically.",
]),
processor = attempt(
mapping("""root.query = content().string()"""),
http(
verb = "POST",
url = "https://api.kapa.ai/query/v1/projects/{project_id}/chat/".format(
project_id=secret("KAPA_PROJECT_ID"),
),
headers = {
"Content-Type": "application/json",
"X-API-KEY": secret("KAPA_API_KEY"),
},
timeout = "60s",
)
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
label: 'check_weather'
processors:
- http:
verb: GET
url: 'https://wttr.in/${!content().string()}?T'
headers:
User-Agent: curl/8.11.1 # Returns a text string from the weather website

meta:
mcp:
enabled: true
description: 'A tool that can tell you what the weather is in a city passed as the value'
75 changes: 75 additions & 0 deletions examples/connect_integration/pipeline_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import asyncio
from pathlib import Path
from typing import Any, override

from agents import Agent, RPKMCPEndpoint
from agents.agent import AgentHooks
from agents.tools import Tool


class MyHooks(AgentHooks):
@override
async def on_start(self, agent: Agent) -> None:
print("Agent started")

@override
async def on_end(self, agent: Agent, output: Any) -> None:
print("Agent ended")

@override
async def on_tool_start(
self,
agent: Agent,
tool: Tool,
args: str,
) -> None:
print(f"Agent calling tool {tool.name} with args: {args}")

@override
async def on_tool_end(
self,
agent: Agent,
tool: Tool,
result: str,
) -> None:
print(f"Agent tool {tool.name} resulted in: {result}")


my_agent = Agent(
name="ConnectPipelineDevAgent",
model="openai/gpt-4o",
instructions="""
You are a development agent that helps an engineer create Redpanda Connect pipelines.
Redpanda Connect is a stream processing tool that allows you to move data between different systems.
Redpanda Connect pipelines are defined in a single YAML file usually called `connect.yaml`.
You have access to the developer's project with filesystem access as well as a file to lint pipelines,
which is usually a good idea to use after modifying the `connect.yaml` file.
Additionally, you have access to another agent that has full access to the Redpanda documentation that
can lookup functionality or how the project works in more depth. However, that agent is not specific
for Redpanda Connect so make sure you provide it with context when asking it questions. Please feel free
to ask multiple questions.

Always write the output to the directory.

NOTE: You really need to ask the documentation agent for help. Also there is no opportunity to ask the user
for comfirmation.
""".strip().replace("\n", ""),
mcp=[
RPKMCPEndpoint(directory=Path("mcp")),
],
hooks=MyHooks(),
)


async def main() -> None:
while True:
try:
prompt = input("> ")
except EOFError:
return
response = await my_agent.run(input=prompt)
print(response)


if __name__ == "__main__":
asyncio.run(main())
19 changes: 18 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ authors = [
{ name = "Tyler Rockwood", email = "rockwood@redpanda.com" }
]
requires-python = ">=3.13"
dependencies = []
dependencies = [
"litellm>=1.63.14",
"mcp>=1.5.0",
"pydantic>=2.10.6",
"pyright>=1.1.397",
"websockets>=15.0.1",
]
license = "Apache-2.0"
license-files = ["LICENSE"]

Expand Down Expand Up @@ -60,7 +66,18 @@ packages = ["src/agents"]

[tool.pyright]
reportUnusedCallResult = false
reportExplicitAny = false
reportAny = false
reportUnknownParameterType = false

[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
filterwarnings = [
# https://github.com/BerriAI/litellm/pull/9372
"ignore:.*ConfigDict.*:pydantic.PydanticDeprecatedSince20",
# An error in litellm caching module
"ignore:::litellm",
]


20 changes: 13 additions & 7 deletions src/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
def hello() -> str:
"""
Returns a greeting message.
from .agent import Agent, AgentHooks
from .mcp import MCPEndpoint, RPKMCPEndpoint, SSEMCPEndpoint, StdioMCPEndpoint, WebsocketMCPEndpoint
from .tools import Tool

Returns:
A greeting message.
"""
return "Hello from agent!"
__all__ = [
"Agent",
"AgentHooks",
"Tool",
"MCPEndpoint",
"StdioMCPEndpoint",
"SSEMCPEndpoint",
"WebsocketMCPEndpoint",
"RPKMCPEndpoint",
]
Loading