mirror of
https://github.com/hwchase17/langchain.git
synced 2026-01-23 13:19:22 +00:00
Compare commits
1 Commits
cc/bind_to
...
vwp/rm_dep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf92f71037 |
@@ -5,11 +5,9 @@ import logging
|
||||
import os
|
||||
from concurrent.futures import Future, ThreadPoolExecutor, wait
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union
|
||||
from uuid import UUID
|
||||
|
||||
from langchainplus_sdk import LangChainPlusClient
|
||||
|
||||
from langchain.callbacks.tracers.base import BaseTracer
|
||||
from langchain.callbacks.tracers.schemas import (
|
||||
Run,
|
||||
@@ -19,11 +17,27 @@ from langchain.callbacks.tracers.schemas import (
|
||||
from langchain.env import get_runtime_environment
|
||||
from langchain.schema import BaseMessage, messages_to_dict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import langsmith
|
||||
from langsmith import Client as LangSmithClient
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
_LOGGED = set()
|
||||
_TRACERS: List[LangChainTracer] = []
|
||||
|
||||
|
||||
def _lazy_import_langsmith() -> langsmith:
|
||||
try:
|
||||
import langsmith
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Please install langsmith to use the LangChainTracer."
|
||||
" You can do this by running `pip install langsmith`."
|
||||
)
|
||||
return langsmith
|
||||
|
||||
|
||||
def log_error_once(method: str, exception: Exception) -> None:
|
||||
"""Log an error once."""
|
||||
global _LOGGED
|
||||
@@ -46,7 +60,7 @@ class LangChainTracer(BaseTracer):
|
||||
self,
|
||||
example_id: Optional[Union[UUID, str]] = None,
|
||||
project_name: Optional[str] = None,
|
||||
client: Optional[LangChainPlusClient] = None,
|
||||
client: Optional[LangSmithClient] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize the LangChain tracer."""
|
||||
@@ -60,7 +74,11 @@ class LangChainTracer(BaseTracer):
|
||||
)
|
||||
# set max_workers to 1 to process tasks in order
|
||||
self.executor = ThreadPoolExecutor(max_workers=1)
|
||||
self.client = client or LangChainPlusClient()
|
||||
if client is not None:
|
||||
self.client = client
|
||||
else:
|
||||
langsmith = _lazy_import_langsmith()
|
||||
self.client = langsmith.Client()
|
||||
self._futures: Set[Future] = set()
|
||||
global _TRACERS
|
||||
_TRACERS.append(self)
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from langchainplus_sdk.schemas import RunBase as BaseRunV2
|
||||
from langchainplus_sdk.schemas import RunTypeEnum
|
||||
from pydantic import BaseModel, Field, root_validator
|
||||
|
||||
from langchain.schema import LLMResult
|
||||
@@ -88,13 +87,58 @@ class ToolRun(BaseRun):
|
||||
# Begin V2 API Schemas
|
||||
|
||||
|
||||
class Run(BaseRunV2):
|
||||
class RunTypeEnum(str, Enum):
|
||||
"""Enum for run types."""
|
||||
|
||||
tool = "tool"
|
||||
chain = "chain"
|
||||
llm = "llm"
|
||||
|
||||
|
||||
class Run(BaseModel):
|
||||
"""Run schema for the V2 API in the Tracer."""
|
||||
|
||||
id: UUID
|
||||
"""The UUID of the run."""
|
||||
name: str
|
||||
"""The name of the run, usually taken from the serialized object's ID."""
|
||||
start_time: datetime.datetime
|
||||
"""The start time of the run."""
|
||||
run_type: Union[RunTypeEnum, str]
|
||||
"""The type of run."""
|
||||
inputs: dict
|
||||
"""The inputs to the run."""
|
||||
execution_order: int
|
||||
"""The order in which this run was executed in a run tree."""
|
||||
child_execution_order: int
|
||||
"""The next execution order of child runs."""
|
||||
end_time: Optional[datetime.datetime] = None
|
||||
"""The end time of the run."""
|
||||
extra: Optional[dict] = None
|
||||
"""Extra information about the run."""
|
||||
error: Optional[str] = None
|
||||
"""The error message of the run, if any."""
|
||||
serialized: dict = Field(default_factory=dict)
|
||||
"""The serialized object that was run."""
|
||||
events: Optional[List[Dict]] = None
|
||||
"""The events that occurred during the run."""
|
||||
outputs: Optional[dict] = None
|
||||
"""The outputs of the run."""
|
||||
reference_example_id: Optional[UUID] = None
|
||||
"""The ID of the reference example that was used to run the run, if this
|
||||
run was performed during an evaluation."""
|
||||
parent_run_id: Optional[UUID] = None
|
||||
"""The ID of the parent run if this is not a root."""
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
"""Any tags assigned to the run."""
|
||||
session_id: Optional[UUID] = None
|
||||
"""The Project / Session ID this run belongs to."""
|
||||
child_run_ids: Optional[List[UUID]] = None
|
||||
"""The IDs of the child runs."""
|
||||
child_runs: List[Run] = Field(default_factory=list)
|
||||
tags: Optional[List[str]] = Field(default_factory=list)
|
||||
"""The child runs. These are used during initial tracing."""
|
||||
feedback_stats: Optional[Dict[str, Any]] = None
|
||||
"""Any feedback statistics for this run."""
|
||||
|
||||
@root_validator(pre=True)
|
||||
def assign_name(cls, values: dict) -> dict:
|
||||
|
||||
@@ -104,12 +104,13 @@ def _convert_tool_run_to_wb_span(trace_tree: Any, run: Run) -> trace_tree.Span:
|
||||
def _convert_run_to_wb_span(trace_tree: Any, run: Run) -> trace_tree.Span:
|
||||
attributes = {**run.extra} if run.extra else {}
|
||||
attributes["execution_order"] = run.execution_order
|
||||
end_time = run.end_time if run.end_time is not None else run.start_time
|
||||
|
||||
return trace_tree.Span(
|
||||
span_id=str(run.id) if run.id is not None else None,
|
||||
name=run.serialized.get("name"),
|
||||
start_time_ms=int(run.start_time.timestamp() * 1000),
|
||||
end_time_ms=int(run.end_time.timestamp() * 1000),
|
||||
end_time_ms=int(end_time.timestamp() * 1000),
|
||||
status_code=trace_tree.StatusCode.SUCCESS
|
||||
if run.error is None
|
||||
else trace_tree.StatusCode.ERROR,
|
||||
|
||||
@@ -6,6 +6,7 @@ import functools
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Coroutine,
|
||||
@@ -16,9 +17,6 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from langchainplus_sdk import LangChainPlusClient
|
||||
from langchainplus_sdk.schemas import Example
|
||||
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.callbacks.base import BaseCallbackHandler
|
||||
from langchain.callbacks.manager import Callbacks
|
||||
@@ -35,6 +33,11 @@ from langchain.schema import (
|
||||
messages_from_dict,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import langsmith
|
||||
from langsmith import Client as LangSmithClient
|
||||
from langsmith.schemas import Example
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MODEL_OR_CHAIN_FACTORY = Union[Callable[[], Chain], BaseLanguageModel]
|
||||
@@ -44,6 +47,17 @@ class InputFormatError(Exception):
|
||||
"""Raised when input format is invalid."""
|
||||
|
||||
|
||||
def _lazy_import_langsmith() -> langsmith:
|
||||
try:
|
||||
import langsmith
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Please install langsmith to use the langchain runner utils."
|
||||
" You can do this by running `pip install langsmith`."
|
||||
)
|
||||
return langsmith
|
||||
|
||||
|
||||
def _get_prompts(inputs: Dict[str, Any]) -> List[str]:
|
||||
"""Get prompts from inputs."""
|
||||
if not inputs:
|
||||
@@ -448,7 +462,7 @@ async def arun_on_dataset(
|
||||
num_repetitions: int = 1,
|
||||
project_name: Optional[str] = None,
|
||||
verbose: bool = False,
|
||||
client: Optional[LangChainPlusClient] = None,
|
||||
client: Optional[LangSmithClient] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
@@ -474,7 +488,11 @@ async def arun_on_dataset(
|
||||
Returns:
|
||||
A dictionary containing the run's project name and the resulting model outputs.
|
||||
"""
|
||||
client_ = client or LangChainPlusClient()
|
||||
if client is not None:
|
||||
client_ = client
|
||||
else:
|
||||
langsmith = _lazy_import_langsmith()
|
||||
client = langsmith.Client()
|
||||
project_name = _get_project_name(project_name, llm_or_chain_factory, dataset_name)
|
||||
dataset = client_.read_dataset(dataset_name=dataset_name)
|
||||
examples = client_.list_examples(dataset_id=str(dataset.id))
|
||||
@@ -501,7 +519,7 @@ def run_on_dataset(
|
||||
num_repetitions: int = 1,
|
||||
project_name: Optional[str] = None,
|
||||
verbose: bool = False,
|
||||
client: Optional[LangChainPlusClient] = None,
|
||||
client: Optional[LangSmithClient] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Run the chain on a dataset and store traces to the specified project name.
|
||||
@@ -525,7 +543,11 @@ def run_on_dataset(
|
||||
Returns:
|
||||
A dictionary containing the run's project name and the resulting model outputs.
|
||||
"""
|
||||
client_ = client or LangChainPlusClient()
|
||||
if client is not None:
|
||||
client_ = client
|
||||
else:
|
||||
langsmith = _lazy_import_langsmith()
|
||||
client = langsmith.Client()
|
||||
project_name = _get_project_name(project_name, llm_or_chain_factory, dataset_name)
|
||||
dataset = client_.read_dataset(dataset_name=dataset_name)
|
||||
examples = client_.list_examples(dataset_id=str(dataset.id))
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langchainplus_sdk import EvaluationResult, RunEvaluator
|
||||
from langchainplus_sdk.schemas import Example, Run
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
from langchain.callbacks.manager import (
|
||||
AsyncCallbackManagerForChainRun,
|
||||
@@ -13,6 +10,18 @@ from langchain.callbacks.manager import (
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.schema import RUN_KEY, BaseOutputParser
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langsmith import EvaluationResult, RunEvaluator
|
||||
from langsmith.schemas import Example, Run
|
||||
else:
|
||||
try:
|
||||
from langsmith import EvaluationResult, RunEvaluator
|
||||
from langsmith.schemas import Example, Run
|
||||
except ImportError:
|
||||
from pydantic import BaseModel
|
||||
|
||||
EvaluationResult = BaseModel
|
||||
|
||||
|
||||
class RunEvaluatorInputMapper:
|
||||
"""Map the inputs of a run to the inputs of an evaluation."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Dict, List, Mapping, Optional, Sequence, Union
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Sequence, Union
|
||||
|
||||
from langchainplus_sdk.evaluation import EvaluationResult
|
||||
from langchainplus_sdk.schemas import Example, Run, RunTypeEnum
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
@@ -28,6 +28,16 @@ from langchain.prompts.prompt import PromptTemplate
|
||||
from langchain.schema import OutputParserException
|
||||
from langchain.tools.base import BaseTool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langsmith import EvaluationResult
|
||||
from langsmith.schemas import Example, Run, RunTypeEnum
|
||||
else:
|
||||
try:
|
||||
from langsmith import EvaluationResult
|
||||
from langsmith.schemas import Example, Run, RunTypeEnum
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
_QA_PROMPTS = {
|
||||
"qa": QA_DEFAULT_PROMPT,
|
||||
"sql": SQL_PROMPT,
|
||||
|
||||
@@ -1,8 +1,33 @@
|
||||
"""Script to run langchain-server locally using docker-compose."""
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from langchainplus_sdk.cli.main import get_docker_compose_command
|
||||
|
||||
def get_docker_compose_command() -> List[str]:
|
||||
"""Get the correct docker compose command for this system."""
|
||||
try:
|
||||
subprocess.check_call(
|
||||
["docker", "compose", "--version"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
return ["docker", "compose"]
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
try:
|
||||
subprocess.check_call(
|
||||
["docker-compose", "--version"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
return ["docker-compose"]
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
raise ValueError(
|
||||
"Neither 'docker compose' nor 'docker-compose'"
|
||||
" commands are available. Please install the Docker"
|
||||
" server following the instructions for your operating"
|
||||
" system at https://docs.docker.com/engine/install/"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
@@ -106,7 +106,7 @@ pyspark = {version = "^3.4.0", optional = true}
|
||||
clarifai = {version = "9.1.0", optional = true}
|
||||
tigrisdb = {version = "^1.0.0b6", optional = true}
|
||||
nebula3-python = {version = "^3.4.0", optional = true}
|
||||
langchainplus-sdk = ">=0.0.17"
|
||||
langsmith = {version = ">=0.0.17", optional = true}
|
||||
awadb = {version = "^0.3.3", optional = true}
|
||||
azure-search-documents = {version = "11.4.0a20230509004", source = "azure-sdk-dev", optional = true}
|
||||
openllm = {version = ">=0.1.6", optional = true}
|
||||
@@ -333,7 +333,8 @@ extended_testing = [
|
||||
"scikit-learn",
|
||||
"streamlit",
|
||||
"pyspark",
|
||||
"openai"
|
||||
"openai",
|
||||
"langsmith"
|
||||
]
|
||||
|
||||
[[tool.poetry.source]]
|
||||
|
||||
@@ -583,7 +583,7 @@ def test_convert_run(
|
||||
child_execution_order=1,
|
||||
start_time=datetime.utcnow(),
|
||||
end_time=datetime.utcnow(),
|
||||
session_id=TEST_SESSION_ID,
|
||||
session_id=uuid4(),
|
||||
inputs={"prompts": []},
|
||||
outputs=LLMResult(generations=[[]]).dict(),
|
||||
serialized={},
|
||||
|
||||
@@ -5,8 +5,6 @@ from typing import Any, Dict, List, Optional, Union
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from langchainplus_sdk.client import LangChainPlusClient
|
||||
from langchainplus_sdk.schemas import Dataset, Example
|
||||
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
from langchain.chains.base import Chain
|
||||
@@ -104,8 +102,12 @@ def test_run_chat_model_all_formats(inputs: Dict[str, Any]) -> None:
|
||||
run_llm(llm, inputs, mock.MagicMock())
|
||||
|
||||
|
||||
@pytest.mark.requires("langsmith")
|
||||
@pytest.mark.asyncio
|
||||
async def test_arun_on_dataset(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from langsmith import Client as LangSmithClient
|
||||
from langsmith.schemas import Dataset, Example
|
||||
|
||||
dataset = Dataset(
|
||||
id=uuid.uuid4(),
|
||||
name="test",
|
||||
@@ -180,15 +182,15 @@ async def test_arun_on_dataset(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
pass
|
||||
|
||||
with mock.patch.object(
|
||||
LangChainPlusClient, "read_dataset", new=mock_read_dataset
|
||||
LangSmithClient, "read_dataset", new=mock_read_dataset
|
||||
), mock.patch.object(
|
||||
LangChainPlusClient, "list_examples", new=mock_list_examples
|
||||
LangSmithClient, "list_examples", new=mock_list_examples
|
||||
), mock.patch(
|
||||
"langchain.client.runner_utils._arun_llm_or_chain", new=mock_arun_chain
|
||||
), mock.patch.object(
|
||||
LangChainPlusClient, "create_project", new=mock_create_project
|
||||
LangSmithClient, "create_project", new=mock_create_project
|
||||
):
|
||||
client = LangChainPlusClient(api_url="http://localhost:1984", api_key="123")
|
||||
client = LangSmithClient(api_url="http://localhost:1984", api_key="123")
|
||||
chain = mock.MagicMock()
|
||||
num_repetitions = 3
|
||||
results = await arun_on_dataset(
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
"""Test run evaluator implementations basic functionality."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from langchainplus_sdk.schemas import Example, Run
|
||||
|
||||
from langchain.evaluation.run_evaluators import get_criteria_evaluator, get_qa_evaluator
|
||||
from tests.unit_tests.llms.fake_llm import FakeLLM
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langsmith.schemas import Example, Run
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def run() -> Run:
|
||||
from langsmith.schemas import Run
|
||||
|
||||
return Run(
|
||||
id=UUID("f77cd087-48f7-4c62-9e0e-297842202107"),
|
||||
name="My Run",
|
||||
@@ -25,6 +31,8 @@ def run() -> Run:
|
||||
|
||||
@pytest.fixture
|
||||
def example() -> Example:
|
||||
from langsmith.schemas import Example
|
||||
|
||||
return Example(
|
||||
id=UUID("f77cd087-48f7-4c62-9e0e-297842202106"),
|
||||
dataset_id=UUID("f77cd087-48f7-4c62-9e0e-297842202105"),
|
||||
@@ -34,6 +42,7 @@ def example() -> Example:
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.requires("langsmith")
|
||||
def test_get_qa_evaluator(run: Run, example: Example) -> None:
|
||||
"""Test get_qa_evaluator."""
|
||||
eval_llm = FakeLLM(
|
||||
@@ -45,6 +54,7 @@ def test_get_qa_evaluator(run: Run, example: Example) -> None:
|
||||
assert res.score == 1
|
||||
|
||||
|
||||
@pytest.mark.requires("langsmith")
|
||||
def test_get_criteria_evaluator(run: Run, example: Example) -> None:
|
||||
"""Get a criteria evaluator."""
|
||||
eval_llm = FakeLLM(queries={"a": "This checks out.\nY"}, sequential_responses=True)
|
||||
|
||||
@@ -38,7 +38,6 @@ def test_required_dependencies(poetry_conf: Mapping[str, Any]) -> None:
|
||||
"aiohttp",
|
||||
"async-timeout",
|
||||
"dataclasses-json",
|
||||
"langchainplus-sdk",
|
||||
"numexpr",
|
||||
"numpy",
|
||||
"openapi-schema-pydantic",
|
||||
|
||||
Reference in New Issue
Block a user