155 lines
5.4 KiB
Python
155 lines
5.4 KiB
Python
|
|
"""Interface with the [LangChain Hub](https://smith.langchain.com/hub)."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
from collections.abc import Sequence
|
||
|
|
from typing import Any, Literal
|
||
|
|
|
||
|
|
from langchain_core.load.dump import dumps
|
||
|
|
from langchain_core.load.load import loads
|
||
|
|
from langchain_core.prompts import BasePromptTemplate
|
||
|
|
|
||
|
|
|
||
|
|
def _get_client(
|
||
|
|
api_key: str | None = None,
|
||
|
|
api_url: str | None = None,
|
||
|
|
) -> Any:
|
||
|
|
"""Get a client for interacting with the LangChain Hub.
|
||
|
|
|
||
|
|
Attempts to use LangSmith client if available, otherwise falls back to
|
||
|
|
the legacy `langchainhub` client.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
api_key: API key to authenticate with the LangChain Hub API.
|
||
|
|
api_url: URL of the LangChain Hub API.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Client instance for interacting with the hub.
|
||
|
|
|
||
|
|
Raises:
|
||
|
|
ImportError: If neither `langsmith` nor `langchainhub` can be imported.
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
from langsmith import Client as LangSmithClient
|
||
|
|
|
||
|
|
ls_client = LangSmithClient(api_url, api_key=api_key)
|
||
|
|
if hasattr(ls_client, "push_prompt") and hasattr(ls_client, "pull_prompt"):
|
||
|
|
return ls_client
|
||
|
|
from langchainhub import Client as LangChainHubClient
|
||
|
|
|
||
|
|
return LangChainHubClient(api_url, api_key=api_key)
|
||
|
|
except ImportError:
|
||
|
|
try:
|
||
|
|
from langchainhub import Client as LangChainHubClient
|
||
|
|
|
||
|
|
return LangChainHubClient(api_url, api_key=api_key)
|
||
|
|
except ImportError as e:
|
||
|
|
msg = (
|
||
|
|
"Could not import langsmith or langchainhub (deprecated),"
|
||
|
|
"please install with `pip install langsmith`."
|
||
|
|
)
|
||
|
|
raise ImportError(msg) from e
|
||
|
|
|
||
|
|
|
||
|
|
def push(
|
||
|
|
repo_full_name: str,
|
||
|
|
object: Any, # noqa: A002
|
||
|
|
*,
|
||
|
|
api_url: str | None = None,
|
||
|
|
api_key: str | None = None,
|
||
|
|
parent_commit_hash: str | None = None,
|
||
|
|
new_repo_is_public: bool = False,
|
||
|
|
new_repo_description: str | None = None,
|
||
|
|
readme: str | None = None,
|
||
|
|
tags: Sequence[str] | None = None,
|
||
|
|
) -> str:
|
||
|
|
"""Push an object to the hub and returns the URL it can be viewed at in a browser.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
repo_full_name: The full name of the prompt to push to in the format of
|
||
|
|
`owner/prompt_name` or `prompt_name`.
|
||
|
|
object: The LangChain object to serialize and push to the hub.
|
||
|
|
api_url: The URL of the LangChain Hub API. Defaults to the hosted API service
|
||
|
|
if you have an API key set, or a localhost instance if not.
|
||
|
|
api_key: The API key to use to authenticate with the LangChain Hub API.
|
||
|
|
parent_commit_hash: The commit hash of the parent commit to push to. Defaults
|
||
|
|
to the latest commit automatically.
|
||
|
|
new_repo_is_public: Whether the prompt should be public.
|
||
|
|
new_repo_description: The description of the prompt.
|
||
|
|
readme: README content for the repository.
|
||
|
|
tags: Tags to associate with the prompt.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
URL where the pushed object can be viewed in a browser.
|
||
|
|
"""
|
||
|
|
client = _get_client(api_key=api_key, api_url=api_url)
|
||
|
|
|
||
|
|
# Then it's langsmith
|
||
|
|
if hasattr(client, "push_prompt"):
|
||
|
|
return client.push_prompt(
|
||
|
|
repo_full_name,
|
||
|
|
object=object,
|
||
|
|
parent_commit_hash=parent_commit_hash,
|
||
|
|
is_public=new_repo_is_public,
|
||
|
|
description=new_repo_description,
|
||
|
|
readme=readme,
|
||
|
|
tags=tags,
|
||
|
|
)
|
||
|
|
|
||
|
|
# Then it's langchainhub
|
||
|
|
manifest_json = dumps(object)
|
||
|
|
return client.push(
|
||
|
|
repo_full_name,
|
||
|
|
manifest_json,
|
||
|
|
parent_commit_hash=parent_commit_hash,
|
||
|
|
new_repo_is_public=new_repo_is_public,
|
||
|
|
new_repo_description=new_repo_description,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def pull(
|
||
|
|
owner_repo_commit: str,
|
||
|
|
*,
|
||
|
|
include_model: bool | None = None,
|
||
|
|
api_url: str | None = None,
|
||
|
|
api_key: str | None = None,
|
||
|
|
) -> Any:
|
||
|
|
"""Pull an object from the hub and returns it as a LangChain object.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
owner_repo_commit: The full name of the prompt to pull from in the format of
|
||
|
|
`owner/prompt_name:commit_hash` or `owner/prompt_name`
|
||
|
|
or just `prompt_name` if it's your own prompt.
|
||
|
|
include_model: Whether to include the model configuration in the pulled prompt.
|
||
|
|
api_url: The URL of the LangChain Hub API. Defaults to the hosted API service
|
||
|
|
if you have an API key set, or a localhost instance if not.
|
||
|
|
api_key: The API key to use to authenticate with the LangChain Hub API.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
The pulled LangChain object.
|
||
|
|
"""
|
||
|
|
client = _get_client(api_key=api_key, api_url=api_url)
|
||
|
|
|
||
|
|
# Then it's langsmith
|
||
|
|
if hasattr(client, "pull_prompt"):
|
||
|
|
return client.pull_prompt(owner_repo_commit, include_model=include_model)
|
||
|
|
|
||
|
|
# Then it's langchainhub
|
||
|
|
if hasattr(client, "pull_repo"):
|
||
|
|
# >= 0.1.15
|
||
|
|
res_dict = client.pull_repo(owner_repo_commit)
|
||
|
|
allowed_objects: Literal["all", "core"] = "all" if include_model else "core"
|
||
|
|
obj = loads(json.dumps(res_dict["manifest"]), allowed_objects=allowed_objects)
|
||
|
|
if isinstance(obj, BasePromptTemplate):
|
||
|
|
if obj.metadata is None:
|
||
|
|
obj.metadata = {}
|
||
|
|
obj.metadata["lc_hub_owner"] = res_dict["owner"]
|
||
|
|
obj.metadata["lc_hub_repo"] = res_dict["repo"]
|
||
|
|
obj.metadata["lc_hub_commit_hash"] = res_dict["commit_hash"]
|
||
|
|
return obj
|
||
|
|
|
||
|
|
# Then it's < 0.1.15 langchainhub
|
||
|
|
resp: str = client.pull(owner_repo_commit)
|
||
|
|
return loads(resp)
|