Skip to content

Commit

Permalink
[Python] 0.1.64 |Accept RunnableConfig|Customize OAI wrapper name|@Tr…
Browse files Browse the repository at this point in the history
…aceable typing|Cache default RunTree Client

## Read parent run info from RunnableConfig if passed to function decorated with traceable

- pass config into wrapped func only if signature declares it
- modify signature of wrapper func to declare config as a kw arg,
otherwise runnables don't pass it in
- extract client and project_name from tracer, in addition to parent run
info
- update as_runnable to delegate run tree creation to new method


## Custom Run Name support in OpenAI Client

## Improve @Traceable typing #

Improves for python >= 3.10. Should in theory not change anything for
users of 3.8 and 3.9 (at least, things pass in our 3.8 linting here...
Apologies in advance to any mypy acolytes on 3.{8,9} who gain undesired
linting errors after this change)

Uses:
1. ParamSpec (available in 3.10 and onwards)
2. Protocols (already used)

Even though python doesn't naturally support keyword-only concatenation,
we can work around this with protocols and duck typing to communicate
the actual returned type of "the same function signature + a keyword
only langsmith_extra arg"

Python's typing situation makes it hard to make everyone happy, but
hopefully this strikes a better compromise than before (typing kwargs as
Any in the wrapped function; too lenient)


## Cache default run tree client

---------

Co-authored-by: William Fu-Hinthorn <[email protected]>
  • Loading branch information
nfcampos and hinthornw authored May 30, 2024
1 parent 1a42623 commit 621fae6
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 111 deletions.
2 changes: 1 addition & 1 deletion python/langsmith/env/_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_git_info(remote: str = "origin") -> GitInfo:
dirty=None,
tags=None,
repo_name=None,
)
)

return {
"remote_url": exec_git(["remote", "get-url", remote]),
Expand Down
18 changes: 15 additions & 3 deletions python/langsmith/env/_runtime_env.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Environment information."""

import functools
import logging
import os
Expand Down Expand Up @@ -77,6 +78,7 @@ def get_runtime_environment() -> dict:
"py_implementation": platform.python_implementation(),
"runtime_version": platform.python_version(),
"langchain_version": get_langchain_environment(),
"langchain_core_version": get_langchain_core_version(),
**shas,
}

Expand All @@ -91,6 +93,16 @@ def get_langchain_environment() -> Optional[str]:
return None


@functools.lru_cache(maxsize=1)
def get_langchain_core_version() -> Optional[str]:
try:
import langchain_core # type: ignore

return langchain_core.__version__
except ImportError:
return None


@functools.lru_cache(maxsize=1)
def get_docker_version() -> Optional[str]:
import subprocess
Expand Down Expand Up @@ -138,9 +150,9 @@ def get_docker_environment() -> dict:
compose_command = _get_compose_command()
return {
"docker_version": get_docker_version(),
"docker_compose_command": " ".join(compose_command)
if compose_command is not None
else None,
"docker_compose_command": (
" ".join(compose_command) if compose_command is not None else None
),
"docker_compose_version": get_docker_compose_version(),
}

Expand Down
6 changes: 4 additions & 2 deletions python/langsmith/evaluation/_arunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ async def wait(self) -> None:


async def _aforward(
fn: rh.SupportsLangsmithExtra[Awaitable],
fn: rh.SupportsLangsmithExtra[[dict], Awaitable],
example: schemas.Example,
experiment_name: str,
metadata: dict,
Expand Down Expand Up @@ -839,7 +839,9 @@ def _get_run(r: run_trees.RunTree) -> None:
)


def _ensure_async_traceable(target: ATARGET_T) -> rh.SupportsLangsmithExtra[Awaitable]:
def _ensure_async_traceable(
target: ATARGET_T,
) -> rh.SupportsLangsmithExtra[[dict], Awaitable]:
if not asyncio.iscoroutinefunction(target):
raise ValueError(
"Target must be an async function. For sync functions, use evaluate."
Expand Down
6 changes: 4 additions & 2 deletions python/langsmith/evaluation/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1466,12 +1466,14 @@ def _resolve_data(
return data


def _ensure_traceable(target: TARGET_T) -> rh.SupportsLangsmithExtra:
def _ensure_traceable(
target: TARGET_T | rh.SupportsLangsmithExtra[[dict], dict],
) -> rh.SupportsLangsmithExtra[[dict], dict]:
"""Ensure the target function is traceable."""
if not callable(target):
raise ValueError("Target must be a callable function.")
if rh.is_traceable_function(target):
fn = cast(rh.SupportsLangsmithExtra, target)
fn = target
else:
fn = rh.traceable(name="Target")(target)
return fn
Expand Down
16 changes: 10 additions & 6 deletions python/langsmith/evaluation/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ def __init__(
self.afunc = run_helpers.ensure_traceable(func)
self._name = getattr(func, "__name__", "DynamicRunEvaluator")
else:
self.func = cast(
run_helpers.SupportsLangsmithExtra[_RUNNABLE_OUTPUT],
run_helpers.ensure_traceable(func),
self.func = run_helpers.ensure_traceable(
cast(Callable[[Run, Optional[Example]], _RUNNABLE_OUTPUT], func)
)
self._name = getattr(func, "__name__", "DynamicRunEvaluator")

Expand Down Expand Up @@ -383,9 +382,14 @@ def __init__(
self.afunc = run_helpers.ensure_traceable(func)
self._name = getattr(func, "__name__", "DynamicRunEvaluator")
else:
self.func = cast(
run_helpers.SupportsLangsmithExtra[_COMPARISON_OUTPUT],
run_helpers.ensure_traceable(func),
self.func = run_helpers.ensure_traceable(
cast(
Callable[
[Sequence[Run], Optional[Example]],
_COMPARISON_OUTPUT,
],
func,
)
)
self._name = getattr(func, "__name__", "DynamicRunEvaluator")

Expand Down
Loading

0 comments on commit 621fae6

Please sign in to comment.