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

add fixes to try to add blocks #1727

Open
wants to merge 16 commits into
base: integration
Choose a base branch
from

Conversation

sarahwooders
Copy link
Collaborator

Please describe the purpose of this pull request.
Is it to add a new feature? Is it to fix a bug?

How to test
How can we test your PR during review? What commands should we run? What outcomes should we expect?

Have you tested this PR?
Have you tested the latest commit on the PR? If so please provide outputs from your tests.

Related issues or PRs
Please link any related GitHub issues or PRs.

Is your PR over 500 lines of code?
If so, please break up your PR into multiple smaller PRs so that we can review them quickly, or provide justification for its length.

Additional context
Add any other context or screenshots about the PR here.

@sarahwooders sarahwooders changed the base branch from integration to integration-black September 6, 2024 21:43
Base automatically changed from integration-black to integration September 7, 2024 01:26
@@ -46,16 +46,16 @@ class Tool(SqlalchemyBase, OrganizationMixin):
organization: Mapped["Organization"] = relationship("Organization", back_populates="tools", lazy="selectin")

@classmethod
def read(cls, db_session: "Session", name: str) -> "Tool":
def read_by_name(cls, db_session: "Session", name: str) -> "Tool":
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@norton120 do we want to just do something like this, but also provide the actor?

def read_by_name(cls, db_session: "Session", name: str, actor_id: str) -> "Tool": 
...

Copy link
Collaborator

@norton120 norton120 Sep 9, 2024

Choose a reason for hiding this comment

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

ideally instead of read_by_name, overload the read method on tool and allow it to accept either a name or an id.

something like

# see the parent in https://github.com/cpacker/MemGPT/blob/c82f071b16367e43a31e71bfa74a62ab10cb96a2/memgpt/orm/sqlalchemy_base.py#L75C4-L83C33

 @classmethod
    def read(
        cls,
        db_session: "Session",
        identifier: Union[str, UUID],
        actor: Optional["User"] = None,
        access: Optional[List[Literal["read", "write", "admin"]]] = ["read"],
        **kwargs,
    ) -> Type["SqlalchemyBase"]:
    # see if it's a UUID or a valid identifier
    try:
        _ = cls.to_uid(identifier)
        # if so cool, use the normal super method
        return super().read(db_session, identifier, actor=actor)
    # only catch the error you'd expect (check out to_uid to figure out what it throws when you pass it a name)
    except (some-known-exception-that-indicates-its-a-name):
        # overload the query to use name lookup instead
        query = select(cls).where(cls.name == identifier)
        # same actor logic as is in normal read
        if actor:
            query = cls.apply_access_predicate(query, actor, access)
        if hasattr(cls, "is_deleted"):
            query = query.where(cls.is_deleted == False)
        if found := db_session.execute(query).scalar():
            return found
        raise NoResultFound(f"{cls.__name__} with id {identifier} not found")

if found := db_session.query(cls).filter(cls.id == id, cls.is_deleted == False).scalar():
return found
raise NoResultFound(f"{cls.__name__} with id {id} not found")
# @classmethod
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed this because it seems duplicated with the parent class read, and I think its confusing to pretend that Tool.name is the id

Copy link
Collaborator

Choose a reason for hiding this comment

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

if you want to be explicit about it without adding extra one-off methods you could also overload the parent read on Tool to take a name param instead.
This is one of those things that functional programmers hate but overloading (extending the arity) this way is an OOP pattern as opposed to lots of single-use getters

@@ -824,7 +824,9 @@ def __init__(
if user_id:
Copy link
Collaborator

Choose a reason for hiding this comment

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

a shorthand here to help readability:

self.user_id = user_id or self.server.get_default_user().id

# return found

query = db_session.query(cls).filter(cls.name == name, cls.is_deleted == False)
query = cls.apply_access_predicate(query, actor, "read")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

is this what this should be, to filter to data that the actor has access to?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yep - take a look at the method I just pushed if you want to dig in more, but this now accepts any object that duck types as a User for an actor

@norton120
Copy link
Collaborator

The migration included here will fail if there are old tools with duplicate names already in the db - truncating the tools table or just blowing away the db rm -rf .persist/* will fix it, and then once the migration runs it'll never happen again :)

Objects are unique per org, and auth predicate allows pydantic or sqlalchemy Users (and derivatives)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: To triage
Development

Successfully merging this pull request may close these issues.

2 participants