Skip to content

Commit

Permalink
fix: error messages, reading ssh keys, and option to print all agent …
Browse files Browse the repository at this point in the history
…nodes in a project workflow (#15)

* docs: escape pasted url for test cli command

* fix: ruff formatting and runner docstrings

* Settings: expand path on ssh key file

* docs: pip install example on README

* fix: better errors from runner

* print errors with colors on stage creation

* fix: better error statements for runner

* feat(cli): add option to print project agent nodes for convenience

* docs: remove UUID from examples as no longer necessary

* fix: typo in error message
  • Loading branch information
frederik-encord authored Nov 6, 2024
1 parent 2cae68b commit fff80d8
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 128 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
Easily build agents for the Encord echo system.
With just few lines of code, you can take automation to the next level.

```shell
python -m pip install encord-agents
```

**Key features:**

1.**Easy**: Multiple template agents to be adapted and hosted via GCP, own infra, or cloud.
Expand All @@ -66,13 +70,13 @@ from encord_agents.tasks import Runner
runner = Runner(project_hash="<your_project_hash>")


@runner.stage(UUID("<your_agent_stage_uuid>"))
@runner.stage("<your_agent_stage_uuid>")
def by_file_name(lr: LabelRowV2) -> UUID | None:
# Assuming the data_title is of the format "%d.jpg"
# and in the range [0; 100]
priority = int(lr.data_title.split(".")[0]) / 100
lr.set_priority(priority=priority)
return UUID("<your_pathway_uuid>")
return "<your_pathway_uuid>"


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion docs/code_examples/gcp/add_bounding_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Successively, you can test it locally by pasting an editor url into the following command:
```shell
encord-agents test add_bounding_box <editor_url>
encord-agents test add_bounding_box '<editor_url>'
```
"""

Expand Down
7 changes: 3 additions & 4 deletions docs/code_examples/tasks/prioritize_by_data_title.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from uuid import UUID
from encord.objects.ontology_labels_impl import LabelRowV2
from encord_agents.tasks import Runner

runner = Runner(project_hash="<your_project_hash>")


@runner.stage(UUID("<your_agent_stage_uuid>"))
def by_file_name(lr: LabelRowV2) -> UUID | None:
@runner.stage("<your_agent_stage_uuid>")
def by_file_name(lr: LabelRowV2) -> str | None:
# Assuming the data_title is of the format "%d.jpg"
# and in the range [0; 100]
priority = int(lr.data_title.split(".")[0]) / 100
lr.set_priority(priority=priority)
return UUID("<your_pathway_uuid>")
return "<your_pathway_uuid>"


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from uuid import UUID
from encord.objects.ontology_labels_impl import LabelRowV2
from encord_agents.tasks import Runner

runner = Runner(project_hash="<your_project_hash>")


@runner.stage(UUID("1e7751b3-6dc8-4796-a64b-d1323918b8f4"))
@runner.stage("1e7751b3-6dc8-4796-a64b-d1323918b8f4")
def by_file_name(lr: LabelRowV2) -> str | None:
# Assuming the data_title is of the format "%d.jpg"
# and in the range [0; 100]
Expand Down
6 changes: 2 additions & 4 deletions docs/code_examples/tasks/twin_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@
)
checklist_attribute = checklist_classification.attributes[0]

# 3. Define the agent
from uuid import UUID


@runner.stage(stage=UUID("<transfer_agent_stage_uuid>"))
# 3. Define the agent
@runner.stage(stage="<transfer_agent_stage_uuid>")
def copy_labels(
manually_annotated_lr: LabelRowV2,
twin: Annotated[
Expand Down
14 changes: 14 additions & 0 deletions docs/code_examples/tasks/wrong_stage_and_pathway_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from encord.objects.ontology_labels_impl import LabelRowV2
from encord_agents.tasks import Runner

r = Runner()


@r.stage(stage="wrong")
def my_stage(lr: LabelRowV2) -> None:
print(lr.data_title)


if __name__ == "__main__":
# r()
r.run()
5 changes: 4 additions & 1 deletion docs/editor_agents/fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,12 @@ Then, run

```shell
source venv/bin/activate
encord-agents test local my_agent <the_pasted_url>
encord-agents test local my_agent '<the_pasted_url>'
```

!!! warning
Notice the single quotes around `<the_pasted_url>`. They are important and should be there because you might copy a url with, e.g., an `&` character that have a [special meaning](https://www.howtogeek.com/439199/15-special-characters-you-need-to-know-for-bash/#amp-background-process){ target="_blank", rel="noopener noreferrer" } if it is not within a string (or escaped).

Refresh the label editor in your browser to see the effect that you applied to the `label_row: LabelRowV2` happening.

## Deployment
Expand Down
5 changes: 4 additions & 1 deletion docs/editor_agents/gcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,12 @@ Then, run

```shell
source venv/bin/activate
encord-agents test local my_agent <the_pasted_url>
encord-agents test local my_agent '<the_pasted_url>'
```

!!! warning
Notice the single quotes around `<the_pasted_url>`. They are important and should be there because you might copy a url with, e.g., an `&` character that have a [special meaning](https://www.howtogeek.com/439199/15-special-characters-you-need-to-know-for-bash/#amp-background-process){ target="_blank", rel="noopener noreferrer" } if it is not within a string (or escaped).

Refresh the label editor in your browser to see the effect that you applied to the `label_row: LabelRowV2` happening.

## Deployment
Expand Down
11 changes: 11 additions & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ Furthermore, is has just one pathway called "annotate."

Copy the `Project ID` in the top left of the project page.

!!! tip
After [authenticating](./authentication.md), you can check if your existing project has any agent nodes by running this command:
```shell
encord-agents print agent-nodes <your_project_hash>
```
If the project has agent nodes in the workflow, you should see a list similar to this:
```shell
AgentStage(title="pre-label", uuid="b9c1363c-615f-4125-ae1c-a81e19331c96")
AgentStage(title="evaluate", uuid="28d1bcc9-6a3a-4229-8c06-b498fcaf94a0")
```

### 3. Define your agent

In your freshly created directory, create a python file.
Expand Down
5 changes: 2 additions & 3 deletions docs/task_agents/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,10 @@ Which would mean that the agents would be defined with the following decorator t
make the workflow stage association explicit.

```python
from uuid import UUID
@runner.stage(stage=UUID("60d9f14f-755e-40fd-...")) # <- last bit omitted
@runner.stage(stage="60d9f14f-755e-40fd-...") # <- last bit omitted
```

> Notice the match between the uuid in the "label transfer" agent stage of the workflow in Project A and the UUID in the decorator.
> Notice the match between the uuid in the "label transfer" agent stage of the workflow in Project A and the uuid in the decorator.
**To prepare your projects:**

Expand Down
9 changes: 6 additions & 3 deletions encord_agents/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@
import typer

from .gcp import app as gcp_app
from .test import app as test_app
from .print import app as print_app
from .test import app as test_app

app = typer.Typer(rich_markup_mode="rich")
app.add_typer(gcp_app, name="gcp")
app.add_typer(test_app, name="test")
app.add_typer(print_app, name="print")


@app.callback(invoke_without_command=True)
def version(version_: bool = typer.Option(False, "--version", "-v", "-V", help="Print the current version of Encord Agents")):
def version(
version_: bool = typer.Option(False, "--version", "-v", "-V", help="Print the current version of Encord Agents"),
):
if version_:
import rich

from encord_agents import __version__ as ea_version

rich.print(f"[purple]encord-agents[/purple] version: [green]{ea_version}[/green]")
exit()

68 changes: 54 additions & 14 deletions encord_agents/cli/print.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import sys

from encord.orm.workflow import WorkflowStageType
from typer import Typer

from encord_agents.core.settings import Settings

app = Typer(
name="print",
help="Utility to print system info, e.g., for bug reporting.",
Expand All @@ -9,21 +13,57 @@
)


@app.command(name="agent-nodes")
def print_agent_nodes(project_hash: str):
"""
Prints agent nodes from project.
Given the project hash, loads the project and prints the agent nodes.
Args:
project_hash: The project hash for which to print agent nodes.
"""
import rich
from encord.exceptions import AuthorisationError
from encord.user_client import EncordUserClient

_ = Settings()
client = EncordUserClient.create_with_ssh_private_key()
try:
project = client.get_project(project_hash)
except AuthorisationError:
rich.print(f"You do not seem to have access to project with project hash `[purple]{project_hash}[/purple]`")
exit()

agent_nodes = [
f'AgentStage(title="{n.title}", uuid="{n.uuid}")'
for n in project.workflow.stages
if n.stage_type == WorkflowStageType.AGENT
]
if not agent_nodes:
print("Project does not have any agent nodes.")
return

for node in agent_nodes:
rich.print(node)


@app.command(name="system-info")
def print_system_info():
"""
[bold]Prints[/bold] the information of the system for the purpose of bug reporting.
"""
import platform
print("System Information:")
uname = platform.uname()
print(f"\tSystem: {uname.system}")
print(f"\tRelease: {uname.release}")
print(f"\tMachine: {uname.machine}")
print(f"\tProcessor: {uname.processor}")
print(f"\tPython: {sys.version}")

import encord_agents
print(f"encord-agents version: {encord_agents.__version__}")
"""
[bold]Prints[/bold] the information of the system for the purpose of bug reporting.
"""
import platform

print("System Information:")
uname = platform.uname()
print(f"\tSystem: {uname.system}")
print(f"\tRelease: {uname.release}")
print(f"\tMachine: {uname.machine}")
print(f"\tProcessor: {uname.processor}")
print(f"\tPython: {sys.version}")

import encord_agents

print(f"encord-agents version: {encord_agents.__version__}")
12 changes: 11 additions & 1 deletion encord_agents/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from typing import Optional

from pydantic import Field, model_validator
from pydantic import Field, field_validator, model_validator
from pydantic_settings import BaseSettings


Expand All @@ -29,6 +29,16 @@ class Settings(BaseSettings):
[the platform docs](https://docs.encord.com/platform-documentation/Annotate/annotate-api-keys).
"""

@field_validator("ssh_key_file")
@classmethod
def check_path_expand_and_exists(cls, path: Path | None):
if path is None:
return path

path = path.expanduser()
assert path.is_file(), f"Provided ssh key file (ENCORD_SSH_KEY_FILE: '{path}') does not exist"
return path

@model_validator(mode="after")
def check_key(self):
assert any(
Expand Down
Loading

0 comments on commit fff80d8

Please sign in to comment.