Use LCP Client in Tracer (#5908)

Move the LCP calls to the client.
This commit is contained in:
Zander Chase 2023-06-08 21:15:14 -07:00 committed by GitHub
parent 3ec6400d70
commit 77c286cf02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 109 deletions

View File

@ -8,61 +8,16 @@ from datetime import datetime
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from uuid import UUID from uuid import UUID
import requests from langchainplus_sdk import LangChainPlusClient
from requests.exceptions import HTTPError
from tenacity import (
before_sleep_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_exponential,
)
from langchain.callbacks.tracers.base import BaseTracer from langchain.callbacks.tracers.base import BaseTracer
from langchain.callbacks.tracers.schemas import ( from langchain.callbacks.tracers.schemas import Run, RunTypeEnum, TracerSession
Run, from langchain.env import get_runtime_environment
RunCreate,
RunTypeEnum,
RunUpdate,
TracerSession,
)
from langchain.schema import BaseMessage, messages_to_dict from langchain.schema import BaseMessage, messages_to_dict
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_headers() -> Dict[str, Any]:
"""Get the headers for the LangChain API."""
headers: Dict[str, Any] = {"Content-Type": "application/json"}
if os.getenv("LANGCHAIN_API_KEY"):
headers["x-api-key"] = os.getenv("LANGCHAIN_API_KEY")
return headers
def get_endpoint() -> str:
return os.getenv("LANGCHAIN_ENDPOINT", "http://localhost:1984")
class LangChainTracerAPIError(Exception):
"""An error occurred while communicating with the LangChain API."""
class LangChainTracerUserError(Exception):
"""An error occurred while communicating with the LangChain API."""
class LangChainTracerError(Exception):
"""An error occurred while communicating with the LangChain API."""
retry_decorator = retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10),
retry=retry_if_exception_type(LangChainTracerAPIError),
before_sleep=before_sleep_log(logger, logging.WARNING),
)
class LangChainTracer(BaseTracer): class LangChainTracer(BaseTracer):
"""An implementation of the SharedTracer that POSTS to the langchain endpoint.""" """An implementation of the SharedTracer that POSTS to the langchain endpoint."""
@ -70,19 +25,19 @@ class LangChainTracer(BaseTracer):
self, self,
example_id: Optional[Union[UUID, str]] = None, example_id: Optional[Union[UUID, str]] = None,
session_name: Optional[str] = None, session_name: Optional[str] = None,
client: Optional[LangChainPlusClient] = None,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Initialize the LangChain tracer.""" """Initialize the LangChain tracer."""
super().__init__(**kwargs) super().__init__(**kwargs)
self.session: Optional[TracerSession] = None self.session: Optional[TracerSession] = None
self._endpoint = get_endpoint()
self._headers = get_headers()
self.example_id = ( self.example_id = (
UUID(example_id) if isinstance(example_id, str) else example_id UUID(example_id) if isinstance(example_id, str) else example_id
) )
self.session_name = session_name or os.getenv("LANGCHAIN_SESSION", "default") self.session_name = session_name or os.getenv("LANGCHAIN_SESSION", "default")
# set max_workers to 1 to process tasks in order # set max_workers to 1 to process tasks in order
self.executor = ThreadPoolExecutor(max_workers=1) self.executor = ThreadPoolExecutor(max_workers=1)
self.client = client or LangChainPlusClient()
def on_chat_model_start( def on_chat_model_start(
self, self,
@ -114,60 +69,19 @@ class LangChainTracer(BaseTracer):
def _persist_run(self, run: Run) -> None: def _persist_run(self, run: Run) -> None:
"""The Langchain Tracer uses Post/Patch rather than persist.""" """The Langchain Tracer uses Post/Patch rather than persist."""
@retry_decorator
def _persist_run_single(self, run: Run) -> None: def _persist_run_single(self, run: Run) -> None:
"""Persist a run.""" """Persist a run."""
if run.parent_run_id is None: if run.parent_run_id is None:
run.reference_example_id = self.example_id run.reference_example_id = self.example_id
run_dict = run.dict() run_dict = run.dict(exclude={"child_runs"})
del run_dict["child_runs"] extra = run_dict.get("extra", {})
run_create = RunCreate(**run_dict, session_name=self.session_name) extra["runtime"] = get_runtime_environment()
response = None run_dict["extra"] = extra
try: run = self.client.create_run(**run_dict, session_name=self.session_name)
# TODO: Add retries when async
response = requests.post(
f"{self._endpoint}/runs",
data=run_create.json(),
headers=self._headers,
)
response.raise_for_status()
except HTTPError as e:
if response is not None and response.status_code == 500:
raise LangChainTracerAPIError(
f"Failed to upsert persist run to LangChain API. {e}"
)
else:
raise LangChainTracerUserError(
f"Failed to persist run to LangChain API. {e}"
)
except Exception as e:
raise LangChainTracerError(
f"Failed to persist run to LangChain API. {e}"
) from e
@retry_decorator
def _update_run_single(self, run: Run) -> None: def _update_run_single(self, run: Run) -> None:
"""Update a run.""" """Update a run."""
run_update = RunUpdate(**run.dict()) self.client.update_run(run.id, **run.dict())
response = None
try:
response = requests.patch(
f"{self._endpoint}/runs/{run.id}",
data=run_update.json(),
headers=self._headers,
)
response.raise_for_status()
except HTTPError as e:
if response is not None and response.status_code == 500:
raise LangChainTracerAPIError(
f"Failed to update run to LangChain API. {e}"
)
else:
raise LangChainTracerUserError(f"Failed to run to LangChain API. {e}")
except Exception as e:
raise LangChainTracerError(
f"Failed to update run to LangChain API. {e}"
) from e
def _on_llm_start(self, run: Run) -> None: def _on_llm_start(self, run: Run) -> None:
"""Persist an LLM run.""" """Persist an LLM run."""

View File

@ -2,12 +2,11 @@ from __future__ import annotations
import logging import logging
import os import os
from typing import Any, Optional, Union from typing import Any, Dict, Optional, Union
import requests import requests
from langchain.callbacks.tracers.base import BaseTracer from langchain.callbacks.tracers.base import BaseTracer
from langchain.callbacks.tracers.langchain import get_headers
from langchain.callbacks.tracers.schemas import ( from langchain.callbacks.tracers.schemas import (
ChainRun, ChainRun,
LLMRun, LLMRun,
@ -21,6 +20,14 @@ from langchain.schema import get_buffer_string
from langchain.utils import raise_for_status_with_text from langchain.utils import raise_for_status_with_text
def get_headers() -> Dict[str, Any]:
"""Get the headers for the LangChain API."""
headers: Dict[str, Any] = {"Content-Type": "application/json"}
if os.getenv("LANGCHAIN_API_KEY"):
headers["x-api-key"] = os.getenv("LANGCHAIN_API_KEY")
return headers
def _get_endpoint() -> str: def _get_endpoint() -> str:
return os.getenv("LANGCHAIN_ENDPOINT", "http://localhost:8000") return os.getenv("LANGCHAIN_ENDPOINT", "http://localhost:8000")

View File

@ -10,6 +10,7 @@ def get_runtime_environment() -> dict:
return { return {
"library_version": __version__, "library_version": __version__,
"library": "langchain",
"platform": platform.platform(), "platform": platform.platform(),
"runtime": "python", "runtime": "python",
"runtime_version": platform.python_version(), "runtime_version": platform.python_version(),

18
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry and should not be changed by hand. # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]] [[package]]
name = "absl-py" name = "absl-py"
@ -4005,14 +4005,14 @@ tests = ["pytest", "pytest-mock"]
[[package]] [[package]]
name = "langchainplus-sdk" name = "langchainplus-sdk"
version = "0.0.6" version = "0.0.7"
description = "Client library to connect to the LangChainPlus LLM Tracing and Evaluation Platform." description = "Client library to connect to the LangChainPlus LLM Tracing and Evaluation Platform."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.8.1,<4.0" python-versions = ">=3.8.1,<4.0"
files = [ files = [
{file = "langchainplus_sdk-0.0.6-py3-none-any.whl", hash = "sha256:43fe01c66442b88403c969b8812f6be81e023c0d2a6d5d3572a8d87961438658"}, {file = "langchainplus_sdk-0.0.7-py3-none-any.whl", hash = "sha256:aefc471058648bf9fc51f659117d33ef905d25a304d5a021f7e32c30f5921076"},
{file = "langchainplus_sdk-0.0.6.tar.gz", hash = "sha256:c911a98fd2d02baa48f742b7d700fd6a55f11c9a545ee5d66b08825940c9a32e"}, {file = "langchainplus_sdk-0.0.7.tar.gz", hash = "sha256:b58565bdcaf301d2e6e7dd8898f0b8ccf549a35476258e0c14d871d6de02d210"},
] ]
[package.dependencies] [package.dependencies]
@ -11340,13 +11340,13 @@ cffi = {version = ">=1.11", markers = "platform_python_implementation == \"PyPy\
cffi = ["cffi (>=1.11)"] cffi = ["cffi (>=1.11)"]
[extras] [extras]
all = ["anthropic", "cohere", "openai", "nlpcloud", "huggingface_hub", "jina", "manifest-ml", "elasticsearch", "opensearch-py", "google-search-results", "faiss-cpu", "sentence-transformers", "transformers", "spacy", "nltk", "wikipedia", "beautifulsoup4", "tiktoken", "torch", "jinja2", "pinecone-client", "pinecone-text", "pymongo", "weaviate-client", "redis", "google-api-python-client", "google-auth", "wolframalpha", "qdrant-client", "tensorflow-text", "pypdf", "networkx", "nomic", "aleph-alpha-client", "deeplake", "pgvector", "psycopg2-binary", "pyowm", "pytesseract", "html2text", "atlassian-python-api", "gptcache", "duckduckgo-search", "arxiv", "azure-identity", "clickhouse-connect", "azure-cosmos", "lancedb", "langkit", "lark", "pexpect", "pyvespa", "O365", "jq", "docarray", "steamship", "pdfminer-six", "lxml", "requests-toolbelt", "neo4j", "openlm", "azure-ai-formrecognizer", "azure-ai-vision", "azure-cognitiveservices-speech", "momento", "singlestoredb", "tigrisdb", "nebula3-python"] all = ["O365", "aleph-alpha-client", "anthropic", "arxiv", "atlassian-python-api", "azure-ai-formrecognizer", "azure-ai-vision", "azure-cognitiveservices-speech", "azure-cosmos", "azure-identity", "beautifulsoup4", "clickhouse-connect", "cohere", "deeplake", "docarray", "duckduckgo-search", "elasticsearch", "faiss-cpu", "google-api-python-client", "google-auth", "google-search-results", "gptcache", "html2text", "huggingface_hub", "jina", "jinja2", "jq", "lancedb", "langkit", "lark", "lxml", "manifest-ml", "momento", "nebula3-python", "neo4j", "networkx", "nlpcloud", "nltk", "nomic", "openai", "openlm", "opensearch-py", "pdfminer-six", "pexpect", "pgvector", "pinecone-client", "pinecone-text", "psycopg2-binary", "pymongo", "pyowm", "pypdf", "pytesseract", "pyvespa", "qdrant-client", "redis", "requests-toolbelt", "sentence-transformers", "singlestoredb", "spacy", "steamship", "tensorflow-text", "tigrisdb", "tiktoken", "torch", "transformers", "weaviate-client", "wikipedia", "wolframalpha"]
azure = ["azure-identity", "azure-cosmos", "openai", "azure-core", "azure-ai-formrecognizer", "azure-ai-vision", "azure-cognitiveservices-speech"] azure = ["azure-ai-formrecognizer", "azure-ai-vision", "azure-cognitiveservices-speech", "azure-core", "azure-cosmos", "azure-identity", "openai"]
cohere = ["cohere"] cohere = ["cohere"]
docarray = ["docarray"] docarray = ["docarray"]
embeddings = ["sentence-transformers"] embeddings = ["sentence-transformers"]
extended-testing = ["beautifulsoup4", "bibtexparser", "chardet", "jq", "pdfminer-six", "pypdf", "pymupdf", "pypdfium2", "tqdm", "lxml", "atlassian-python-api", "beautifulsoup4", "pandas", "telethon", "psychicapi", "zep-python", "gql", "requests-toolbelt", "html2text", "py-trello", "scikit-learn", "pyspark"] extended-testing = ["atlassian-python-api", "beautifulsoup4", "beautifulsoup4", "bibtexparser", "chardet", "gql", "html2text", "jq", "lxml", "pandas", "pdfminer-six", "psychicapi", "py-trello", "pymupdf", "pypdf", "pypdfium2", "pyspark", "requests-toolbelt", "scikit-learn", "telethon", "tqdm", "zep-python"]
llms = ["anthropic", "cohere", "openai", "openlm", "nlpcloud", "huggingface_hub", "manifest-ml", "torch", "transformers"] llms = ["anthropic", "cohere", "huggingface_hub", "manifest-ml", "nlpcloud", "openai", "openlm", "torch", "transformers"]
openai = ["openai", "tiktoken"] openai = ["openai", "tiktoken"]
qdrant = ["qdrant-client"] qdrant = ["qdrant-client"]
text-helpers = ["chardet"] text-helpers = ["chardet"]
@ -11354,4 +11354,4 @@ text-helpers = ["chardet"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.8.1,<4.0" python-versions = ">=3.8.1,<4.0"
content-hash = "0da3585d7f3216764f396c162c8f9456423b9f80a6dc9af46040c3e5eec0b79e" content-hash = "dbbaa2907bf2ac09ed111ce712772bba0fe56901627f41c53aef71ae5a38d1c6"

View File

@ -105,7 +105,7 @@ singlestoredb = {version = "^0.6.1", optional = true}
pyspark = {version = "^3.4.0", optional = true} pyspark = {version = "^3.4.0", optional = true}
tigrisdb = {version = "^1.0.0b6", optional = true} tigrisdb = {version = "^1.0.0b6", optional = true}
nebula3-python = {version = "^3.4.0", optional = true} nebula3-python = {version = "^3.4.0", optional = true}
langchainplus-sdk = ">=0.0.6" langchainplus-sdk = ">=0.0.7"
[tool.poetry.group.docs.dependencies] [tool.poetry.group.docs.dependencies]