Skip to content

Commit

Permalink
Merge branch 'main' into anthropic-fix-raw-message
Browse files Browse the repository at this point in the history
  • Loading branch information
teocns authored Nov 18, 2024
2 parents 7b855dc + 80d4bf5 commit a777c0a
Show file tree
Hide file tree
Showing 42 changed files with 287 additions and 475 deletions.
29 changes: 0 additions & 29 deletions .github/workflows/black-formatter.yml

This file was deleted.

62 changes: 62 additions & 0 deletions .github/workflows/static-analysis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Static analysis

# on PR and push to main
on:
push:
branches:
- main
pull_request:
paths:
- '**/*.py'

permissions:
contents: read

# Limit concurrency by workflow/branch combination.
#
# For pull request builds, pushing additional changes to the
# branch will cancel prior in-progress and pending builds.
#
# For builds triggered on a branch push, additional changes
# will wait for prior builds to complete before starting.
#
# https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
pre-commit-checks:
name: Pre-commit checks
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
id: setup_python
with:
python-version: "3.11.10"

- name: UV Cache
# Manually cache the uv cache directory
# until setup-python supports it:
# https://github.com/actions/setup-python/issues/822
uses: actions/cache@v4
id: cache-uv
with:
path: ~/.cache/uv
key: uvcache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}

- name: Install packages
run: |
python -m pip install -U uv pre-commit
uv pip install --upgrade --system -e .[dev]
- name: Run pre-commit
run: |
pre-commit run --show-diff-on-failure --color=always --all-files
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.2.1"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ Even if you're not ready to contribute code, we'd love to hear your thoughts. Dr
.\venv\Scripts\activate # Windows
```

3. **Pre-commit Setup**:
We use pre-commit hooks to automatically format and lint code. Set them up with:
```bash
pip install pre-commit
pre-commit install
```

That's it! The hooks will run automatically when you commit. To manually check all files:
```bash
pre-commit run --all-files
```
## Testing
We use a comprehensive testing stack to ensure code quality and reliability. Our testing framework includes pytest and several specialized testing tools.
Expand Down
4 changes: 1 addition & 3 deletions agentops/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ def main():
parser = argparse.ArgumentParser(description="AgentOps CLI")
subparsers = parser.add_subparsers(dest="command")

timetravel_parser = subparsers.add_parser(
"timetravel", help="Time Travel Debugging commands", aliases=["tt"]
)
timetravel_parser = subparsers.add_parser("timetravel", help="Time Travel Debugging commands", aliases=["tt"])
timetravel_parser.add_argument(
"branch_name",
type=str,
Expand Down
53 changes: 12 additions & 41 deletions agentops/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ def __init__(self):
api_key=os.environ.get("AGENTOPS_API_KEY"),
parent_key=os.environ.get("AGENTOPS_PARENT_KEY"),
endpoint=os.environ.get("AGENTOPS_API_ENDPOINT"),
env_data_opt_out=os.environ.get(
"AGENTOPS_ENV_DATA_OPT_OUT", "False"
).lower()
== "true",
env_data_opt_out=os.environ.get("AGENTOPS_ENV_DATA_OPT_OUT", "False").lower() == "true",
)

def configure(
Expand Down Expand Up @@ -106,9 +103,7 @@ def initialize(self) -> Union[Session, None]:

if session:
for agent_args in self._pre_init_queue["agents"]:
session.create_agent(
name=agent_args["name"], agent_id=agent_args["agent_id"]
)
session.create_agent(name=agent_args["name"], agent_id=agent_args["agent_id"])
self._pre_init_queue["agents"] = []

return session
Expand Down Expand Up @@ -141,9 +136,7 @@ def add_tags(self, tags: List[str]) -> None:

session = self._safe_get_session()
if session is None:
return logger.warning(
"Could not add tags. Start a session by calling agentops.start_session()."
)
return logger.warning("Could not add tags. Start a session by calling agentops.start_session().")

session.add_tags(tags=tags)

Expand All @@ -162,9 +155,7 @@ def set_tags(self, tags: List[str]) -> None:
session = self._safe_get_session()

if session is None:
return logger.warning(
"Could not set tags. Start a session by calling agentops.start_session()."
)
return logger.warning("Could not set tags. Start a session by calling agentops.start_session().")
else:
session.set_tags(tags=tags)

Expand Down Expand Up @@ -198,9 +189,7 @@ def record(self, event: Union[Event, ErrorEvent]) -> None:

session = self._safe_get_session()
if session is None:
return logger.error(
"Could not record event. Start a session by calling agentops.start_session()."
)
return logger.error("Could not record event. Start a session by calling agentops.start_session().")
session.record(event)

def start_session(
Expand Down Expand Up @@ -244,9 +233,7 @@ def start_session(

if self._pre_init_queue["agents"] and len(self._pre_init_queue["agents"]) > 0:
for agent_args in self._pre_init_queue["agents"]:
session.create_agent(
name=agent_args["name"], agent_id=agent_args["agent_id"]
)
session.create_agent(name=agent_args["name"], agent_id=agent_args["agent_id"])
self._pre_init_queue["agents"] = []

self._sessions.append(session)
Expand Down Expand Up @@ -277,9 +264,7 @@ def end_session(
if is_auto_end and self._config.skip_auto_end_session:
return

token_cost = session.end_session(
end_state=end_state, end_state_reason=end_state_reason, video=video
)
token_cost = session.end_session(end_state=end_state, end_state_reason=end_state_reason, video=video)

return token_cost

Expand All @@ -299,9 +284,7 @@ def create_agent(
# if no session passed, assume single session
session = self._safe_get_session()
if session is None:
self._pre_init_queue["agents"].append(
{"name": name, "agent_id": agent_id}
)
self._pre_init_queue["agents"].append({"name": name, "agent_id": agent_id})
else:
session.create_agent(name=name, agent_id=agent_id)

Expand All @@ -326,9 +309,7 @@ def signal_handler(signum, frame):
"""
signal_name = "SIGINT" if signum == signal.SIGINT else "SIGTERM"
logger.info("%s detected. Ending session...", signal_name)
self.end_session(
end_state="Fail", end_state_reason=f"Signal {signal_name} detected"
)
self.end_session(end_state="Fail", end_state_reason=f"Signal {signal_name} detected")
sys.exit(0)

def handle_exception(exc_type, exc_value, exc_traceback):
Expand All @@ -341,9 +322,7 @@ def handle_exception(exc_type, exc_value, exc_traceback):
exc_traceback (TracebackType): A traceback object encapsulating the call stack at the
point where the exception originally occurred.
"""
formatted_traceback = "".join(
traceback.format_exception(exc_type, exc_value, exc_traceback)
)
formatted_traceback = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))

for session in self._sessions:
session.end_session(
Expand Down Expand Up @@ -376,13 +355,7 @@ def add_pre_init_warning(self, message: str):
# replaces the session currently stored with a specific session_id, with a new session
def _update_session(self, session: Session):
self._sessions[
self._sessions.index(
[
sess
for sess in self._sessions
if sess.session_id == session.session_id
][0]
)
self._sessions.index([sess for sess in self._sessions if sess.session_id == session.session_id][0])
] = session

def _safe_get_session(self) -> Optional[Session]:
Expand All @@ -392,9 +365,7 @@ def _safe_get_session(self) -> Optional[Session]:
return self._sessions[0]

if len(self._sessions) > 1:
calling_function = inspect.stack()[
2
].function # Using index 2 because we have a wrapper at index 1
calling_function = inspect.stack()[2].function # Using index 2 because we have a wrapper at index 1
return logger.warning(
f"Multiple sessions detected. You must use session.{calling_function}(). More info: https://docs.agentops.ai/v1/concepts/core-concepts#session-management"
)
Expand Down
27 changes: 8 additions & 19 deletions agentops/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ async def async_wrapper(*args, session: Optional[Session] = None, **kwargs):
arg_names = list(func_args.keys())
# Get default values
arg_values = {
name: func_args[name].default
for name in arg_names
if func_args[name].default is not inspect._empty
name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty
}
# Update with positional arguments
arg_values.update(dict(zip(arg_names, args)))
Expand Down Expand Up @@ -111,9 +109,7 @@ def sync_wrapper(*args, session: Optional[Session] = None, **kwargs):
arg_names = list(func_args.keys())
# Get default values
arg_values = {
name: func_args[name].default
for name in arg_names
if func_args[name].default is not inspect._empty
name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty
}
# Update with positional arguments
arg_values.update(dict(zip(arg_names, args)))
Expand Down Expand Up @@ -191,9 +187,7 @@ async def async_wrapper(*args, session: Optional[Session] = None, **kwargs):
arg_names = list(func_args.keys())
# Get default values
arg_values = {
name: func_args[name].default
for name in arg_names
if func_args[name].default is not inspect._empty
name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty
}
# Update with positional arguments
arg_values.update(dict(zip(arg_names, args)))
Expand Down Expand Up @@ -257,9 +251,7 @@ def sync_wrapper(*args, session: Optional[Session] = None, **kwargs):
arg_names = list(func_args.keys())
# Get default values
arg_values = {
name: func_args[name].default
for name in arg_names
if func_args[name].default is not inspect._empty
name: func_args[name].default for name in arg_names if func_args[name].default is not inspect._empty
}
# Update with positional arguments
arg_values.update(dict(zip(arg_names, args)))
Expand Down Expand Up @@ -338,20 +330,17 @@ def new_init(self, *args, **kwargs):
session=session,
)
except AttributeError as e:
Client().add_pre_init_warning(
f"Failed to track an agent {name} with the @track_agent decorator."
)
logger.warning(
"Failed to track an agent with the @track_agent decorator."
)
Client().add_pre_init_warning(f"Failed to track an agent {name} with the @track_agent decorator.")
logger.warning("Failed to track an agent with the @track_agent decorator.")
original_init(self, *args, **kwargs)

obj.__init__ = new_init

elif inspect.isfunction(obj):
obj.agent_ops_agent_id = str(uuid4()) # type: ignore
Client().create_agent(
name=obj.agent_ops_agent_name, agent_id=obj.agent_ops_agent_id # type: ignore
name=obj.agent_ops_agent_name,
agent_id=obj.agent_ops_agent_id, # type: ignore
)

else:
Expand Down
19 changes: 9 additions & 10 deletions agentops/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from .log_config import logger
from uuid import UUID
from importlib.metadata import version


def get_ISO_time():
Expand Down Expand Up @@ -38,7 +37,9 @@ def filter_dict(obj):
k: (
filter_dict(v)
if isinstance(v, (dict, list)) or is_jsonable(v)
else str(v) if isinstance(v, UUID) else ""
else str(v)
if isinstance(v, UUID)
else ""
)
for k, v in obj.items()
}
Expand All @@ -47,7 +48,9 @@ def filter_dict(obj):
(
filter_dict(x)
if isinstance(x, (dict, list)) or is_jsonable(x)
else str(x) if isinstance(x, UUID) else ""
else str(x)
if isinstance(x, UUID)
else ""
)
for x in obj
]
Expand Down Expand Up @@ -85,9 +88,7 @@ def remove_unwanted_items(value):
"""Recursively remove self key and None/... values from dictionaries so they aren't serialized"""
if isinstance(value, dict):
return {
k: remove_unwanted_items(v)
for k, v in value.items()
if v is not None and v is not ... and k != "self"
k: remove_unwanted_items(v) for k, v in value.items() if v is not None and v is not ... and k != "self"
}
elif isinstance(value, list):
return [remove_unwanted_items(item) for item in value]
Expand All @@ -106,9 +107,7 @@ def check_call_stack_for_agent_id() -> Union[UUID, None]:
# We stop looking up the stack at main because after that we see global variables
if var == "__main__":
return None
if hasattr(var, "agent_ops_agent_id") and getattr(
var, "agent_ops_agent_id"
):
if hasattr(var, "agent_ops_agent_id") and getattr(var, "agent_ops_agent_id"):
logger.debug(
"LLM call from agent named: %s",
getattr(var, "agent_ops_agent_name"),
Expand Down Expand Up @@ -141,7 +140,7 @@ def check_agentops_update():

if not latest_version == current_version:
logger.warning(
f" WARNING: agentops is out of date. Please update with the command: 'pip install --upgrade agentops'"
" WARNING: agentops is out of date. Please update with the command: 'pip install --upgrade agentops'"
)
except Exception as e:
logger.debug(f"Failed to check for updates: {e}")
Expand Down
Loading

0 comments on commit a777c0a

Please sign in to comment.