Skip to content

Commit

Permalink
tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
zzstoatzz committed Feb 4, 2025
1 parent 901842a commit 70235f1
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 14 deletions.
66 changes: 56 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ poem = marvin.run("Write a short poem about artificial intelligence")
print(poem)
```
<details>
<summary>View the <code>poem</code></summary>
<summary><i>output</i></summary>
<pre>
In circuits and code, a mind does bloom,
With algorithms weaving through the gloom.
Expand Down Expand Up @@ -58,17 +58,65 @@ Marvin is built around a few powerful abstractions that make it easy to work wit

Tasks are the fundamental unit of work in Marvin. Each task represents a clear objective that can be accomplished by an AI agent:

The simplest way to run a task is with `marvin.run`:
```python
# The simplest way to run a task
import marvin
result = marvin.run("Write a haiku about coding")
print(result)
```

<details>
<summary><i>output</i></summary>

```bash
Lines of code unfold,
Digital whispers create
Virtual landscapes.
```
</details>

> [!NOTE]
>
> While this result produces _type_ safe results 🙂, it runs untrusted shell commands.
Add context and/or tools to achieve more specific and complex results:
```python
import platform
import subprocess
from pydantic import IPvAnyAddress

def run_shell_command(command: list[str]) -> str:
"""e.g. ['ls', '-l'] or ['git', '--no-pager', 'diff', '--cached']"""
return subprocess.check_output(command).decode()

# Create a task with more control
task = marvin.Task(
instructions="Write a haiku about coding",
result_type=str,
tools=[my_custom_tool]
instructions="find the current ip address",
result_type=IPvAnyAddress,
tools=[run_shell_command],
context={"os": platform.system()},
)

task.run()
```
<details>
<summary><i>output</i></summary>

```bash
╭─ Agent "Marvin" (db3cf035) ───────────────────────────────╮
│ Tool: run_shell_command │
│ Input: {'command': ['ipconfig', 'getifaddr', 'en0']} │
│ Status: ✅ │
│ Output: '192.168.0.202\n'
╰───────────────────────────────────────────────────────────╯

╭─ Agent "Marvin" (db3cf035) ───────────────────────────────╮
│ Tool: MarkTaskSuccessful_cb267859 │
│ Input: {'response': {'result': '192.168.0.202'}} │
│ Status: ✅ │
│ Output: 'Final result processed.'
╰───────────────────────────────────────────────────────────╯
```
</details>

Tasks are:
- 🎯 **Objective-Focused**: Each task has clear instructions and a type-safe result
Expand Down Expand Up @@ -251,7 +299,7 @@ print(f"# {article.title}\n\n{article.content}")
```

<details>
<summary><i>Click to see results</i></summary>
<summary><i>output</i></summary>

>**Conversation:**
>```text
Expand Down Expand Up @@ -279,6 +327,4 @@ print(f"# {article.title}\n\n{article.content}")
>- Growing ecosystem of tools and frameworks
>- Used by major companies like Google, Mozilla, and Unity
>```
</details>
## Keep it Simple
</details>
5 changes: 5 additions & 0 deletions examples/hello_marvin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import marvin

poem = marvin.run("Write a short poem about artificial intelligence")

print(poem)
26 changes: 26 additions & 0 deletions examples/hello_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Warning: this example will run untrusted shell commands.
Use with caution.
"""

import platform
import subprocess

from pydantic import IPv4Address

import marvin


def run_shell_command(command: list[str]) -> str:
"""e.g. ['ls', '-l'] or ['git', '--no-pager', 'diff', '--cached']"""
return subprocess.check_output(command).decode()


task = marvin.Task(
instructions="find the current ip address",
result_type=IPv4Address,
tools=[run_shell_command],
context={"os": platform.system()},
)

task.run()
13 changes: 9 additions & 4 deletions src/marvin/tasks/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
from pathlib import Path
from typing import (
TYPE_CHECKING,
Annotated,
Any,
Generic,
Literal,
Optional,
Sequence,
TypeAlias,
TypeVar,
)

Expand Down Expand Up @@ -63,6 +65,9 @@ class TaskState(str, enum.Enum):
SKIPPED = "skipped"


ResultType: TypeAlias = type[T] | Annotated | Labels | Literal["__NOTSET__"]


@dataclass(kw_only=True, init=False)
class Task(Generic[T]):
"""A task is a container for a prompt and its associated state."""
Expand All @@ -77,7 +82,7 @@ class Task(Generic[T]):
kw_only=False,
)

result_type: type[T] | Labels | Literal["__NOTSET__"] = field( # type: ignore[reportRedeclaration]
result_type: ResultType[T] = field( # type: ignore[reportRedeclaration]
default=NOTSET,
metadata={
"description": "The expected type of the result. This can be a type or None if no result is expected. If not set, the result type will be str.",
Expand Down Expand Up @@ -197,7 +202,7 @@ class Task(Generic[T]):
def __init__(
self,
instructions: str,
result_type: type[T] | Labels | Literal["__NOTSET__"] = NOTSET,
result_type: ResultType[T] = NOTSET,
*,
name: str | None = None,
prompt_template: str | Path = Path("task.jinja"),
Expand All @@ -206,8 +211,8 @@ def __init__(
tools: list[Callable[..., Any]] | None = None,
memories: list[Memory] | None = None,
result_validator: Callable[..., Any] | None = None,
parent: "Task[T] | None | Literal['__NOTSET__']" = NOTSET,
depends_on: Sequence["Task[T]"] | None = None,
parent: "Task[Any] | None | Literal['__NOTSET__']" = NOTSET,
depends_on: Sequence["Task[Any]"] | None = None,
allow_fail: bool = False,
allow_skip: bool = False,
cli: bool = False,
Expand Down

0 comments on commit 70235f1

Please sign in to comment.