Skip to content

Commit

Permalink
Update Agent Connector with the new Agent API (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidiuCode authored Oct 9, 2024
1 parent 2b5be84 commit 3e040b8
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 73 deletions.
8 changes: 8 additions & 0 deletions actions/agent-connector/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/)
and this project adheres to [Semantic Versioning](https://semver.org/).

## [2.0.0]

- Support new version of Agent API

## [1.0.1] - 2024-10-09

- Update dependencies

## [1.0.0] - 2024-07-25

First version published, changelog tracking starts.
Expand Down
50 changes: 47 additions & 3 deletions actions/agent-connector/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
# Action Bootstrapping
In Sema4.ai Studio, let your agents talk to each other seamlessly.

TBD
## Overview

The Agent Connector allows agents within the Sema4.ai platform to communicate with each other through a simple and
intuitive interface. This connector provides actions to create communication threads and send messages between agents,
facilitating smooth and efficient inter-agent communication.

Currently supported features:

- Get agents
- Get agent by name
- Get threads
- Get a thread
- Create a thread
- Send a message

## Prompts

TBD
```
use wayback machine to find how robocorp.com has changed over past 2yrs
```

> **Changes to Robocorp.com Over the Past 2 Years**
> Snapshot from September 1, 2022
>
> ... Content shortened ...
>
> Snapshot from August 13, 2024
>
> ... Content shortened ...
>
## How It Works

1. **Creating a Communication Thread**:
- The `create_thread` action allows you to create a new thread for communication with an agent by specifying the
agent's name and a user-defined thread name. This action internally fetches the list of available agents and
validates the agent name before creating the thread.

2. **Sending Messages**:
- The `send_message` action enables you to send messages within a specified thread and retrieve the assistant's
response. This action ensures smooth communication by providing only the relevant content of the response or an
error message if the operation fails.

By using these actions, agents within the Sema4.ai platform can effectively communicate, enabling seamless collaboration
and information exchange.In Sema4.ai Desktop let your agents talk to each other.

## Caveats

- Agent API is not GA, and is likely to change before release. This action package is experimental and only for demos.
155 changes: 86 additions & 69 deletions actions/agent-connector/actions.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,116 @@
import json
from sema4ai.actions import action

import requests
from sema4ai.actions import action

API_URL = "http://localhost:8100"
API_URL = "http://localhost:8000"


def _get_all_agents():
@action
def get_all_agents() -> str:
"""Fetches a list of all available agents with their IDs and names.
Returns:
list or str: A list of dictionaries with assistant IDs and names,
or an error message if the request fails.
A json of the list of dictionaries with agent IDs and names, or an error message if the request fails.
"""
url = f"{API_URL}/assistants/"
url = f"{API_URL}/api/v1/agents/"
response = requests.get(url)
if response.status_code == 200:
assistants = response.json()
return [{"assistant_id": assistant["assistant_id"], "name": assistant["name"]} for assistant in assistants]
agents = response.json()
result = [{"agent_id": agent["id"], "name": agent["name"]} for agent in agents]
return json.dumps(result)
else:
return f"Error fetching agents: {response.status_code} {response.text}"


@action
def create_test_agent(agent_name: str, action_server_url: str) -> str:
"""Creates a test agent with the given parameters.
def get_agent_by_name(name: str) -> str:
"""Fetches an agent by name.
Args:
agent_name (str): The name of the agent.
action_server_url (str): The URL of the action server.
name: The name of the agent
Returns:
str: The created agent ID, or error message if the call fails.
A string with the agent ID and name or error message.
"""
system_message = f"You are an assistant with the following name: {agent_name}.\nThe current date and time is: {{CURRENT_DATETIME}}.\nYour instructions are to follow users instructions."
url = f"{API_URL}/assistants"
payload = {
"name": agent_name,
"config": {
"configurable": {
"type": "agent",
"type==agent/agent_type": "GPT 4o",
"type==agent/interrupt_before_action": False,
"type==agent/retrieval_description": "Can be used to look up information that was uploaded to this assistant.\nIf the user is referencing particular files, that is often a good hint that information may be here.\nIf the user asks a vague question, they are likely meaning to look up info from this retriever, and you should call it!",
"type==agent/system_message": system_message,
"type==agent/description": "Default description for the agent",
"type==agent/tools": [
{
"type": "action_server_by_sema4ai",
"name": "Action Server by Sema4.ai",
"description": "Run AI actions with [Sema4.ai Action Server](https://github.com/Sema4AI/actions).",
"config": {
"url": action_server_url,
"api_key": "APIKEY",
"isBundled": "false",
}
}
]
}
}
}
response = requests.post(url, json=payload)
url = f"{API_URL}/api/v1/agents/"
response = requests.get(url)
if response.status_code == 200:
return response.json()["assistant_id"]
agents = response.json()
for agent in agents:
if agent["name"] == name:
return agent["id"]
return f"Error fetching agent, No agent with name '{name}'"
else:
return f"Error creating agent: {response.status_code} {response.text}"
return f"Error fetching agents: {response.status_code} {response.text}"


@action
def create_thread(agent_name: str, thread_name: str) -> str:
"""Creates a new thread for communication with an agent.
def get_thread(agent_name: str, thread_name: str) -> str:
"""Fetches a thread for an agent.
Note: Agent names are pre-defined and must match existing agent names.
Args:
agent_name: The name of the agent
thread_name: The name of the thread
Returns:
The thread ID or error message.
"""

agent_id = get_agent_by_name(agent_name)
url = f"{API_URL}/api/v1/threads/"
response = requests.get(url)
if response.status_code == 200:
threads = response.json()
for thread in threads:
if thread["agent_id"] == agent_id and thread["name"] == thread_name:
return thread["thread_id"]
return f"Error fetching thread, No thread for agent ID '{agent_id}' and '{thread_name}'"
else:
return f"Error fetching threads: {response.status_code} {response.text}"


@action
def get_threads(agent_id: str) -> str:
"""Fetches all threads for an agent.
Args:
agent_name (str): The name of the pre-defined agent.
thread_name (str): The name of the thread (user-defined).
agent_id: The ID of the agent
Returns:
str: The thread ID, or error message if the call fails.
A json string with the List of threads or error message.
"""
agents = _get_all_agents()
if isinstance(agents, str):
return agents

assistant = next((agent for agent in agents if agent["name"] == agent_name), None)
if assistant is None:
available_agents = ", ".join(agent["name"] for agent in agents)
return f"No agent found with name '{agent_name}'. Available agents: {available_agents}"
url = f"{API_URL}/api/v1/threads/"
response = requests.get(url)
if response.status_code == 200:
threads = response.json()
result = []
for thread in threads:
if thread["agent_id"] == agent_id:
result.append(
{"thread_id": thread["thread_id"], "name": thread["name"]}
)
return json.dumps(result)
else:
return f"Error fetching threads: {response.status_code} {response.text}"

assistant_id = assistant["assistant_id"]
url = f"{API_URL}/threads"
payload = {
"name": thread_name,
"assistant_id": assistant_id
}

@action
def create_thread(agent_id: str, thread_name: str) -> str:
"""Creates a new thread for communication with an agent.
Note: Agent names are pre-defined and must match existing agent names.
Args:
agent_id: The id of the agent to create thread in. Use tools get_all_agents or get_agent_by_name to get the id of an agent based on it's name.
thread_name: The name of the thread to be created (user-defined).
Returns:
The thread ID, or error message if the call fails.
"""
url = f"{API_URL}/api/v1/threads"
payload = {"name": thread_name, "agent_id": agent_id}
response = requests.post(url, json=payload)
if response.status_code == 200:
return response.json()["thread_id"]
Expand All @@ -108,22 +125,22 @@ def send_message(thread_id: str, message: str) -> str:
Note: The thread ID must be obtained from a successful call to `create_thread`.
Args:
thread_id (str): The thread ID obtained from `create_thread`.
message (str): The message content.
thread_id: The thread ID obtained from `create_thread`.
message: The message content.
Returns:
str: The agent's response, or error message if the call fails.
The agent's response, or error message if the call fails.
"""
url = f"{API_URL}/runs/stream"
url = f"{API_URL}/api/v1/runs/stream"
payload = {
"thread_id": thread_id,
"input": [
{
"content": message,
"type": 'human',
"type": "human",
"example": False,
},
]
],
}
response = requests.post(url, json=payload, stream=True)

Expand All @@ -134,7 +151,7 @@ def send_message(thread_id: str, message: str) -> str:

for line in response.iter_lines():
if line:
decoded_line = line.decode('utf-8')
decoded_line = line.decode("utf-8")
if decoded_line.startswith("data: "):
collected_data.append(decoded_line[6:])

Expand Down
2 changes: 1 addition & 1 deletion actions/agent-connector/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Agent Connector
description: Actions to connect agents with each other

# Package version number, recommend using semver.org
version: 1.0.1
version: 2.0.0

# Required: A link to where the documentation on the package lives.
documentation: https://sema4.ai/
Expand Down

0 comments on commit 3e040b8

Please sign in to comment.