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

Minor changes in Codeanalysis tools, Testing tool added. Support to change schema added #578

Merged
merged 4 commits into from
Sep 17, 2024
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
552 changes: 536 additions & 16 deletions python/composio/client/enums/_action.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions python/composio/client/enums/_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class App(_AnnotatedEnum[AppData], path=APPS_CACHE):
APIFY: "App"
ASANA: "App"
ATTIO: "App"
BAMBOOHR: "App"
BROWSERBASE_TOOL: "App"
BROWSER_TOOL: "App"
CLICKUP: "App"
Expand Down Expand Up @@ -64,6 +65,7 @@ class App(_AnnotatedEnum[AppData], path=APPS_CACHE):
NOTION: "App"
PERPLEXITYAI: "App"
PIPEDRIVE: "App"
POSTHOG: "App"
RAGTOOL: "App"
SALESFORCE: "App"
SCHEDULER: "App"
Expand Down
69 changes: 69 additions & 0 deletions python/composio/client/enums/_tag.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Tag enums.
"""

from composio.client.enums.base import TAGS_CACHE, TagData, _AnnotatedEnum, enum


Expand Down Expand Up @@ -58,6 +59,26 @@ class Tag(_AnnotatedEnum[TagData], path=TAGS_CACHE):
ATTIO_THREADS: "Tag"
ATTIO_WEBHOOKS: "Tag"
ATTIO_WORKSPACE_MEMBERS: "Tag"
BAMBOOHR_ACCOUNT_INFORMATION: "Tag"
BAMBOOHR_APPLICANT_TRACKING: "Tag"
BAMBOOHR_BENEFITS: "Tag"
BAMBOOHR_COMPANY_FILES: "Tag"
BAMBOOHR_EMPLOYEES: "Tag"
BAMBOOHR_EMPLOYEE_FILES: "Tag"
BAMBOOHR_GOALS: "Tag"
BAMBOOHR_HOURS: "Tag"
BAMBOOHR_IMPORTANT: "Tag"
BAMBOOHR_LAST_CHANGE_INFORMATION: "Tag"
BAMBOOHR_LOGIN: "Tag"
BAMBOOHR_PAYROLL: "Tag"
BAMBOOHR_PHOTOS: "Tag"
BAMBOOHR_REPORTS: "Tag"
BAMBOOHR_TABULAR_DATA: "Tag"
BAMBOOHR_TIME_OFF: "Tag"
BAMBOOHR_TIME_TRACKING: "Tag"
BAMBOOHR_TIME_TRACKING___PRIVATE_BETA: "Tag"
BAMBOOHR_TRAINING: "Tag"
BAMBOOHR_WEBHOOKS: "Tag"
CLICKUP_ATTACHMENTS: "Tag"
CLICKUP_AUTHORIZATION: "Tag"
CLICKUP_COMMENTS: "Tag"
Expand Down Expand Up @@ -361,6 +382,54 @@ class Tag(_AnnotatedEnum[TagData], path=TAGS_CACHE):
PIPEDRIVE_USERS: "Tag"
PIPEDRIVE_USERSETTINGS: "Tag"
PIPEDRIVE_WEBHOOKS: "Tag"
POSTHOG_ACTIONS: "Tag"
POSTHOG_ACTIVITY_LOG: "Tag"
POSTHOG_ANNOTATIONS: "Tag"
POSTHOG_APP_METRICS: "Tag"
POSTHOG_BATCH_EXPORTS: "Tag"
POSTHOG_COHORTS: "Tag"
POSTHOG_DASHBOARDS: "Tag"
POSTHOG_DASHBOARD_TEMPLATES: "Tag"
POSTHOG_DOMAINS: "Tag"
POSTHOG_EARLY_ACCESS_FEATURE: "Tag"
POSTHOG_EVENTS: "Tag"
POSTHOG_EVENT_DEFINITIONS: "Tag"
POSTHOG_EXPERIMENTS: "Tag"
POSTHOG_EXPLICIT_MEMBERS: "Tag"
POSTHOG_EXPORTS: "Tag"
POSTHOG_FEATURE_FLAGS: "Tag"
POSTHOG_FUNNEL: "Tag"
POSTHOG_GROUPS: "Tag"
POSTHOG_GROUPS_TYPES: "Tag"
POSTHOG_IMPORTANT: "Tag"
POSTHOG_INSIGHTS: "Tag"
POSTHOG_INVITES: "Tag"
POSTHOG_MEMBERS: "Tag"
POSTHOG_NOTEBOOKS: "Tag"
POSTHOG_ORGANIZATIONS: "Tag"
POSTHOG_PERSONS: "Tag"
POSTHOG_PIPELINE_DESTINATIONS: "Tag"
POSTHOG_PIPELINE_DESTINATION_CONFIGS: "Tag"
POSTHOG_PIPELINE_FRONTEND_APPS: "Tag"
POSTHOG_PIPELINE_FRONTEND_APPS_CONFIGS: "Tag"
POSTHOG_PIPELINE_IMPORT_APPS: "Tag"
POSTHOG_PIPELINE_IMPORT_APPS_CONFIGS: "Tag"
POSTHOG_PIPELINE_TRANSFORMATIONS: "Tag"
POSTHOG_PIPELINE_TRANSFORMATION_CONFIGS: "Tag"
POSTHOG_PLUGINS: "Tag"
POSTHOG_PLUGIN_CONFIGS: "Tag"
POSTHOG_PROJECTS: "Tag"
POSTHOG_PROPERTY_DEFINITIONS: "Tag"
POSTHOG_PROXY_RECORDS: "Tag"
POSTHOG_QUERY: "Tag"
POSTHOG_ROLES: "Tag"
POSTHOG_SESSIONS: "Tag"
POSTHOG_SESSION_RECORDINGS: "Tag"
POSTHOG_SESSION_RECORDING_PLAYLISTS: "Tag"
POSTHOG_SUBSCRIPTIONS: "Tag"
POSTHOG_SURVEYS: "Tag"
POSTHOG_TREND: "Tag"
POSTHOG_USERS: "Tag"
SALESFORCE_ACCOUNT: "Tag"
SALESFORCE_CAMPAIGN: "Tag"
SALESFORCE_CONTACT: "Tag"
Expand Down
1 change: 1 addition & 0 deletions python/composio/client/enums/_trigger.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Trigger enums.
"""

from composio.client.enums.base import TRIGGERS_CACHE, TriggerData, _AnnotatedEnum, enum


Expand Down
1 change: 0 additions & 1 deletion python/composio/tools/base/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Tool exceptions."""


from composio.exceptions import ComposioSDKError


Expand Down
12 changes: 7 additions & 5 deletions python/composio/tools/env/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,14 @@ def check_for_missing_dependencies(
},
timeout=600,
)
if request.status_code != 200:
raise ComposioSDKError(f"Error installing dependencies: {request.text}")

response = request.json()
print(response)
# if response["error"] is not None:
# raise ComposioSDKError(
# f"Error installing dependencies: {response['error']}"
# )
if response["error"] is not None:
raise ComposioSDKError(
f"Error installing dependencies: {response['error']}"
)

def execute_action(
self,
Expand Down
6 changes: 5 additions & 1 deletion python/composio/tools/env/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ def close(cls, id: str) -> None:
"""Teardown the workspace with given ID."""
if id not in cls._workspaces:
return
cls._workspaces[id].teardown()
workspace = cls._workspaces[id]
if workspace.persistent:
return

workspace.teardown()

@classmethod
def teardown(cls) -> None:
Expand Down
37 changes: 26 additions & 11 deletions python/composio/tools/local/codeanalysis/actions/base_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,29 @@ def load_fqdn_cache(self, repo_name: str):
}

def get_matching_items(
self, query_name: Optional[str], item_type: str
self,
query_name: Optional[str],
item_type: str,
parent_fqdns: Optional[List[str]] = None,
) -> List[str]:
if not self.fqdn_index:
raise ValueError("FQDN index not loaded")

matching_fqdns = [
curr_fqdn
for curr_fqdn, curr_fqdn_elem in self.fqdn_index.items()
if curr_fqdn_elem["global_type"] == item_type
and (
def matches_query(curr_fqdn: str) -> bool:
return (
query_name is None
or query_name == curr_fqdn.split(".")[-1]
or query_name == curr_fqdn
)

matching_fqdns = [
curr_fqdn
for curr_fqdn, curr_fqdn_elem in self.fqdn_index.items()
if curr_fqdn_elem["global_type"] == item_type
and matches_query(curr_fqdn)
and (parent_fqdns is None or curr_fqdn_elem["parent_fqdn"] in parent_fqdns)
]

return matching_fqdns

def fetch_relevant_details(self, relevant_fqdn: str, repo_path: str) -> List[Dict]:
Expand Down Expand Up @@ -89,13 +97,20 @@ def execute(self, request, metadata):
def get_method_artefacts(
self, query_class_name: Optional[str], query_method_name: str, repo_path: str
) -> Dict:
matching_fqdns_func = self.get_matching_items(query_method_name, "function")
matching_fqdns_class = self.get_matching_items(query_class_name, "class")

if query_class_name is not None:
matching_fqdns_func = self.get_matching_items(
query_method_name, "function", matching_fqdns_class
)
else:
matching_fqdns_func = self.get_matching_items(query_method_name, "function")
func_results = self.get_item_results(matching_fqdns_func, repo_path)
filtered_func_results = self.filter_function_results(
func_results, query_class_name, matching_fqdns_class, repo_path
)
if query_class_name is not None:
filtered_func_results = self.filter_function_results(
func_results, query_class_name, matching_fqdns_class, repo_path
)
else:
filtered_func_results = func_results

return self.format_method_results(filtered_func_results)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GetRelevantCode(LocalAction[GetRelevantCodeRequest, GetRelevantCodeRespons
query: "database connection pooling"

The relevance of retrieved code snippets depends on the quality and specificity of the provided query.
Don't use this action if you are not sure about the query. And the results returned are not very relevant.
"""

display_name = "Get Relevant Code"
Expand Down
3 changes: 3 additions & 0 deletions python/composio/tools/local/filetool/actions/scroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class Scroll(LocalAction[ScrollRequest, ScrollResponse]):
- A dictionary of line numbers and their content for the new view window.
- An error message if no file is open or if the file is not found.

Use SearchWord Action to search for a specific word in the file in case
the file is long, as scrolling is not efficient for large files.

Raises:
- FileNotFoundError: If the file is not found.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ def execute(self, request: GetPatchRequest, metadata: t.Dict) -> GetPatchRespons
cmd = ["git add -u"]
if len(request.new_file_path) > 0:
cmd = [f"git add {new_files}", "git add -u"]
cmd.append("git diff --cached")

output = self.shells.get(request.shell_id).exec(cmd=" && ".join(cmd))
return GetPatchResponse(
stdout=output[STDOUT],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Tool for executing shell commands."""

import typing as t

from pydantic import BaseModel, Field

from composio.tools.base.local import LocalAction
from composio.tools.env.constants import STDERR, STDOUT
from composio.tools.local.shelltool.shell_exec.actions.exec import ShellRequest


class TestExecRequest(ShellRequest):
"""Test execution request."""


class TestExecResponse(BaseModel):
"""Shell execution response."""

test_response: str = Field(
...,
description="Response from the test command",
)
current_shell_pwd: str = Field(
default="",
description="Current shell's working directory",
)


class TestCommand(LocalAction[TestExecRequest, TestExecResponse]):
"""
Run the command for testing the patch.
"""

_tags = ["workspace", "shell"]

def execute(self, request: TestExecRequest, metadata: t.Dict) -> TestExecResponse:
"""Execute a shell command."""
shell = self.shells.get(id=request.shell_id)
project_path = metadata.get("project_path")
command = metadata.get("test_command")
self.logger.debug(f"Executing {command} @ {shell}")
shell.exec(cmd=f"cd {project_path}")
shell.exec(cmd="python -m pip install -e .")
output = shell.exec(cmd=f"{command}")
self.logger.debug(output)
return TestExecResponse(
test_response=output[STDERR],
current_shell_pwd=f"Currently in {shell.exec(cmd='pwd')[STDOUT].strip()}",
)
2 changes: 2 additions & 0 deletions python/composio/tools/local/shelltool/shell_exec/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from composio.tools.local.shelltool.shell_exec.actions.exec import ExecCommand
from composio.tools.local.shelltool.shell_exec.actions.new import CreateShell
from composio.tools.local.shelltool.shell_exec.actions.spawn import SpawnProcess
from composio.tools.local.shelltool.shell_exec.actions.test import TestCommand


class Shelltool(LocalTool, autoload=True):
Expand All @@ -20,4 +21,5 @@ def actions(cls) -> t.List[t.Type[LocalAction]]:
ExecCommand,
CreateShell,
SpawnProcess,
TestCommand,
]
29 changes: 24 additions & 5 deletions python/composio/tools/toolset.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class ProcessorsType(te.TypedDict):
post: te.NotRequired[t.Dict[_KeyType, _ProcessorType]]
"""Response processors."""

schema: te.NotRequired[t.Dict[_KeyType, _ProcessorType]]
"""Schema processors"""


def _check_agentops() -> bool:
"""Check if AgentOps is installed and initialized."""
Expand Down Expand Up @@ -237,7 +240,9 @@ def _limit_file_search_response(response: t.Dict) -> t.Dict:
self.logger.debug("`api_key` is not set when initializing toolset.")

self._processors = (
processors if processors is not None else {"post": {}, "pre": {}}
processors
if processors is not None
else {"post": {}, "pre": {}, "schema": {}}
)
self._metadata = metadata or {}
self._workspace_id = workspace_id
Expand Down Expand Up @@ -496,7 +501,7 @@ def _add_metadata(self, action: Action, metadata: t.Optional[t.Dict]) -> t.Dict:
def _get_processor(
self,
key: _KeyType,
type_: te.Literal["post", "pre"],
type_: te.Literal["post", "pre", "schema"],
) -> t.Optional[_ProcessorType]:
"""Get processor for given app or action"""
processor = self._processors.get(type_, {}).get(key) # type: ignore
Expand All @@ -512,12 +517,12 @@ def _process(
self,
key: _KeyType,
data: t.Dict,
type_: te.Literal["pre", "post"],
type_: te.Literal["pre", "post", "schema"],
) -> t.Dict:
processor = self._get_processor(key=key, type_=type_)
if processor is not None:
self.logger.info(
f"Running {'request' if type_ == 'pre' else 'response'}"
f"Running {'request' if type_ == 'pre' else 'response' if type_ == 'post' else 'schema'}"
f" through: {processor.__name__}"
)
data = processor(data)
Expand Down Expand Up @@ -545,6 +550,17 @@ def _process_respone(self, action: Action, response: t.Dict) -> t.Dict:
type_="post",
)

def _process_schema_properties(self, action: Action, properties: t.Dict) -> t.Dict:
return self._process(
key=App(action.app),
data=self._process(
key=action,
data=properties,
type_="schema",
),
type_="schema",
)

@_record_action_if_available
def execute_action(
self,
Expand Down Expand Up @@ -723,7 +739,10 @@ def _process_schema(self, action_item: ActionModel) -> ActionModel:
action_item.description = action_item.description[
: self._description_char_limit
]

action_item.parameters.properties = self._process_schema_properties(
action=Action(action_item.name.upper()),
properties=action_item.parameters.properties,
)
return action_item

def create_trigger_listener(self, timeout: float = 15.0) -> TriggerSubscription:
Expand Down
Loading
Loading