Skip to content

Commit

Permalink
feat: Add display_name to ops (#2655)
Browse files Browse the repository at this point in the history
* changed to WEAVE_KWARGS_KEY=__weave
  • Loading branch information
tssweeney authored Oct 10, 2024
1 parent 4f210f8 commit 05e9149
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 7 deletions.
26 changes: 26 additions & 0 deletions docs/docs/guides/core-types/evaluations.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,32 @@ asyncio.run(evaluation.evaluate(model))

This will run `predict` on each example and score the output with each scoring functions.

#### Custom Naming

You can change the name of the Evaluation itself by passing a `name` parameter to the `Evaluation` class.

```python
evaluation = Evaluation(
dataset=examples, scorers=[match_score1], name="My Evaluation"
)
```

You can also change the name of individual evaluations by setting the `display_name` key of the `__weave` dictionary.

:::note

Using the `__weave` dictionary sets the call display name which is distinct from the Evaluation object name. In the
UI, you will see the display name if set, otherwise the Evaluation object name will be used.

:::

```python
evaluation = Evaluation(
dataset=examples, scorers=[match_score1]
)
evaluation.evaluate(model, __weave={"display_name": "My Evaluation Run"})
```

### Define a function to evaluate

Alternatively, you can also evaluate a function that is wrapped in a `@weave.op()`.
Expand Down
14 changes: 13 additions & 1 deletion docs/docs/guides/tracking/tracing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,19 @@ instance.my_method.call(instance, "World")

#### Call Display Name

Sometimes you may want to override the display name of a call. You can achieve this in one of three ways:
Sometimes you may want to override the display name of a call. You can achieve this in one of four ways:

0. Change the display name at the time of calling the op:

```python showLineNumbers
result = my_function("World", __weave={"display_name": "My Custom Display Name"})
```

:::note

Using the `__weave` dictionary sets the call display name which will take precedence over the Op display name.

:::

1. Change the display name on a per-call basis. This uses the [`Op.call`](../../reference/python-sdk/weave/trace/weave.trace.op.md#function-call) method to return a `Call` object, which you can then use to set the display name using [`Call.set_display_name`](../../reference/python-sdk/weave/trace/weave.trace.weave_client.md#method-set_display_name).
```python showLineNumbers
Expand Down
22 changes: 22 additions & 0 deletions tests/trace/test_weave_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1499,3 +1499,25 @@ def test_object_version_read(client):
digest="v1",
)
)


@pytest.mark.asyncio
async def test_op_calltime_display_name(client):
@weave.op()
def my_op(a: int) -> int:
return a

result = my_op(1, __weave={"display_name": "custom_display_name"})
calls = list(my_op.calls())
assert len(calls) == 1
call = calls[0]
assert call.display_name == "custom_display_name"

evaluation = weave.Evaluation(dataset=[{"a": 1}], scorers=[])
res = await evaluation.evaluate(
my_op, __weave={"display_name": "custom_display_name"}
)
calls = list(evaluation.evaluate.calls())
assert len(calls) == 1
call = calls[0]
assert call.display_name == "custom_display_name"
28 changes: 22 additions & 6 deletions weave/trace/op.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Mapping,
Optional,
Protocol,
TypedDict,
Union,
cast,
overload,
Expand All @@ -32,6 +33,7 @@

logger = logging.getLogger(__name__)

WEAVE_KWARGS_KEY = "__weave"

if TYPE_CHECKING:
from weave.trace.weave_client import Call, CallsIter
Expand Down Expand Up @@ -116,6 +118,10 @@ def _apply_fn_defaults_to_inputs(
return inputs


class WeaveKwargs(TypedDict):
display_name: Optional[str]


@runtime_checkable
class Op(Protocol):
"""
Expand Down Expand Up @@ -195,7 +201,9 @@ def _is_unbound_method(func: Callable) -> bool:
return bool(is_method)


def _create_call(func: Op, *args: Any, **kwargs: Any) -> "Call":
def _create_call(
func: Op, *args: Any, __weave: Optional[WeaveKwargs] = None, **kwargs: Any
) -> "Call":
client = weave_client_context.require_weave_client()

try:
Expand All @@ -208,6 +216,8 @@ def _create_call(func: Op, *args: Any, **kwargs: Any) -> "Call":
if "api_key" in inputs_with_defaults:
inputs_with_defaults["api_key"] = "REDACTED"

call_time_display_name = __weave.get("display_name") if __weave else None

# If/When we do memoization, this would be a good spot

parent_call = call_context.get_current_call()
Expand All @@ -217,7 +227,8 @@ def _create_call(func: Op, *args: Any, **kwargs: Any) -> "Call":
func,
inputs_with_defaults,
parent_call,
display_name=func.call_display_name,
# Very important for `call_time_display_name` to take precedence over `func.call_display_name`
display_name=call_time_display_name or func.call_display_name,
attributes=attributes,
)

Expand Down Expand Up @@ -300,7 +311,9 @@ async def _call_async() -> Coroutine[Any, Any, Any]:
return None, call


def call(op: Op, *args: Any, **kwargs: Any) -> tuple[Any, "Call"]:
def call(
op: Op, *args: Any, __weave: Optional[WeaveKwargs] = None, **kwargs: Any
) -> tuple[Any, "Call"]:
"""
Executes the op and returns both the result and a Call representing the execution.
Expand All @@ -317,7 +330,7 @@ def add(a: int, b: int) -> int:
result, call = add.call(1, 2)
```
"""
c = _create_call(op, *args, **kwargs)
c = _create_call(op, *args, __weave=__weave, **kwargs)
return _execute_call(op, c, *args, __should_raise=False, **kwargs)


Expand Down Expand Up @@ -438,6 +451,7 @@ def create_wrapper(func: Callable) -> Op:

@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
__weave: Optional[WeaveKwargs] = kwargs.pop(WEAVE_KWARGS_KEY, None)
if settings.should_disable_weave():
return await func(*args, **kwargs)
if weave_client_context.get_weave_client() is None:
Expand All @@ -447,7 +461,7 @@ async def wrapper(*args: Any, **kwargs: Any) -> Any:
try:
# This try/except allows us to fail gracefully and
# still let the user code continue to execute
call = _create_call(wrapper, *args, **kwargs) # type: ignore
call = _create_call(wrapper, *args, __weave=__weave, **kwargs) # type: ignore
except Exception as e:
if get_raise_on_captured_errors():
raise
Expand All @@ -462,6 +476,7 @@ async def wrapper(*args: Any, **kwargs: Any) -> Any:

@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
__weave: Optional[WeaveKwargs] = kwargs.pop(WEAVE_KWARGS_KEY, None)
if settings.should_disable_weave():
return func(*args, **kwargs)
if weave_client_context.get_weave_client() is None:
Expand All @@ -471,7 +486,8 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
try:
# This try/except allows us to fail gracefully and
# still let the user code continue to execute
call = _create_call(wrapper, *args, **kwargs) # type: ignore

call = _create_call(wrapper, *args, __weave=__weave, **kwargs) # type: ignore
except Exception as e:
if get_raise_on_captured_errors():
raise
Expand Down

0 comments on commit 05e9149

Please sign in to comment.