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

Initial submission #1

Merged
merged 29 commits into from
Aug 9, 2024
Merged

Initial submission #1

merged 29 commits into from
Aug 9, 2024

Conversation

srtaalej
Copy link
Contributor

@srtaalej srtaalej commented Aug 1, 2024

Type of change (place an x in the [ ] that applies)

  • New sample
  • New feature
  • Bug fix
  • Documentation

Summary

This PR introduces a new sample app that uses OpenAI and Anthropic APIs to generate responses to user prompts. The app showcases various interaction methods including DMs, app mentions, and a custom /ask command, while also supporting Bring Your Own Bot (BYOB) functionality and custom function implementation.

Requirements (place an x in each [ ] that applies)

  • I’ve checked my submission against the Samples Checklist to ensure it complies with all standards
  • I have ensured the changes I am contributing align with existing patterns and have tested and linted my code
  • I've read and agree to the Code of Conduct

Copy link

@WilliamBergamin WilliamBergamin left a comment

Choose a reason for hiding this comment

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

Great work 💯

left a few comments

README.md Outdated Show resolved Hide resolved
manifest.json Outdated Show resolved Hide resolved
requirements.txt Outdated Show resolved Hide resolved
slack.json Outdated Show resolved Hide resolved
state_store/create_file.py Outdated Show resolved Hide resolved
Copy link
Contributor

@hello-ashleyintech hello-ashleyintech left a comment

Choose a reason for hiding this comment

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

looking fabulous and thank you for submitting! ❤️ I have a few requested changes I think would help improve the documentation aspect of the app.

to avoid a huge wave of comments all at once, i'm going to split reviewing into different parts, starting with the README - leaving the first round of feedback for the README. in the next pass, I will re-review the README and then review the ai directory, and so on (each pass I'll note what specific sections I'm reviewing for clarity)

README.md Outdated Show resolved Hide resolved
.gitignore Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
Copy link
Contributor

@hello-ashleyintech hello-ashleyintech left a comment

Choose a reason for hiding this comment

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

🙌 phenomenal! This iteration I reviewed the README again as well as all files in ai folder, manifest.json, requirements.txt, and slack.json - I will cover /listeners next and also start testing the functionality of the app more in the next iteration! 🙌

from ai.providers.openai import OpenAI_API

"""
New providers need to be added below
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
New providers need to be added below
New AI providers must be added below

Maybe also add a note of what the file does in this comment! Ex: This file _________. New AI providers must be added below.

ai/ai_utils/handle_response.py Outdated Show resolved Hide resolved
ai/providers/anthropic.py Show resolved Hide resolved
ai/providers/base_provider.py Outdated Show resolved Hide resolved
requirements.txt Outdated Show resolved Hide resolved
manifest.json Outdated Show resolved Hide resolved
Copy link
Member

@seratch seratch left a comment

Choose a reason for hiding this comment

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

Great work already! Left a few comments but you don't need to resolve all of them at all! Hope some of these help you polish the details!

return AnthropicAPI()


def get_ai_response(user_id: str, prompt: str, context: list = [], system_content=DEFAULT_SYSTEM_CONTENT):
Copy link
Member

Choose a reason for hiding this comment

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

Passing a mutable object as the default argument value is a common pitfall in python; you can change the argument type to Optional[List[X]] and set None as the default value instead: https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html

raise ValueError("Invalid model")
self.current_model = model_name

def get_models(self):
Copy link
Member

Choose a reason for hiding this comment

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

nice-to-have but having return more types can make the code maintenance easier

Suggested change
def get_models(self):
def get_models(self) -> dict:

self.api_key: str

def set_model(self, model_name: str):
self.current_model = model_name
Copy link
Member

Choose a reason for hiding this comment

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

If you raise an exception here, this line of code won't be used

Suggested change
self.current_model = model_name


def generate_response(self, prompt: str, system_content: str) -> str:
try:
client = openai.OpenAI(api_key=self.api_key)
Copy link
Member

Choose a reason for hiding this comment

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

you can set up this instance within the constructor and have the instance as an instance field (self.client) to eliminate a very slight overhead here

)
return response.choices[0].message.content
except openai.APIConnectionError as e:
Logger.error(f"Server could not be reached: {e.__cause__}")
Copy link
Member

Choose a reason for hiding this comment

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

Printing error logs is a good idea! If this app wants to tell the end-user what's happening, this code may want to return the text to display in Slack UI rather than returning nothing.

manifest.json Outdated
"channel_id":{
"type":"slack#/types/channel_id",
"title":"Channel",
"description":"Channel that user joined",
Copy link
Member

Choose a reason for hiding this comment

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

It seems this app's bot user has to be a member of the channel, right? If yes, this app may want to have the permission to use conversations.join API. If it's not suitable for this app, the description should mention the workflow user has to manually invite the bot.

from .user_identity import UserIdentity


class StateStore:
Copy link
Member

Choose a reason for hiding this comment

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

nit: UserStateStore may sound even clearer to me.

# A base class for API providers, defining the interface and common properties for subclasses.


class BaseProvider:
Copy link
Member

Choose a reason for hiding this comment

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

For consistency, having "API" in the name (e.g., BaseAPIProvider) may be clearer.

"""


def _get_provider(api_name: str):
Copy link
Member

Choose a reason for hiding this comment

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

nit: these strings are actually names of the service providers, not the API names (I mean "Chat Completions" is an API name). renaming the argument to provider_name: str and the method to _resolve_api_provider() may be an alternative way to name them.


class UserIdentity(TypedDict):
user_id: str
api: str
Copy link
Member

Choose a reason for hiding this comment

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

if this can be either "openai" or "anthoropic", it sounds like "provider"

Copy link
Contributor

@hello-ashleyintech hello-ashleyintech left a comment

Choose a reason for hiding this comment

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

when trying to run the slash command or dm bolty, I keep getting the following error: missing 1 required positional argument: 'msg' 😢 do you know what could be causing this?

README.md Show resolved Hide resolved
README.md Outdated
@@ -19,18 +29,20 @@ Before you can run the app, you'll need to store some environment variables.
2. Click ***Basic Information** from the left hand menu and follow the steps in the App-Level Tokens section to create an app-level token with the `connections:write` scope. Copy this token. You will store this in your environment as `SLACK_APP_TOKEN`.

```zsh
# Replace with your app token and bot token
# Replace with your app token, bot token, and the token for whichever API(s) you plan on using
Copy link
Contributor

Choose a reason for hiding this comment

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

specify how to configure these env vars - by running in terminal

ai/__init__.py Outdated
Copy link
Contributor

Choose a reason for hiding this comment

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

this file is empty, do we need it?

Copy link
Contributor

@hello-ashleyintech hello-ashleyintech left a comment

Choose a reason for hiding this comment

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

mentioning the bot results in the following error: ERROR:app.py:app_mentioned_callback:parse_conversation() takes 1 positional argument but 2 were given

from ai.ai_utils.handle_response import get_ai_response


def ask_callback(ack: Ack, command, say: Say, logger: Logger, context: BoltContext):
Copy link
Contributor

Choose a reason for hiding this comment

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

let's add a comment for this callback detailing what feature it's connected to and what it does!



def app_home_opened_callback(client, event, logger: Logger):
# ignore the app_home_opened event for anything but the Home tab
def app_home_opened_callback(event, logger: Logger, client: WebClient, context: BoltContext):
Copy link
Contributor

Choose a reason for hiding this comment

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

let's add a comment for this callback detailing what feature it's connected to and what it does!

@srtaalej
Copy link
Contributor Author

srtaalej commented Aug 7, 2024

@hello-ashleyintech

when trying to run the slash command or dm bolty, I keep getting the following error: missing 1 required positional argument: 'msg' 😢 do you know what could be causing this?

Did it create a user file for you? It usually means it cant find your provider selection.

@srtaalej
Copy link
Contributor Author

srtaalej commented Aug 7, 2024

mentioning the bot results in the following error: ERROR:app.py:app_mentioned_callback:parse_conversation() takes 1 positional argument but 2 were given

i removed bot_id from the parameters for parse_conversation(). my bad, i missed that!

@hello-ashleyintech
Copy link
Contributor

Did it create a user file for you? It usually means it cant find your provider selection.

It did! 😲 maybe it's having trouble reading it?

@hello-ashleyintech hello-ashleyintech self-requested a review August 7, 2024 21:44
@hello-ashleyintech
Copy link
Contributor

unblocked on my end - errors were being thrown because i didn't have credits on my account 😢 got some working API keys for each service and it's working now, will test more tomorrow and leave an updated review ❤️

Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

Big fan of the magic with Bolty ⚡ 🤖

The code is looking great and the feature set is so solid. Approving it now but I think some of the comments are worth addressing before merging- also feel free to ping me for more review! 🔍

Most of the comments I left are around file structure and naming which isn't too important but I think a few changes might make it easier to start changing things as an exploring developer. This will be quite a nice sample to explore 😄 🚀

manifest.json Outdated
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Can this file be linted for indentation- https://jsonlint.com? 🙏

Edit- I'm a fan of 2 or 4 spaces but can never decide myself lol. I would just avoid 3 or 5 or 1 or even 11 with all importance.

Copy link
Contributor Author

@srtaalej srtaalej Aug 8, 2024

Choose a reason for hiding this comment

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

its black's formatting. should the readme instructions be black *.py instead of black .?

Copy link
Member

Choose a reason for hiding this comment

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

This might also have a extra space indentation 👾

Copy link
Contributor Author

Choose a reason for hiding this comment

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

* `handle_response.py`: Processes responses from API providers.
<a name="byo-llm"></a>
#### `ai/providers`
This module contains classes for communicating with different API providers, such as [Anthropic](https://www.anthropic.com/) and [OpenAI](https://openai.com/). To add your own LLM, create a new class for it using the `base_provider.py` as an example, then update `get_available_apis.py` and `handle_response.py` to include and utilize your new class for API communication.

Copy link
Member

Choose a reason for hiding this comment

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

A section on state_store would also be nice to have here 📚 👀

Copy link
Member

Choose a reason for hiding this comment

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

Can the get_user_state and set_user_state methods be moved into this class?

Copy link
Contributor Author

@srtaalej srtaalej Aug 8, 2024

Choose a reason for hiding this comment

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

set_user_state creates a FileStateStore object which inherits from UserStateStore though

# add an empty option if the user has no previously selected model.
options.append(
{
"text": {"type": "plain_text", "text": " ", "emoji": True},
Copy link
Member

Choose a reason for hiding this comment

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

Could we change this the text to "None selected" or something similar? Blank might be confusing at first glance 👁️‍🗨️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

how about Select a provider

Comment on lines 18 to 22
def _get_provider(provider_name: str):
if provider_name.lower() == "openai":
return OpenAI_API()
if provider_name.lower() == "anthropic":
return AnthropicAPI()
Copy link
Member

Choose a reason for hiding this comment

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

Also not sure if this is possible, but it'd be neat if this could be from __new__ in an ai/providers/__init__.py file

.gitignore Outdated
Comment on lines 39 to 40
!data/.gitkeep
Copy link
Member

Choose a reason for hiding this comment

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

This could be replaced with the following in a data/.gitignore instead 👀

*
!.gitignore

.gitignore Outdated
data/*
!data/.gitkeep
.slack/
Copy link
Member

Choose a reason for hiding this comment

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

We'll just want to recommend ignoring

.slack/apps.dev.json

Copy link
Member

Choose a reason for hiding this comment

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

I also think these utils can be moved to the top level listeners for easier editing. IMO these could even be one conversation.py file but this can also be ignored altogether!

manifest.json Outdated
"oauth_config": {
"scopes": {
"bot": [
"commands",
Copy link
Member

Choose a reason for hiding this comment

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

Nit but it's nice to alphabetize these lists for fast scanning

# and formats it as a string with user IDs and their messages.
# Used in `app_mentioned_callback`, `dm_sent_callback`,
# and `handle_summary_function_callback`.
def parse_conversation(conversation: str) -> Optional[List[dict]]:
Copy link
Member

Choose a reason for hiding this comment

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

conversation's type hint looks incorrect

Copy link
Member

Choose a reason for hiding this comment

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

FYI: Running https://github.com/google/pytype may help you detect this type of error


# Change into this project directory
cd bolt-python-starter-template
cd bolt-python-ai-chatbot

# Setup your python virtual environment
python3 -m venv .venv
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we can add a note here after the setup instructions to also make sure you select a provider from the app home before you try mentioning the bot, using slash commands, or dming them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

with the new error handling the bot tells them directly when they interact with it

@hello-ashleyintech hello-ashleyintech self-requested a review August 9, 2024 17:08
Copy link
Contributor

@hello-ashleyintech hello-ashleyintech left a comment

Choose a reason for hiding this comment

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

LGTM, excellent work! This is such a great addition to our collection of samples! 🙌

@srtaalej srtaalej merged commit 4488e57 into main Aug 9, 2024
3 checks passed
@hello-ashleyintech hello-ashleyintech deleted the initial-submission branch August 9, 2024 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants