Skip to content
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

feat: Add Discord #19

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions agents/example.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
],
"loop_delay": 900,
"config": [
{
"name": "discord"
},
{
"name": "twitter",
"timeline_read_count": 10,
Expand Down
785 changes: 675 additions & 110 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ openai = "^1.57.2"
tweepy = "^4.14.0"
prompt-toolkit = "^3.0.48"
anthropic = "^0.42.0"
"discord-py" = "^2.4.0"


[build-system]
Expand Down
17 changes: 17 additions & 0 deletions src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def _setup_llm_provider(self):
self.model_provider = llm_providers[0]

# Load Twitter username for self-reply detection
# TODO: This should be done elsewhere
load_dotenv()
self.username = os.getenv('TWITTER_USERNAME', '').lower()

Expand Down Expand Up @@ -99,8 +100,24 @@ def prompt_llm(self, prompt: str, system_prompt: str = None) -> str:
def perform_action(self, connection: str, action: str, **kwargs) -> None:
return self.connection_manager.perform_action(connection, action, **kwargs)

def _setup_connections(self):
"""Setup all connections that have initialization requirements"""
# Initialize all connections that need setup
for name, connection in self.connection_manager.connections.items():
if hasattr(connection, 'start') and connection.is_configured():
connection.start(connection_manager=self.connection_manager)

# Set up LLM provider after all connections are initialized
llm_providers = self.connection_manager.get_model_providers()
if not llm_providers:
raise ValueError("No configured LLM provider found")
self.model_provider = llm_providers[0]
self.is_llm_set = True

def loop(self):
"""Main agent loop for autonomous behavior"""
# TODO: Remove this and make background loop actions better integrated
#self._setup_connections()
if not self.is_llm_set:
self._setup_llm_provider()

Expand Down
26 changes: 22 additions & 4 deletions src/connection_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from src.connections.anthropic_connection import AnthropicConnection
from src.connections.openai_connection import OpenAIConnection
from src.connections.twitter_connection import TwitterConnection
from src.connections.discord_connection import DiscordConnection

logger = logging.getLogger("connection_manager")

Expand All @@ -21,6 +22,8 @@ def _class_name_to_type(class_name: str) -> Type[BaseConnection]:
return AnthropicConnection
elif class_name == "openai":
return OpenAIConnection
elif class_name == "discord":
return DiscordConnection

return None

Expand Down Expand Up @@ -122,17 +125,32 @@ def perform_action(self, connection_name: str, action_name: str, params: List[An
# Check if we have enough parameters
if len(params) != required_params_count:
param_names = [param.name for param in action.parameters if param.required]
logging.error(f"\nError: Expected {required_params_count} required parameters for {action_name}: {', '.join(param_names)}")
return None
# LLM provider does not need to be passed as an argument
# TODO: Make it possible for user to specify LLM provider
param_names.remove("llm_provider")

# If there are required unmet params (besides llm provider), throw error
if len(param_names) > 0:
logging.error(f"\nError: Expected {required_params_count} required parameters for {action_name}: {', '.join(param_names)}")
return None

# Convert list of params to kwargs dictionary
kwargs = {}
param_index = 0
for param in action.parameters:
if param.required:
if param.name == "llm_provider":
# TODO: Make it possible for user to specify LLM provider
# Choose an available LLM provider
model_providers = self.get_model_providers()
chosen_provider = model_providers[0] # Just pick the first one
llm_provider = self.connections[chosen_provider]
kwargs[param.name] = llm_provider
elif param.required:
kwargs[param.name] = params[param_index]
param_index += 1


# TODO: Should this be **kwargs? Where do args get unpacked, here or in the connection class?
# TODO: This fix involves making the perform_action method in BaseConnection consistent across connections
return connection.perform_action(action_name, kwargs)

except Exception as e:
Expand Down
7 changes: 6 additions & 1 deletion src/connections/base_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ def validate_params(self, params: Dict[str, Any]) -> List[str]:
errors.append(f"Missing required parameter: {param.name}")
elif param.name in params:
try:
params[param.name] = param.type(params[param.name])
# If the parameter is a Connection, pass as is
if param.type == BaseConnection and isinstance(params[param.name], BaseConnection):
continue
else:
# Otherwise, check if the parameter can be cast to its specified type
params[param.name] = param.type(params[param.name])
except ValueError:
errors.append(f"Invalid type for {param.name}. Expected {param.type.__name__}")
return errors
Expand Down
Loading