mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-11 19:49:54 +00:00
Compare commits
46 Commits
langchain-
...
langchain-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f76246cf2 | ||
|
|
4bdf1d7d1a | ||
|
|
60e572f591 | ||
|
|
35e6052df5 | ||
|
|
161ab736ce | ||
|
|
8738973267 | ||
|
|
2acc83f146 | ||
|
|
1a66175e38 | ||
|
|
dee72c46c1 | ||
|
|
2901fa20cc | ||
|
|
567dc1e422 | ||
|
|
abaea28417 | ||
|
|
cb32bab69d | ||
|
|
7c7ee07d30 | ||
|
|
923ef85105 | ||
|
|
218b4e073e | ||
|
|
4da35623af | ||
|
|
43e24cd4a1 | ||
|
|
4027da1b6e | ||
|
|
16918842bf | ||
|
|
6bda89f9a1 | ||
|
|
197b885911 | ||
|
|
5599a0a537 | ||
|
|
97f752c92d | ||
|
|
0a06732d3e | ||
|
|
0dbaf05bb7 | ||
|
|
24eea2e398 | ||
|
|
d9d689572a | ||
|
|
cbeb8601d6 | ||
|
|
018f4102f4 | ||
|
|
6dfea7e508 | ||
|
|
3a63055ce2 | ||
|
|
a1db744b20 | ||
|
|
c26b3575f8 | ||
|
|
093f24ba4d | ||
|
|
0c051e57e0 | ||
|
|
22a8652ecc | ||
|
|
76e210a349 | ||
|
|
0a57fc0016 | ||
|
|
e6fe8cc2fb | ||
|
|
0b2bea4c0e | ||
|
|
3b602d0453 | ||
|
|
2de59d0b3e | ||
|
|
709c418022 | ||
|
|
683644320b | ||
|
|
c48fdbba6a |
36
.github/scripts/prep_api_docs_build.py
vendored
36
.github/scripts/prep_api_docs_build.py
vendored
@@ -11,7 +11,9 @@ from typing import Dict, Any
|
||||
def load_packages_yaml() -> Dict[str, Any]:
|
||||
"""Load and parse the packages.yml file."""
|
||||
with open("langchain/libs/packages.yml", "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
all_packages = yaml.safe_load(f)
|
||||
|
||||
return {k: v for k, v in all_packages.items() if k["repo"]}
|
||||
|
||||
|
||||
def get_target_dir(package_name: str) -> Path:
|
||||
@@ -23,24 +25,19 @@ def get_target_dir(package_name: str) -> Path:
|
||||
return base_path / "partners" / package_name_short
|
||||
|
||||
|
||||
def clean_target_directories(packages: Dict[str, Any]) -> None:
|
||||
def clean_target_directories(packages: list) -> None:
|
||||
"""Remove old directories that will be replaced."""
|
||||
for package in packages["packages"]:
|
||||
if package["repo"] != "langchain-ai/langchain":
|
||||
target_dir = get_target_dir(package["name"])
|
||||
if target_dir.exists():
|
||||
print(f"Removing {target_dir}")
|
||||
shutil.rmtree(target_dir)
|
||||
for package in packages:
|
||||
|
||||
target_dir = get_target_dir(package["name"])
|
||||
if target_dir.exists():
|
||||
print(f"Removing {target_dir}")
|
||||
shutil.rmtree(target_dir)
|
||||
|
||||
|
||||
def move_libraries(packages: Dict[str, Any]) -> None:
|
||||
def move_libraries(packages: list) -> None:
|
||||
"""Move libraries from their source locations to the target directories."""
|
||||
for package in packages["packages"]:
|
||||
# Skip if it's the main langchain repo or disabled
|
||||
if package["repo"] == "langchain-ai/langchain" or package.get(
|
||||
"disabled", False
|
||||
):
|
||||
continue
|
||||
for package in packages:
|
||||
|
||||
repo_name = package["repo"].split("/")[1]
|
||||
source_path = package["path"]
|
||||
@@ -68,7 +65,14 @@ def main():
|
||||
"""Main function to orchestrate the library sync process."""
|
||||
try:
|
||||
# Load packages configuration
|
||||
packages = load_packages_yaml()
|
||||
package_yaml = load_packages_yaml()
|
||||
packages = [
|
||||
p
|
||||
for p in package_yaml["packages"]
|
||||
if not p.get("disabled", False)
|
||||
and p["repo"].startswith("langchain-ai/")
|
||||
and p["repo"] != "langchain-ai/langchain"
|
||||
]
|
||||
|
||||
# Clean target directories
|
||||
clean_target_directories(packages)
|
||||
|
||||
6
.github/workflows/_release.yml
vendored
6
.github/workflows/_release.yml
vendored
@@ -219,11 +219,7 @@ jobs:
|
||||
|
||||
# Replace all dashes in the package name with underscores,
|
||||
# since that's how Python imports packages with dashes in the name.
|
||||
if [ "$PKG_NAME" == "langchain-tests" ]; then
|
||||
IMPORT_NAME="langchain_standard_tests"
|
||||
else
|
||||
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
|
||||
fi
|
||||
IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)"
|
||||
|
||||
poetry run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))"
|
||||
|
||||
|
||||
62
.github/workflows/_release_docker.yml
vendored
62
.github/workflows/_release_docker.yml
vendored
@@ -1,62 +0,0 @@
|
||||
name: release_docker
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dockerfile:
|
||||
required: true
|
||||
type: string
|
||||
description: "Path to the Dockerfile to build"
|
||||
image:
|
||||
required: true
|
||||
type: string
|
||||
description: "Name of the image to build"
|
||||
|
||||
env:
|
||||
TEST_TAG: ${{ inputs.image }}:test
|
||||
LATEST_TAG: ${{ inputs.image }}:latest
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Get git tag
|
||||
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||
id: get-latest-tag
|
||||
- name: Set docker tag
|
||||
env:
|
||||
VERSION: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
run: |
|
||||
echo "VERSION_TAG=${{ inputs.image }}:${VERSION#v}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build for Test
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
load: true
|
||||
tags: ${{ env.TEST_TAG }}
|
||||
- name: Test
|
||||
run: |
|
||||
docker run --rm ${{ env.TEST_TAG }} python -c "import langchain"
|
||||
- name: Build and Push to Docker Hub
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
# We can only build for the intersection of platforms supported by
|
||||
# QEMU and base python image, for now build only for
|
||||
# linux/amd64 and linux/arm64
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ env.LATEST_TAG }},${{ env.VERSION_TAG }}
|
||||
push: true
|
||||
4
.github/workflows/api_doc_build.yml
vendored
4
.github/workflows/api_doc_build.yml
vendored
@@ -37,9 +37,9 @@ jobs:
|
||||
# Get unique repositories
|
||||
REPOS=$(echo "$REPOS_UNSORTED" | sort -u)
|
||||
|
||||
# Checkout each unique repository
|
||||
# Checkout each unique repository that is in langchain-ai org
|
||||
for repo in $REPOS; do
|
||||
if [ "$repo" != "langchain-ai/langchain" ]; then
|
||||
if [[ "$repo" != "langchain-ai/langchain" && "$repo" == langchain-ai/* ]]; then
|
||||
REPO_NAME=$(echo $repo | cut -d'/' -f2)
|
||||
echo "Checking out $repo to $REPO_NAME"
|
||||
git clone --depth 1 https://github.com/$repo.git $REPO_NAME
|
||||
|
||||
14
.github/workflows/langchain_release_docker.yml
vendored
14
.github/workflows/langchain_release_docker.yml
vendored
@@ -1,14 +0,0 @@
|
||||
---
|
||||
name: docker/langchain/langchain Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows to trigger the workflow manually in GitHub UI
|
||||
workflow_call: # Allows triggering from another workflow
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: ./.github/workflows/_release_docker.yml
|
||||
with:
|
||||
dockerfile: docker/Dockerfile.base
|
||||
image: langchain/langchain
|
||||
secrets: inherit
|
||||
@@ -129,7 +129,7 @@ Please see [here](https://python.langchain.com) for full documentation, which in
|
||||
|
||||
- [🦜🛠️ LangSmith](https://docs.smith.langchain.com/): Trace and evaluate your language model applications and intelligent agents to help you move from prototype to production.
|
||||
- [🦜🕸️ LangGraph](https://langchain-ai.github.io/langgraph/): Create stateful, multi-actor applications with LLMs. Integrates smoothly with LangChain, but can be used without it.
|
||||
- [🦜🏓 LangServe](https://python.langchain.com/docs/langserve): Deploy LangChain runnables and chains as REST APIs.
|
||||
- [🦜🕸️ LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform): Deploy LLM applications built with LangGraph into production.
|
||||
|
||||
## 💁 Contributing
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM python:3.11
|
||||
|
||||
RUN pip install langchain
|
||||
@@ -1,12 +0,0 @@
|
||||
# Makefile
|
||||
|
||||
build_graphdb:
|
||||
docker build --tag graphdb ./graphdb
|
||||
|
||||
start_graphdb:
|
||||
docker-compose up -d graphdb
|
||||
|
||||
down:
|
||||
docker-compose down -v --remove-orphans
|
||||
|
||||
.PHONY: build_graphdb start_graphdb down
|
||||
@@ -1,84 +0,0 @@
|
||||
# docker-compose to make it easier to spin up integration tests.
|
||||
# Services should use NON standard ports to avoid collision with
|
||||
# any existing services that might be used for development.
|
||||
# ATTENTION: When adding a service below use a non-standard port
|
||||
# increment by one from the preceding port.
|
||||
# For credentials always use `langchain` and `langchain` for the
|
||||
# username and password.
|
||||
version: "3"
|
||||
name: langchain-tests
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis/redis-stack-server:latest
|
||||
# We use non standard ports since
|
||||
# these instances are used for testing
|
||||
# and users may already have existing
|
||||
# redis instances set up locally
|
||||
# for other projects
|
||||
ports:
|
||||
- "6020:6379"
|
||||
volumes:
|
||||
- ./redis-volume:/data
|
||||
graphdb:
|
||||
image: graphdb
|
||||
ports:
|
||||
- "6021:7200"
|
||||
mongo:
|
||||
image: mongo:latest
|
||||
container_name: mongo_container
|
||||
ports:
|
||||
- "6022:27017"
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: langchain
|
||||
MONGO_INITDB_ROOT_PASSWORD: langchain
|
||||
postgres:
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_DB: langchain
|
||||
POSTGRES_USER: langchain
|
||||
POSTGRES_PASSWORD: langchain
|
||||
ports:
|
||||
- "6023:5432"
|
||||
command: |
|
||||
postgres -c log_statement=all
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"psql postgresql://langchain:langchain@localhost/langchain --command 'SELECT 1;' || exit 1",
|
||||
]
|
||||
interval: 5s
|
||||
retries: 60
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
pgvector:
|
||||
# postgres with the pgvector extension
|
||||
image: ankane/pgvector
|
||||
environment:
|
||||
POSTGRES_DB: langchain
|
||||
POSTGRES_USER: langchain
|
||||
POSTGRES_PASSWORD: langchain
|
||||
ports:
|
||||
- "6024:5432"
|
||||
command: |
|
||||
postgres -c log_statement=all
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"psql postgresql://langchain:langchain@localhost/langchain --command 'SELECT 1;' || exit 1",
|
||||
]
|
||||
interval: 5s
|
||||
retries: 60
|
||||
volumes:
|
||||
- postgres_data_pgvector:/var/lib/postgresql/data
|
||||
vdms:
|
||||
image: intellabs/vdms:latest
|
||||
container_name: vdms_container
|
||||
ports:
|
||||
- "6025:55555"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
postgres_data_pgvector:
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM ontotext/graphdb:10.5.1
|
||||
RUN mkdir -p /opt/graphdb/dist/data/repositories/langchain
|
||||
COPY config.ttl /opt/graphdb/dist/data/repositories/langchain/
|
||||
COPY graphdb_create.sh /run.sh
|
||||
ENTRYPOINT bash /run.sh
|
||||
@@ -1,46 +0,0 @@
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
||||
@prefix rep: <http://www.openrdf.org/config/repository#>.
|
||||
@prefix sr: <http://www.openrdf.org/config/repository/sail#>.
|
||||
@prefix sail: <http://www.openrdf.org/config/sail#>.
|
||||
@prefix graphdb: <http://www.ontotext.com/config/graphdb#>.
|
||||
|
||||
[] a rep:Repository ;
|
||||
rep:repositoryID "langchain" ;
|
||||
rdfs:label "" ;
|
||||
rep:repositoryImpl [
|
||||
rep:repositoryType "graphdb:SailRepository" ;
|
||||
sr:sailImpl [
|
||||
sail:sailType "graphdb:Sail" ;
|
||||
|
||||
graphdb:read-only "false" ;
|
||||
|
||||
# Inference and Validation
|
||||
graphdb:ruleset "empty" ;
|
||||
graphdb:disable-sameAs "true" ;
|
||||
graphdb:check-for-inconsistencies "false" ;
|
||||
|
||||
# Indexing
|
||||
graphdb:entity-id-size "32" ;
|
||||
graphdb:enable-context-index "false" ;
|
||||
graphdb:enablePredicateList "true" ;
|
||||
graphdb:enable-fts-index "false" ;
|
||||
graphdb:fts-indexes ("default" "iri") ;
|
||||
graphdb:fts-string-literals-index "default" ;
|
||||
graphdb:fts-iris-index "none" ;
|
||||
|
||||
# Queries and Updates
|
||||
graphdb:query-timeout "0" ;
|
||||
graphdb:throw-QueryEvaluationException-on-timeout "false" ;
|
||||
graphdb:query-limit-results "0" ;
|
||||
|
||||
# Settable in the file but otherwise hidden in the UI and in the RDF4J console
|
||||
graphdb:base-URL "http://example.org/owlim#" ;
|
||||
graphdb:defaultNS "" ;
|
||||
graphdb:imports "" ;
|
||||
graphdb:repository-type "file-repository" ;
|
||||
graphdb:storage-folder "storage" ;
|
||||
graphdb:entity-index-size "10000000" ;
|
||||
graphdb:in-memory-literal-properties "true" ;
|
||||
graphdb:enable-literal-index "true" ;
|
||||
]
|
||||
].
|
||||
@@ -1,28 +0,0 @@
|
||||
#! /bin/bash
|
||||
REPOSITORY_ID="langchain"
|
||||
GRAPHDB_URI="http://localhost:7200/"
|
||||
|
||||
echo -e "\nUsing GraphDB: ${GRAPHDB_URI}"
|
||||
|
||||
function startGraphDB {
|
||||
echo -e "\nStarting GraphDB..."
|
||||
exec /opt/graphdb/dist/bin/graphdb
|
||||
}
|
||||
|
||||
function waitGraphDBStart {
|
||||
echo -e "\nWaiting GraphDB to start..."
|
||||
for _ in $(seq 1 5); do
|
||||
CHECK_RES=$(curl --silent --write-out '%{http_code}' --output /dev/null ${GRAPHDB_URI}/rest/repositories)
|
||||
if [ "${CHECK_RES}" = '200' ]; then
|
||||
echo -e "\nUp and running"
|
||||
break
|
||||
fi
|
||||
sleep 30s
|
||||
echo "CHECK_RES: ${CHECK_RES}"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
startGraphDB &
|
||||
waitGraphDBStart
|
||||
wait
|
||||
@@ -38,7 +38,7 @@ install-py-deps:
|
||||
|
||||
generate-files:
|
||||
mkdir -p $(INTERMEDIATE_DIR)
|
||||
cp -r $(SOURCE_DIR)/* $(INTERMEDIATE_DIR)
|
||||
cp -rp $(SOURCE_DIR)/* $(INTERMEDIATE_DIR)
|
||||
|
||||
$(PYTHON) scripts/tool_feat_table.py $(INTERMEDIATE_DIR)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@ Most popular LangChain integrations implement asynchronous support of their APIs
|
||||
When an asynchronous implementation is not available, LangChain tries to provide a default implementation, even if it incurs
|
||||
a **slight** overhead.
|
||||
|
||||
By default, LangChain will delegate the execution of a unimplemented asynchronous methods to the synchronous counterparts. LangChain almost always assumes that the synchronous method should be treated as a blocking operation and should be run in a separate thread.
|
||||
By default, LangChain will delegate the execution of unimplemented asynchronous methods to the synchronous counterparts. LangChain almost always assumes that the synchronous method should be treated as a blocking operation and should be run in a separate thread.
|
||||
This is done using [asyncio.loop.run_in_executor](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) functionality provided by the `asyncio` library. LangChain uses the default executor provided by the `asyncio` library, which lazily initializes a thread pool executor with a default number of threads that is reused in the given event loop. While this strategy incurs a slight overhead due to context switching between threads, it guarantees that every asynchronous method has a default implementation that works out of the box.
|
||||
|
||||
## Performance
|
||||
|
||||
@@ -22,6 +22,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[Memory](https://langchain-ai.github.io/langgraph/concepts/memory/)**: Information about a conversation that is persisted so that it can be used in future conversations.
|
||||
- **[Multimodality](/docs/concepts/multimodality)**: The ability to work with data that comes in different forms, such as text, audio, images, and video.
|
||||
- **[Runnable interface](/docs/concepts/runnables)**: The base abstraction that many LangChain components and the LangChain Expression Language are built on.
|
||||
- **[Streaming](/docs/concepts/streaming)**: LangChain streaming APIs for surfacing results as they are generated.
|
||||
- **[LangChain Expression Language (LCEL)](/docs/concepts/lcel)**: A syntax for orchestrating LangChain components. Most useful for simpler applications.
|
||||
- **[Document loaders](/docs/concepts/document_loaders)**: Load a source as a list of documents.
|
||||
- **[Retrieval](/docs/concepts/retrieval)**: Information retrieval systems can retrieve structured or unstructured data from a datasource in response to a query.
|
||||
@@ -39,6 +40,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[Callbacks](/docs/concepts/callbacks)**: Callbacks enable the execution of custom auxiliary code in built-in components. Callbacks are used to stream outputs from LLMs in LangChain, trace the intermediate steps of an application, and more.
|
||||
- **[Tracing](/docs/concepts/tracing)**: The process of recording the steps that an application takes to go from input to output. Tracing is essential for debugging and diagnosing issues in complex applications.
|
||||
- **[Evaluation](/docs/concepts/evaluation)**: The process of assessing the performance and effectiveness of AI applications. This involves testing the model's responses against a set of predefined criteria or benchmarks to ensure it meets the desired quality standards and fulfills the intended purpose. This process is vital for building reliable applications.
|
||||
- **[Testing](/docs/concepts/testing)**: The process of verifying that a component of an integration or application works as expected. Testing is essential for ensuring that the application behaves correctly and that changes to the codebase do not introduce new bugs.
|
||||
|
||||
## Glossary
|
||||
|
||||
@@ -61,6 +63,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[InjectedToolArg](/docs/concepts/tools#injectedtoolarg)**: Mechanism to inject arguments into tool functions.
|
||||
- **[input and output types](/docs/concepts/runnables#input-and-output-types)**: Types used for input and output in Runnables.
|
||||
- **[Integration packages](/docs/concepts/architecture/#integration-packages)**: Third-party packages that integrate with LangChain.
|
||||
- **[Integration tests](/docs/concepts/testing#integration-tests)**: Tests that verify the correctness of the interaction between components, usually run with access to the underlying API that powers an integration.
|
||||
- **[invoke](/docs/concepts/runnables)**: A standard method to invoke a Runnable.
|
||||
- **[JSON mode](/docs/concepts/structured_outputs#json-mode)**: Returning responses in JSON format.
|
||||
- **[langchain-community](/docs/concepts/architecture#langchain-community)**: Community-driven components for LangChain.
|
||||
@@ -77,6 +80,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[role](/docs/concepts/messages#role)**: Represents the role (e.g., user, assistant) of a chat message.
|
||||
- **[RunnableConfig](/docs/concepts/runnables/#runnableconfig)**: Use to pass run time information to Runnables (e.g., `run_name`, `run_id`, `tags`, `metadata`, `max_concurrency`, `recursion_limit`, `configurable`).
|
||||
- **[Standard parameters for chat models](/docs/concepts/chat_models#standard-parameters)**: Parameters such as API key, `temperature`, and `max_tokens`,
|
||||
- **[Standard tests](/docs/concepts/testing#standard-tests)**: A defined set of unit and integration tests that all integrations must pass.
|
||||
- **[stream](/docs/concepts/streaming)**: Use to stream output from a Runnable or a graph.
|
||||
- **[Tokenization](/docs/concepts/tokens)**: The process of converting data into tokens and vice versa.
|
||||
- **[Tokens](/docs/concepts/tokens)**: The basic unit that a language model reads, processes, and generates under the hood.
|
||||
@@ -85,6 +89,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[@tool](/docs/concepts/tools/#create-tools-using-the-tool-decorator)**: Decorator for creating tools in LangChain.
|
||||
- **[Toolkits](/docs/concepts/tools#toolkits)**: A collection of tools that can be used together.
|
||||
- **[ToolMessage](/docs/concepts/messages#toolmessage)**: Represents a message that contains the results of a tool execution.
|
||||
- **[Unit tests](/docs/concepts/testing#unit-tests)**: Tests that verify the correctness of individual components, run in isolation without access to the Internet.
|
||||
- **[Vector stores](/docs/concepts/vectorstores)**: Datastores specialized for storing and efficiently searching vector embeddings.
|
||||
- **[with_structured_output](/docs/concepts/structured_outputs/#structured-output-method)**: A helper method for chat models that natively support [tool calling](/docs/concepts/tool_calling) to get structured output matching a given schema specified via Pydantic, JSON schema or a function.
|
||||
- **[with_types](/docs/concepts/runnables#with_types)**: Method to overwrite the input and output types of a runnable. Useful when working with complex LCEL chains and deploying with LangServe.
|
||||
|
||||
81
docs/docs/concepts/testing.mdx
Normal file
81
docs/docs/concepts/testing.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
# Testing
|
||||
<span data-heading-keywords="tests,testing,unit,integration"></span>
|
||||
|
||||
Testing is a critical part of the development process that ensures your code works as expected and meets the desired quality standards.
|
||||
|
||||
In the LangChain ecosystem, we have 2 main types of tests: **unit tests** and **integration tests**.
|
||||
|
||||
For integrations that implement standard LangChain abstractions, we have a set of **standard tests** (both unit and integration) that help maintain compatibility between different components and ensure reliability of high-usage ones.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
**Definition**: Unit tests are designed to validate the smallest parts of your code—individual functions or methods—ensuring they work as expected in isolation. They do not rely on external systems or integrations.
|
||||
|
||||
**Example**: Testing the `convert_langchain_aimessage_to_dict` function to confirm it correctly converts an AI message to a dictionary format:
|
||||
|
||||
```python
|
||||
from langchain_core.messages import AIMessage, ToolCall, convert_to_openai_messages
|
||||
|
||||
def test_convert_to_openai_messages():
|
||||
ai_message = AIMessage(
|
||||
content="Let me call that tool for you!",
|
||||
tool_calls=[
|
||||
ToolCall(name='parrot_multiply_tool', id='1', args={'a': 2, 'b': 3}),
|
||||
]
|
||||
)
|
||||
|
||||
result = convert_to_openai_messages(ai_message)
|
||||
|
||||
expected = {
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"type": "function",
|
||||
"id": "1",
|
||||
"function": {
|
||||
"name": "parrot_multiply_tool",
|
||||
"arguments": '{"a": 2, "b": 3}',
|
||||
},
|
||||
}
|
||||
],
|
||||
"content": "Let me call that tool for you!",
|
||||
}
|
||||
assert result == expected # Ensure conversion matches expected output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Tests
|
||||
|
||||
**Definition**: Integration tests validate that multiple components or systems work together as expected. For tools or integrations relying on external services, these tests often ensure end-to-end functionality.
|
||||
|
||||
**Example**: Testing `ParrotMultiplyTool` with access to an API service that multiplies two numbers and adds 80:
|
||||
|
||||
```python
|
||||
def test_integration_with_service():
|
||||
tool = ParrotMultiplyTool()
|
||||
result = tool.invoke({"a": 2, "b": 3})
|
||||
assert result == 86
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Standard Tests
|
||||
|
||||
**Definition**: Standard tests are pre-defined tests provided by LangChain to ensure consistency and reliability across all tools and integrations. They include both unit and integration test templates tailored for LangChain components.
|
||||
|
||||
**Example**: Subclassing LangChain's `ToolsUnitTests` or `ToolsIntegrationTests` to automatically run standard tests:
|
||||
|
||||
```python
|
||||
from langchain_tests.unit_tests import ToolsUnitTests
|
||||
|
||||
class TestParrotMultiplyToolUnit(ToolsUnitTests):
|
||||
@property
|
||||
def tool_constructor(self):
|
||||
return ParrotMultiplyTool
|
||||
|
||||
def tool_invoke_params_example(self):
|
||||
return {"a": 2, "b": 3}
|
||||
```
|
||||
|
||||
To learn more, check out our guide on [how to add standard tests to an integration](../../contributing/how_to/integrations/standard_tests).
|
||||
@@ -77,7 +77,7 @@ The central concept to understand is that LangChain provides a standardized inte
|
||||
The `.bind_tools()` method can be used to specify which tools are available for a model to call.
|
||||
|
||||
```python
|
||||
model_with_tools = model.bind_tools([tools_list])
|
||||
model_with_tools = model.bind_tools(tools_list)
|
||||
```
|
||||
|
||||
As a specific example, let's take a function `multiply` and bind it as a tool to a model that supports tool calling.
|
||||
|
||||
@@ -29,7 +29,7 @@ or new agents/chains from outside contributors without an existing GitHub discus
|
||||
|
||||
- New features must come with docs, unit tests, and (if appropriate) integration tests.
|
||||
- New integrations must come with docs, unit tests, and (if appropriate) integration tests.
|
||||
- See [this page](../integrations.mdx) for more details on contributing new integrations.
|
||||
- See [this page](../integrations/index.mdx) for more details on contributing new integrations.
|
||||
- New functionality should not inherit from or use deprecated methods or classes.
|
||||
- We will reject features that are likely to lead to security vulnerabilities or reports.
|
||||
- Do not add any hard dependencies. Integrations may add optional dependencies.
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
|
||||
- [**Documentation**](documentation/index.mdx): Help improve our docs, including this one!
|
||||
- [**Code**](code/index.mdx): Help us write code, fix bugs, or improve our infrastructure.
|
||||
- [**Integrations**](integrations.mdx): Help us integrate with your favorite vendors and tools.
|
||||
|
||||
## Integrations
|
||||
|
||||
- [**Start Here**](integrations/index.mdx): Help us integrate with your favorite vendors and tools.
|
||||
- [**Standard Tests**](integrations/standard_tests): Ensure your integration passes an expected set of tests.
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Contribute Integrations
|
||||
|
||||
To begin, make sure you have all the dependencies outlined in guide on [Contributing Code](code/index.mdx).
|
||||
|
||||
There are a few different places you can contribute integrations for LangChain:
|
||||
|
||||
- **Community**: For lighter-weight integrations that are primarily maintained by LangChain and the Open Source Community.
|
||||
- **Partner Packages**: For independent packages that are co-maintained by LangChain and a partner.
|
||||
|
||||
For the most part, **new integrations should be added to the Community package**. Partner packages require more maintenance as separate packages, so please confirm with the LangChain team before creating a new partner package.
|
||||
|
||||
In the following sections, we'll walk through how to contribute to each of these packages from a fake company, `Parrot Link AI`.
|
||||
|
||||
## Community package
|
||||
|
||||
The `langchain-community` package is in `libs/community` and contains most integrations.
|
||||
|
||||
It can be installed with `pip install langchain-community`, and exported members can be imported with code like
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatParrotLink
|
||||
from langchain_community.llms import ParrotLinkLLM
|
||||
from langchain_community.vectorstores import ParrotLinkVectorStore
|
||||
```
|
||||
|
||||
The `community` package relies on manually-installed dependent packages, so you will see errors
|
||||
if you try to import a package that is not installed. In our fake example, if you tried to import `ParrotLinkLLM` without installing `parrot-link-sdk`, you will see an `ImportError` telling you to install it when trying to use it.
|
||||
|
||||
Let's say we wanted to implement a chat model for Parrot Link AI. We would create a new file in `libs/community/langchain_community/chat_models/parrot_link.py` with the following code:
|
||||
|
||||
```python
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
"""ChatParrotLink chat model.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatParrotLink
|
||||
|
||||
model = ChatParrotLink()
|
||||
"""
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
And we would write tests in:
|
||||
|
||||
- Unit tests: `libs/community/tests/unit_tests/chat_models/test_parrot_link.py`
|
||||
- Integration tests: `libs/community/tests/integration_tests/chat_models/test_parrot_link.py`
|
||||
|
||||
And add documentation to:
|
||||
|
||||
- `docs/docs/integrations/chat/parrot_link.ipynb`
|
||||
|
||||
## Partner package in LangChain repo
|
||||
|
||||
:::caution
|
||||
Before starting a **partner** package, please confirm your intent with the LangChain team. Partner packages require more maintenance as separate packages, so we will close PRs that add new partner packages without prior discussion. See the above section for how to add a community integration.
|
||||
:::
|
||||
|
||||
Partner packages can be hosted in the `LangChain` monorepo or in an external repo.
|
||||
|
||||
Partner package in the `LangChain` repo is placed in `libs/partners/{partner}`
|
||||
and the package source code is in `libs/partners/{partner}/langchain_{partner}`.
|
||||
|
||||
A package is
|
||||
installed by users with `pip install langchain-{partner}`, and the package members
|
||||
can be imported with code like:
|
||||
|
||||
```python
|
||||
from langchain_{partner} import X
|
||||
```
|
||||
|
||||
### Set up a new package
|
||||
|
||||
To set up a new partner package, use the latest version of the LangChain CLI. You can install or update it with:
|
||||
|
||||
```bash
|
||||
pip install -U langchain-cli
|
||||
```
|
||||
|
||||
Let's say you want to create a new partner package working for a company called Parrot Link AI.
|
||||
|
||||
Then, run the following command to create a new partner package:
|
||||
|
||||
```bash
|
||||
cd libs/partners
|
||||
langchain-cli integration new
|
||||
> Name: parrot-link
|
||||
> Name of integration in PascalCase [ParrotLink]: ParrotLink
|
||||
```
|
||||
|
||||
This will create a new package in `libs/partners/parrot-link` with the following structure:
|
||||
|
||||
```
|
||||
libs/partners/parrot-link/
|
||||
langchain_parrot_link/ # folder containing your package
|
||||
...
|
||||
tests/
|
||||
...
|
||||
docs/ # bootstrapped docs notebooks, must be moved to /docs in monorepo root
|
||||
...
|
||||
scripts/ # scripts for CI
|
||||
...
|
||||
LICENSE
|
||||
README.md # fill out with information about your package
|
||||
Makefile # default commands for CI
|
||||
pyproject.toml # package metadata, mostly managed by Poetry
|
||||
poetry.lock # package lockfile, managed by Poetry
|
||||
.gitignore
|
||||
```
|
||||
|
||||
### Implement your package
|
||||
|
||||
First, add any dependencies your package needs, such as your company's SDK:
|
||||
|
||||
```bash
|
||||
poetry add parrot-link-sdk
|
||||
```
|
||||
|
||||
If you need separate dependencies for type checking, you can add them to the `typing` group with:
|
||||
|
||||
```bash
|
||||
poetry add --group typing types-parrot-link-sdk
|
||||
```
|
||||
|
||||
Then, implement your package in `libs/partners/parrot-link/langchain_parrot_link`.
|
||||
|
||||
By default, this will include stubs for a Chat Model, an LLM, and/or a Vector Store. You should delete any of the files you won't use and remove them from `__init__.py`.
|
||||
|
||||
### Write Unit and Integration Tests
|
||||
|
||||
Some basic tests are presented in the `tests/` directory. You should add more tests to cover your package's functionality.
|
||||
|
||||
For information on running and implementing tests, see the [Testing guide](testing.mdx).
|
||||
|
||||
### Write documentation
|
||||
|
||||
Documentation is generated from Jupyter notebooks in the `docs/` directory. You should place the notebooks with examples
|
||||
to the relevant `docs/docs/integrations` directory in the monorepo root.
|
||||
|
||||
### (If Necessary) Deprecate community integration
|
||||
|
||||
Note: this is only necessary if you're migrating an existing community integration into
|
||||
a partner package. If the component you're integrating is net-new to LangChain (i.e.
|
||||
not already in the `community` package), you can skip this step.
|
||||
|
||||
Let's pretend we migrated our `ChatParrotLink` chat model from the community package to
|
||||
the partner package. We would need to deprecate the old model in the community package.
|
||||
|
||||
We would do that by adding a `@deprecated` decorator to the old model as follows, in
|
||||
`libs/community/langchain_community/chat_models/parrot_link.py`.
|
||||
|
||||
Before our change, our chat model might look like this:
|
||||
|
||||
```python
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
...
|
||||
```
|
||||
|
||||
After our change, it would look like this:
|
||||
|
||||
```python
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
|
||||
@deprecated(
|
||||
since="0.0.<next community version>",
|
||||
removal="0.2.0",
|
||||
alternative_import="langchain_parrot_link.ChatParrotLink"
|
||||
)
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
...
|
||||
```
|
||||
|
||||
You should do this for *each* component that you're migrating to the partner package.
|
||||
|
||||
### Additional steps
|
||||
|
||||
Contributor steps:
|
||||
|
||||
- [ ] Add secret names to manual integrations workflow in `.github/workflows/_integration_test.yml`
|
||||
- [ ] Add secrets to release workflow (for pre-release testing) in `.github/workflows/_release.yml`
|
||||
|
||||
Maintainer steps (Contributors should **not** do these):
|
||||
|
||||
- [ ] set up pypi and test pypi projects
|
||||
- [ ] add credential secrets to Github Actions
|
||||
- [ ] add package to conda-forge
|
||||
|
||||
## Partner package in external repo
|
||||
|
||||
Partner packages in external repos must be coordinated between the LangChain team and
|
||||
the partner organization to ensure that they are maintained and updated.
|
||||
|
||||
If you're interested in creating a partner package in an external repo, please start
|
||||
with one in the LangChain repo, and then reach out to the LangChain team to discuss
|
||||
how to move it to an external repo.
|
||||
51
docs/docs/contributing/how_to/integrations/community.mdx
Normal file
51
docs/docs/contributing/how_to/integrations/community.mdx
Normal file
@@ -0,0 +1,51 @@
|
||||
## How to add a community integration (not recommended)
|
||||
|
||||
:::danger
|
||||
|
||||
We recommend following the [main integration guide](./index.mdx) to add new integrations instead.
|
||||
|
||||
If you follow this guide, there is a high likelihood we will close your PR with the above
|
||||
guide linked without much discussion.
|
||||
|
||||
:::
|
||||
|
||||
The `langchain-community` package is in `libs/community`.
|
||||
|
||||
It can be installed with `pip install langchain-community`, and exported members can be imported with code like
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatParrotLink
|
||||
from langchain_community.llms import ParrotLinkLLM
|
||||
from langchain_community.vectorstores import ParrotLinkVectorStore
|
||||
```
|
||||
|
||||
The `community` package relies on manually-installed dependent packages, so you will see errors
|
||||
if you try to import a package that is not installed. In our fake example, if you tried to import `ParrotLinkLLM` without installing `parrot-link-sdk`, you will see an `ImportError` telling you to install it when trying to use it.
|
||||
|
||||
Let's say we wanted to implement a chat model for Parrot Link AI. We would create a new file in `libs/community/langchain_community/chat_models/parrot_link.py` with the following code:
|
||||
|
||||
```python
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
"""ChatParrotLink chat model.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatParrotLink
|
||||
|
||||
model = ChatParrotLink()
|
||||
"""
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
And we would write tests in:
|
||||
|
||||
- Unit tests: `libs/community/tests/unit_tests/chat_models/test_parrot_link.py`
|
||||
- Integration tests: `libs/community/tests/integration_tests/chat_models/test_parrot_link.py`
|
||||
|
||||
And add documentation to:
|
||||
|
||||
- `docs/docs/integrations/chat/parrot_link.ipynb`
|
||||
132
docs/docs/contributing/how_to/integrations/from_template.mdx
Normal file
132
docs/docs/contributing/how_to/integrations/from_template.mdx
Normal file
@@ -0,0 +1,132 @@
|
||||
# How to publish an integration package from a template
|
||||
|
||||
:::danger
|
||||
This guide is a work-in-progress.
|
||||
:::
|
||||
|
||||
First, duplicate this template repository: https://github.com/langchain-ai/integration-repo-template
|
||||
|
||||
In this guide, we will create a `libs/langchain-parrot-link` folder, simulating the creation
|
||||
of a partner package for a fake company, "Parrot Link AI".
|
||||
|
||||
A package is
|
||||
installed by users with `pip install langchain-{partner}`, and the package members
|
||||
can be imported with code like:
|
||||
|
||||
```python
|
||||
from langchain_{partner} import X
|
||||
```
|
||||
|
||||
## Set up a new package
|
||||
|
||||
To set up a new partner package, use the latest version of the LangChain CLI. You can install or update it with:
|
||||
|
||||
```bash
|
||||
pip install -U langchain-cli
|
||||
```
|
||||
|
||||
Let's say you want to create a new partner package working for a company called Parrot Link AI.
|
||||
|
||||
Then, run the following command to create a new partner package:
|
||||
|
||||
```bash
|
||||
mkdir libs
|
||||
cd libs/
|
||||
langchain-cli integration new
|
||||
> Name: parrot-link
|
||||
> Name of integration in PascalCase [ParrotLink]: ParrotLink
|
||||
```
|
||||
|
||||
This will create a new package in `libs/parrot-link` with the following structure:
|
||||
|
||||
```
|
||||
libs/parrot-link/
|
||||
langchain_parrot_link/ # folder containing your package
|
||||
...
|
||||
tests/
|
||||
...
|
||||
docs/ # bootstrapped docs notebooks, must be moved to /docs in monorepo root
|
||||
...
|
||||
scripts/ # scripts for CI
|
||||
...
|
||||
LICENSE
|
||||
README.md # fill out with information about your package
|
||||
Makefile # default commands for CI
|
||||
pyproject.toml # package metadata, mostly managed by Poetry
|
||||
poetry.lock # package lockfile, managed by Poetry
|
||||
.gitignore
|
||||
```
|
||||
|
||||
## Implement your package
|
||||
|
||||
First, add any dependencies your package needs, such as your company's SDK:
|
||||
|
||||
```bash
|
||||
poetry add parrot-link-sdk
|
||||
```
|
||||
|
||||
If you need separate dependencies for type checking, you can add them to the `typing` group with:
|
||||
|
||||
```bash
|
||||
poetry add --group typing types-parrot-link-sdk
|
||||
```
|
||||
|
||||
Then, implement your package in `libs/partners/parrot-link/langchain_parrot_link`.
|
||||
|
||||
By default, this will include stubs for a Chat Model, an LLM, and/or a Vector Store. You should delete any of the files you won't use and remove them from `__init__.py`.
|
||||
|
||||
## Write Unit and Integration Tests
|
||||
|
||||
Some basic tests are presented in the `tests/` directory. You should add more tests to cover your package's functionality.
|
||||
|
||||
For information on running and implementing tests, see the [Testing guide](../testing.mdx).
|
||||
|
||||
## Write documentation
|
||||
|
||||
Documentation is generated from Jupyter notebooks in the `docs/` directory. You should place the notebooks with examples
|
||||
to the relevant `docs/docs/integrations` directory in the monorepo root.
|
||||
|
||||
## (If Necessary) Deprecate community integration
|
||||
|
||||
Note: this is only necessary if you're migrating an existing community integration into
|
||||
a partner package. If the component you're integrating is net-new to LangChain (i.e.
|
||||
not already in the `community` package), you can skip this step.
|
||||
|
||||
Let's pretend we migrated our `ChatParrotLink` chat model from the community package to
|
||||
the partner package. We would need to deprecate the old model in the community package.
|
||||
|
||||
We would do that by adding a `@deprecated` decorator to the old model as follows, in
|
||||
`libs/community/langchain_community/chat_models/parrot_link.py`.
|
||||
|
||||
Before our change, our chat model might look like this:
|
||||
|
||||
```python
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
...
|
||||
```
|
||||
|
||||
After our change, it would look like this:
|
||||
|
||||
```python
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
|
||||
@deprecated(
|
||||
since="0.0.<next community version>",
|
||||
removal="0.2.0",
|
||||
alternative_import="langchain_parrot_link.ChatParrotLink"
|
||||
)
|
||||
class ChatParrotLink(BaseChatModel):
|
||||
...
|
||||
```
|
||||
|
||||
You should do this for *each* component that you're migrating to the partner package.
|
||||
|
||||
## Additional steps
|
||||
|
||||
Contributor steps:
|
||||
|
||||
- [ ] Add secret names to manual integrations workflow in `.github/workflows/_integration_test.yml`
|
||||
- [ ] Add secrets to release workflow (for pre-release testing) in `.github/workflows/_release.yml`
|
||||
- [ ] set up pypi and test pypi projects
|
||||
- [ ] add credential secrets to Github Actions
|
||||
- [ ] add package to conda-forge
|
||||
79
docs/docs/contributing/how_to/integrations/index.mdx
Normal file
79
docs/docs/contributing/how_to/integrations/index.mdx
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Contribute Integrations
|
||||
|
||||
LangChain integrations are packages that provide access to language models, vector stores, and other components that can be used in LangChain.
|
||||
|
||||
This guide will walk you through how to contribute new integrations to LangChain, by
|
||||
publishing an integration package to PyPi, and adding documentation for it
|
||||
to the LangChain Monorepo.
|
||||
|
||||
These instructions will evolve over the next few months as we improve our integration
|
||||
processes.
|
||||
|
||||
## Components to Integrate
|
||||
|
||||
:::info
|
||||
|
||||
See the [Conceptual Guide](../../../concepts/index.mdx) for an overview of all components
|
||||
supported in LangChain
|
||||
|
||||
:::
|
||||
|
||||
While any component can be integrated into LangChain, at this time we are only accepting
|
||||
new integrations in the docs of the following kinds:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Integrate these ✅</th>
|
||||
<th>Not these ❌</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li>Chat Models</li>
|
||||
<li>Tools/Toolkits</li>
|
||||
<li>Retrievers</li>
|
||||
<li>Document Loaders</li>
|
||||
<li>Vector Stores</li>
|
||||
<li>Embedding Models</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>LLMs (Text-Completion Models)</li>
|
||||
<li>Key-Value Stores</li>
|
||||
<li>Document Transformers</li>
|
||||
<li>Model Caches</li>
|
||||
<li>Graphs</li>
|
||||
<li>Message Histories</li>
|
||||
<li>Callbacks</li>
|
||||
<li>Chat Loaders</li>
|
||||
<li>Adapters</li>
|
||||
<li>Other abstractions</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## How to contribute an integration
|
||||
|
||||
The only step necessary to "be" a LangChain integration is to add documentation
|
||||
that will render on this site (https://python.langchain.com/).
|
||||
|
||||
As a prerequisite to adding your integration to our documentation, you must:
|
||||
|
||||
1. Confirm that your integration is in the [list of components](#components-to-integrate) we are currently accepting.
|
||||
2. Ensure that your integration is in a separate package that can be installed with `pip install <your-package>`.
|
||||
3. [Implement the standard tests](/docs/contributing/how_to/integrations/standard_tests) for your integration and successfully run them.
|
||||
3. Write documentation for your integration in the `docs/docs/integrations/<component_type>` directory of the LangChain monorepo.
|
||||
4. Add a provider page for your integration in the `docs/docs/integrations/providers` directory of the LangChain monorepo.
|
||||
|
||||
Once you have completed these steps, you can submit a PR to the LangChain monorepo to add your integration to the documentation.
|
||||
|
||||
## Further Reading
|
||||
|
||||
If you're starting from scratch, you can follow the [Integration Template Guide](./from_template.mdx) to create and publish a new integration package
|
||||
to the above spec.
|
||||
468
docs/docs/contributing/how_to/integrations/standard_tests.ipynb
Normal file
468
docs/docs/contributing/how_to/integrations/standard_tests.ipynb
Normal file
@@ -0,0 +1,468 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# How to add standard tests to an integration\n",
|
||||
"\n",
|
||||
"When creating either a custom class for yourself or a new tool to publish in a LangChain integration, it is important to add standard tests to ensure it works as expected. This guide will show you how to add standard tests to a tool, and you can **[Skip to the test templates](#standard-test-templates-per-component)** for implementing tests for each integration.\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First, let's install 2 dependencies:\n",
|
||||
"\n",
|
||||
"- `langchain-core` will define the interfaces we want to import to define our custom tool.\n",
|
||||
"- `langchain-tests==0.3.3` will provide the standard tests we want to use.\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"Because added tests in new versions of `langchain-tests` will always break your CI/CD pipelines, we recommend pinning the \n",
|
||||
"version of `langchain-tests` to avoid unexpected changes.\n",
|
||||
"\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -U langchain-core langchain-tests==0.3.2 pytest pytest-socket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's say we're publishing a package, `langchain_parrot_link`, that exposes a\n",
|
||||
"tool called `ParrotMultiplyTool`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"langchain_parrot_link/tools.py\"\n",
|
||||
"from langchain_core.tools import BaseTool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class ParrotMultiplyTool(BaseTool):\n",
|
||||
" name: str = \"ParrotMultiplyTool\"\n",
|
||||
" description: str = (\n",
|
||||
" \"Multiply two numbers like a parrot. Parrots always add \"\n",
|
||||
" \"eighty for their matey.\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def _run(self, a: int, b: int) -> int:\n",
|
||||
" return a * b + 80"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And we'll assume you've structured your package the same way as the main LangChain\n",
|
||||
"packages:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"/\n",
|
||||
"├── langchain_parrot_link/\n",
|
||||
"│ └── tools.py\n",
|
||||
"└── tests/\n",
|
||||
" ├── unit_tests/\n",
|
||||
" │ └── test_tools.py\n",
|
||||
" └── integration_tests/\n",
|
||||
" └── test_tools.py\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"## Add and configure standard tests\n",
|
||||
"\n",
|
||||
"There are 2 namespaces in the `langchain-tests` package: \n",
|
||||
"\n",
|
||||
"- [unit tests](../../../concepts/testing.mdx#unit-tests) (`langchain_tests.unit_tests`): designed to be used to test the tool in isolation and without access to external services\n",
|
||||
"- [integration tests](../../../concepts/testing.mdx#unit-tests) (`langchain_tests.integration_tests`): designed to be used to test the tool with access to external services (in particular, the external service that the tool is designed to interact with).\n",
|
||||
"\n",
|
||||
"Both types of tests are implemented as [`pytest` class-based test suites](https://docs.pytest.org/en/7.1.x/getting-started.html#group-multiple-tests-in-a-class).\n",
|
||||
"\n",
|
||||
"By subclassing the base classes for each type of standard test (see below), you get all of the standard tests for that type, and you\n",
|
||||
"can override the properties that the test suite uses to configure the tests.\n",
|
||||
"\n",
|
||||
"### Standard tools tests\n",
|
||||
"\n",
|
||||
"Here's how you would configure the standard unit tests for the custom tool, e.g. in `tests/test_tools.py`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"title": "tests/test_custom_tool.py"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/unit_tests/test_tools.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_tests.unit_tests import ToolsUnitTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotMultiplyToolUnit(ToolsUnitTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_tools.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_tests.integration_tests import ToolsIntegrationTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotMultiplyToolIntegration(ToolsIntegrationTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"and you would run these with the following commands from your project root\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"# run unit tests without network access\n",
|
||||
"pytest --disable-socket --allow-unix-socket tests/unit_tests\n",
|
||||
"\n",
|
||||
"# run integration tests\n",
|
||||
"pytest tests/integration_tests\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Standard test templates per component:\n",
|
||||
"\n",
|
||||
"Above, we implement the **unit** and **integration** standard tests for a tool. Below are the templates for implementing the standard tests for each component:\n",
|
||||
"\n",
|
||||
"<details>\n",
|
||||
" <summary>Chat Models</summary>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/unit_tests/test_chat_models.py\"\n",
|
||||
"from typing import Tuple, Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.chat_models import ChatParrotLink\n",
|
||||
"from langchain_tests.unit_tests import ChatModelUnitTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestChatParrotLinkUnit(ChatModelUnitTests):\n",
|
||||
" @property\n",
|
||||
" def chat_model_class(self) -> Type[ChatParrotLink]:\n",
|
||||
" return ChatParrotLink\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def chat_model_params(self) -> dict:\n",
|
||||
" return {\"model\": \"bird-brain-001\", \"temperature\": 0}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_chat_models.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.chat_models import ChatParrotLink\n",
|
||||
"from langchain_tests.integration_tests import ChatModelIntegrationTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestChatParrotLinkIntegration(ChatModelIntegrationTests):\n",
|
||||
" @property\n",
|
||||
" def chat_model_class(self) -> Type[ChatParrotLink]:\n",
|
||||
" return ChatParrotLink\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def chat_model_params(self) -> dict:\n",
|
||||
" return {\"model\": \"bird-brain-001\", \"temperature\": 0}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"<details>\n",
|
||||
" <summary>Embedding Models</summary>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/unit_tests/test_embeddings.py\"\n",
|
||||
"from typing import Tuple, Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.embeddings import ParrotLinkEmbeddings\n",
|
||||
"from langchain_tests.unit_tests import EmbeddingsUnitTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotLinkEmbeddingsUnit(EmbeddingsUnitTests):\n",
|
||||
" @property\n",
|
||||
" def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:\n",
|
||||
" return ParrotLinkEmbeddings\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def embedding_model_params(self) -> dict:\n",
|
||||
" return {\"model\": \"nest-embed-001\", \"temperature\": 0}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_embeddings.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.embeddings import ParrotLinkEmbeddings\n",
|
||||
"from langchain_tests.integration_tests import EmbeddingsIntegrationTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests):\n",
|
||||
" @property\n",
|
||||
" def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:\n",
|
||||
" return ParrotLinkEmbeddings\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def embedding_model_params(self) -> dict:\n",
|
||||
" return {\"model\": \"nest-embed-001\", \"temperature\": 0}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"<details>\n",
|
||||
" <summary>Tools/Toolkits</summary>\n",
|
||||
" <p>Note: The standard tests for tools/toolkits are implemented in the example in the main body of this guide too.</p>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/unit_tests/test_tools.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_tests.unit_tests import ToolsUnitTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotMultiplyToolUnit(ToolsUnitTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_tools.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_tests.integration_tests import ToolsIntegrationTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestParrotMultiplyToolIntegration(ToolsIntegrationTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>\n",
|
||||
"<details>\n",
|
||||
" <summary>Vector Stores</summary>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_vectorstores_sync.py\"\n",
|
||||
"\n",
|
||||
"from typing import AsyncGenerator, Generator\n",
|
||||
"\n",
|
||||
"import pytest\n",
|
||||
"from langchain_core.vectorstores import VectorStore\n",
|
||||
"from langchain_parrot_link.vectorstores import ParrotVectorStore\n",
|
||||
"from langchain_standard_tests.integration_tests.vectorstores import (\n",
|
||||
" AsyncReadWriteTestSuite,\n",
|
||||
" ReadWriteTestSuite,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestSync(ReadWriteTestSuite):\n",
|
||||
" @pytest.fixture()\n",
|
||||
" def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore\n",
|
||||
" \"\"\"Get an empty vectorstore for unit tests.\"\"\"\n",
|
||||
" store = ParrotVectorStore()\n",
|
||||
" # note: store should be EMPTY at this point\n",
|
||||
" # if you need to delete data, you may do so here\n",
|
||||
" try:\n",
|
||||
" yield store\n",
|
||||
" finally:\n",
|
||||
" # cleanup operations, or deleting data\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TestAsync(AsyncReadWriteTestSuite):\n",
|
||||
" @pytest.fixture()\n",
|
||||
" async def vectorstore(self) -> AsyncGenerator[VectorStore, None]: # type: ignore\n",
|
||||
" \"\"\"Get an empty vectorstore for unit tests.\"\"\"\n",
|
||||
" store = ParrotVectorStore()\n",
|
||||
" # note: store should be EMPTY at this point\n",
|
||||
" # if you need to delete data, you may do so here\n",
|
||||
" try:\n",
|
||||
" yield store\n",
|
||||
" finally:\n",
|
||||
" # cleanup operations, or deleting data\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"</details>"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -16,7 +16,8 @@ More coming soon! We are working on tutorials to help you make your first contri
|
||||
|
||||
- [**Documentation**](how_to/documentation/index.mdx): Help improve our docs, including this one!
|
||||
- [**Code**](how_to/code/index.mdx): Help us write code, fix bugs, or improve our infrastructure.
|
||||
- [**Integrations**](how_to/integrations.mdx): Help us integrate with your favorite vendors and tools.
|
||||
- [**Integrations**](how_to/integrations/index.mdx): Help us integrate with your favorite vendors and tools.
|
||||
- [**Standard Tests**](how_to/integrations/standard_tests): Ensure your integration passes an expected set of tests.
|
||||
|
||||
## Reference
|
||||
|
||||
|
||||
@@ -61,5 +61,5 @@ The `/libs` directory contains the code for the LangChain packages.
|
||||
To learn more about how to contribute code see the following guidelines:
|
||||
|
||||
- [Code](../how_to/code/index.mdx): Learn how to develop in the LangChain codebase.
|
||||
- [Integrations](../how_to/integrations.mdx): Learn how to contribute to third-party integrations to `langchain-community` or to start a new partner package.
|
||||
- [Integrations](../how_to/integrations/index.mdx): Learn how to contribute to third-party integrations to `langchain-community` or to start a new partner package.
|
||||
- [Testing](../how_to/testing.mdx): Guidelines to learn how to write tests for the packages.
|
||||
|
||||
@@ -15,87 +15,9 @@ The base Embeddings class in LangChain provides two methods: one for embedding d
|
||||
|
||||
### Setup
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import EmbeddingTabs from "@theme/EmbeddingTabs";
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI" default>
|
||||
To start we'll need to install the OpenAI partner package:
|
||||
|
||||
```bash
|
||||
pip install langchain-openai
|
||||
```
|
||||
|
||||
Accessing the API requires an API key, which you can get by creating an account and heading [here](https://platform.openai.com/account/api-keys). Once we have a key we'll want to set it as an environment variable by running:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY="..."
|
||||
```
|
||||
|
||||
If you'd prefer not to set an environment variable you can pass the key in directly via the `api_key` named parameter when initiating the OpenAI LLM class:
|
||||
|
||||
```python
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
|
||||
embeddings_model = OpenAIEmbeddings(api_key="...")
|
||||
```
|
||||
|
||||
Otherwise you can initialize without any params:
|
||||
```python
|
||||
from langchain_openai import OpenAIEmbeddings
|
||||
|
||||
embeddings_model = OpenAIEmbeddings()
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cohere" label="Cohere">
|
||||
|
||||
To start we'll need to install the Cohere SDK package:
|
||||
|
||||
```bash
|
||||
pip install langchain-cohere
|
||||
```
|
||||
|
||||
Accessing the API requires an API key, which you can get by creating an account and heading [here](https://dashboard.cohere.com/api-keys). Once we have a key we'll want to set it as an environment variable by running:
|
||||
|
||||
```shell
|
||||
export COHERE_API_KEY="..."
|
||||
```
|
||||
|
||||
If you'd prefer not to set an environment variable you can pass the key in directly via the `cohere_api_key` named parameter when initiating the Cohere LLM class:
|
||||
|
||||
```python
|
||||
from langchain_cohere import CohereEmbeddings
|
||||
|
||||
embeddings_model = CohereEmbeddings(cohere_api_key="...", model='embed-english-v3.0')
|
||||
```
|
||||
|
||||
Otherwise you can initialize simply as shown below:
|
||||
```python
|
||||
from langchain_cohere import CohereEmbeddings
|
||||
|
||||
embeddings_model = CohereEmbeddings(model='embed-english-v3.0')
|
||||
```
|
||||
Do note that it is mandatory to pass the model parameter while initializing the CohereEmbeddings class.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="huggingface" label="Hugging Face">
|
||||
|
||||
To start we'll need to install the Hugging Face partner package:
|
||||
|
||||
```bash
|
||||
pip install langchain-huggingface
|
||||
```
|
||||
|
||||
You can then load any [Sentence Transformers model](https://huggingface.co/models?library=sentence-transformers) from the Hugging Face Hub.
|
||||
|
||||
```python
|
||||
from langchain_huggingface import HuggingFaceEmbeddings
|
||||
|
||||
embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
<EmbeddingTabs customVarName="embeddings_model" />
|
||||
|
||||
### `embed_documents`
|
||||
#### Embed list of texts
|
||||
|
||||
@@ -331,7 +331,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can also pass in an arbitrary function. This function should take in a list of messages and output a list of messages.\n",
|
||||
"We can do all types of arbitrary formatting of messages here. In this cases, let's just add a SystemMessage to the start of the list of messages."
|
||||
"We can do all types of arbitrary formatting of messages here. In this case, let's just add a SystemMessage to the start of the list of messages."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains.query_constructor.base import AttributeInfo\n",
|
||||
"from langchain.chains.query_constructor.schema import AttributeInfo\n",
|
||||
"from langchain.retrievers.self_query.base import SelfQueryRetriever\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# How to add standard tests to a tool\n",
|
||||
"\n",
|
||||
"When creating either a custom tool or a new tool to publish in a LangChain integration, it is important to add standard tests to ensure the tool works as expected. This guide will show you how to add standard tests to a tool.\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"First, let's install 2 dependencies:\n",
|
||||
"\n",
|
||||
"- `langchain-core` will define the interfaces we want to import to define our custom tool.\n",
|
||||
"- `langchain-tests==0.3.0` will provide the standard tests we want to use.\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"The `langchain-tests` package contains the module `langchain_standard_tests`. This name\n",
|
||||
"mistmatch is due to this package historically being called `langchain_standard_tests` and\n",
|
||||
"the name not being available on PyPi. This will either be reconciled by our \n",
|
||||
"[PEP 541 request](https://github.com/pypi/support/issues/5062) (we welcome upvotes!), \n",
|
||||
"or in a new release of `langchain-tests`.\n",
|
||||
"\n",
|
||||
"Because added tests in new versions of `langchain-tests` will always break your CI/CD pipelines, we recommend pinning the \n",
|
||||
"version of `langchain-tests==0.3.0` to avoid unexpected changes.\n",
|
||||
"\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -U langchain-core langchain-tests==0.3.0 pytest pytest-socket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's say we're publishing a package, `langchain_parrot_link`, that exposes a\n",
|
||||
"tool called `ParrotMultiplyTool`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"langchain_parrot_link/tools.py\"\n",
|
||||
"from langchain_core.tools import BaseTool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class ParrotMultiplyTool(BaseTool):\n",
|
||||
" name: str = \"ParrotMultiplyTool\"\n",
|
||||
" description: str = (\n",
|
||||
" \"Multiply two numbers like a parrot. Parrots always add \"\n",
|
||||
" \"eighty for their matey.\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def _run(self, a: int, b: int) -> int:\n",
|
||||
" return a * b + 80"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And we'll assume you've structured your package the same way as the main LangChain\n",
|
||||
"packages:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"/\n",
|
||||
"├── langchain_parrot_link/\n",
|
||||
"│ └── tools.py\n",
|
||||
"└── tests/\n",
|
||||
" ├── unit_tests/\n",
|
||||
" │ └── test_tools.py\n",
|
||||
" └── integration_tests/\n",
|
||||
" └── test_tools.py\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"## Add and configure standard tests\n",
|
||||
"\n",
|
||||
"There are 2 namespaces in the `langchain-tests` package: \n",
|
||||
"\n",
|
||||
"- unit tests (`langchain_standard_tests.unit_tests`): designed to be used to test the tool in isolation and without access to external services\n",
|
||||
"- integration tests (`langchain_standard_tests.integration_tests`): designed to be used to test the tool with access to external services (in particular, the external service that the tool is designed to interact with).\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"Integration tests can also be run without access to external services, **if** they are properly mocked.\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
"Both types of tests are implemented as [`pytest` class-based test suites](https://docs.pytest.org/en/7.1.x/getting-started.html#group-multiple-tests-in-a-class).\n",
|
||||
"\n",
|
||||
"By subclassing the base classes for each type of standard test (see below), you get all of the standard tests for that type, and you\n",
|
||||
"can override the properties that the test suite uses to configure the tests.\n",
|
||||
"\n",
|
||||
"### Standard tools tests\n",
|
||||
"\n",
|
||||
"Here's how you would configure the standard unit tests for the custom tool, e.g. in `tests/test_tools.py`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"title": "tests/test_custom_tool.py"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/unit_tests/test_custom_tool.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_standard_tests.unit_tests import ToolsUnitTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MultiplyToolUnitTests(ToolsUnitTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# title=\"tests/integration_tests/test_custom_tool.py\"\n",
|
||||
"from typing import Type\n",
|
||||
"\n",
|
||||
"from langchain_parrot_link.tools import ParrotMultiplyTool\n",
|
||||
"from langchain_standard_tests.integration_tests import ToolsIntegrationTests\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class MultiplyToolIntegrationTests(ToolsIntegrationTests):\n",
|
||||
" @property\n",
|
||||
" def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n",
|
||||
" return ParrotMultiplyTool\n",
|
||||
"\n",
|
||||
" def tool_constructor_params(self) -> dict:\n",
|
||||
" # if your tool constructor instead required initialization arguments like\n",
|
||||
" # `def __init__(self, some_arg: int):`, you would return those here\n",
|
||||
" # as a dictionary, e.g.: `return {'some_arg': 42}`\n",
|
||||
" return {}\n",
|
||||
"\n",
|
||||
" def tool_invoke_params_example(self) -> dict:\n",
|
||||
" \"\"\"\n",
|
||||
" Returns a dictionary representing the \"args\" of an example tool call.\n",
|
||||
"\n",
|
||||
" This should NOT be a ToolCall dict - i.e. it should not\n",
|
||||
" have {\"name\", \"id\", \"args\"} keys.\n",
|
||||
" \"\"\"\n",
|
||||
" return {\"a\": 2, \"b\": 3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"and you would run these with the following commands from your project root\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"# run unit tests without network access\n",
|
||||
"pytest --disable-socket --enable-unix-socket tests/unit_tests\n",
|
||||
"\n",
|
||||
"# run integration tests\n",
|
||||
"pytest tests/integration_tests\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.4"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"source": [
|
||||
"# GigaChat\n",
|
||||
"This notebook shows how to use LangChain with [GigaChat](https://developers.sber.ru/portal/products/gigachat).\n",
|
||||
"To use you need to install ```gigachat``` python package."
|
||||
"To use you need to install ```langchain_gigachat``` python package."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet gigachat"
|
||||
"%pip install --upgrade --quiet langchain-gigachat"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -53,20 +53,20 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models import GigaChat\n",
|
||||
"from langchain_gigachat import GigaChat\n",
|
||||
"\n",
|
||||
"chat = GigaChat(verify_ssl_certs=False, scope=\"GIGACHAT_API_PERS\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
||||
@@ -15,6 +15,14 @@ If you'd like to contribute an integration, see [Contributing integrations](/doc
|
||||
|
||||
:::
|
||||
|
||||
import ChatModelTabs from "@theme/ChatModelTabs";
|
||||
|
||||
<ChatModelTabs openaiParams={`model="gpt-4o-mini"`} />
|
||||
|
||||
```python
|
||||
model.invoke("Hello, world!")
|
||||
```
|
||||
|
||||
## Featured Providers
|
||||
|
||||
:::info
|
||||
|
||||
348
docs/docs/integrations/chat/outlines.ipynb
Normal file
348
docs/docs/integrations/chat/outlines.ipynb
Normal file
@@ -0,0 +1,348 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "raw",
|
||||
"id": "afaf8039",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n",
|
||||
"sidebar_label: Outlines\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e49f1e0d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# ChatOutlines\n",
|
||||
"\n",
|
||||
"This will help you getting started with Outlines [chat models](/docs/concepts/chat_models/). For detailed documentation of all ChatOutlines features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/chat_models/outlines.chat_models.ChatOutlines.html).\n",
|
||||
"\n",
|
||||
"[Outlines](https://github.com/outlines-dev/outlines) is a library for constrained language generation. It allows you to use large language models (LLMs) with various backends while applying constraints to the generated output.\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | JS support | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [ChatOutlines](https://api.python.langchain.com/en/latest/chat_models/outlines.chat_models.ChatOutlines.html) | [langchain-community](https://api.python.langchain.com/en/latest/community_api_reference.html) | ✅ | ❌ | ❌ |  |  |\n",
|
||||
"\n",
|
||||
"### Model features\n",
|
||||
"| [Tool calling](/docs/how_to/tool_calling) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"To access Outlines models you'll need to have an internet connection to download the model weights from huggingface. Depending on the backend you need to install the required dependencies (see [Outlines docs](https://dottxt-ai.github.io/outlines/latest/installation/))\n",
|
||||
"\n",
|
||||
"### Credentials\n",
|
||||
"\n",
|
||||
"There is no built-in auth mechanism for Outlines.\n",
|
||||
"\n",
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"The LangChain Outlines integration lives in the `langchain-community` package and requires the `outlines` library:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "652d6238-1f87-422a-b135-f5abbb8652fc",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-community outlines"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a38cde65-254d-4219-a441-068766c0d4b5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
"Now we can instantiate our model object and generate chat completions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models.outlines import ChatOutlines\n",
|
||||
"\n",
|
||||
"# For llamacpp backend\n",
|
||||
"model = ChatOutlines(model=\"TheBloke/phi-2-GGUF/phi-2.Q4_K_M.gguf\", backend=\"llamacpp\")\n",
|
||||
"\n",
|
||||
"# For vllm backend (not available on Mac)\n",
|
||||
"model = ChatOutlines(model=\"meta-llama/Llama-3.2-1B\", backend=\"vllm\")\n",
|
||||
"\n",
|
||||
"# For mlxlm backend (only available on Mac)\n",
|
||||
"model = ChatOutlines(model=\"mistralai/Ministral-8B-Instruct-2410\", backend=\"mlxlm\")\n",
|
||||
"\n",
|
||||
"# For huggingface transformers backend\n",
|
||||
"model = ChatOutlines(model=\"microsoft/phi-2\") # defaults to transformers backend"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2b4f3e15",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "62e0dbc3",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"\n",
|
||||
"messages = [HumanMessage(content=\"What will the capital of mars be called?\")]\n",
|
||||
"response = model.invoke(messages)\n",
|
||||
"\n",
|
||||
"response.content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "18e2bfc0-7e78-4528-a73f-499ac150dca8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Streaming\n",
|
||||
"\n",
|
||||
"ChatOutlines supports streaming of tokens:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e197d1d7-a070-4c96-9f8a-a0e86d046e0b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"messages = [HumanMessage(content=\"Count to 10 in French:\")]\n",
|
||||
"\n",
|
||||
"for chunk in model.stream(messages):\n",
|
||||
" print(chunk.content, end=\"\", flush=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ccc3e2f6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5a032003",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"You are a helpful assistant that translates {input_language} to {output_language}.\",\n",
|
||||
" ),\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chain = prompt | model\n",
|
||||
"chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"input_language\": \"English\",\n",
|
||||
" \"output_language\": \"German\",\n",
|
||||
" \"input\": \"I love programming.\",\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d1ee55bc-ffc8-4cfa-801c-993953a08cfd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constrained Generation\n",
|
||||
"\n",
|
||||
"ChatOutlines allows you to apply various constraints to the generated output:\n",
|
||||
"\n",
|
||||
"### Regex Constraint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.regex = r\"((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\"\n",
|
||||
"\n",
|
||||
"response = model.invoke(\"What is the IP address of Google's DNS server?\")\n",
|
||||
"\n",
|
||||
"response.content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Type Constraints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.type_constraints = int\n",
|
||||
"response = model.invoke(\"What is the answer to life, the universe, and everything?\")\n",
|
||||
"\n",
|
||||
"response.content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Pydantic and JSON Schemas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Person(BaseModel):\n",
|
||||
" name: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"model.json_schema = Person\n",
|
||||
"response = model.invoke(\"Who are the main contributors to LangChain?\")\n",
|
||||
"person = Person.model_validate_json(response.content)\n",
|
||||
"\n",
|
||||
"person"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Context Free Grammars"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9a5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.grammar = \"\"\"\n",
|
||||
"?start: expression\n",
|
||||
"?expression: term ((\"+\" | \"-\") term)*\n",
|
||||
"?term: factor ((\"*\" | \"/\") factor)*\n",
|
||||
"?factor: NUMBER | \"-\" factor | \"(\" expression \")\"\n",
|
||||
"%import common.NUMBER\n",
|
||||
"%import common.WS\n",
|
||||
"%ignore WS\n",
|
||||
"\"\"\"\n",
|
||||
"response = model.invoke(\"Give me a complex arithmetic expression:\")\n",
|
||||
"\n",
|
||||
"response.content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "aa5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LangChain's Structured Output\n",
|
||||
"\n",
|
||||
"You can also use LangChain's Structured Output with ChatOutlines:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ba5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class AnswerWithJustification(BaseModel):\n",
|
||||
" answer: str\n",
|
||||
" justification: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_model = model.with_structured_output(AnswerWithJustification)\n",
|
||||
"result = _model.invoke(\"What weighs more, a pound of bricks or a pound of feathers?\")\n",
|
||||
"\n",
|
||||
"result"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ca5bb5ca-c3ae-4a58-be67-2cd18574b9a3",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatOutlines features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/outlines.chat_models.ChatOutlines.html\n",
|
||||
"\n",
|
||||
"## Full Outlines Documentation: \n",
|
||||
"\n",
|
||||
"https://dottxt-ai.github.io/outlines/latest/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"source": [
|
||||
"# ChatSambaNovaCloud\n",
|
||||
"\n",
|
||||
"This will help you getting started with SambaNovaCloud [chat models](/docs/concepts/chat_models). For detailed documentation of all ChatSambaNovaCloud features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html).\n",
|
||||
"This will help you getting started with SambaNovaCloud [chat models](/docs/concepts/chat_models). For detailed documentation of all ChatSambaNovaCloud features and configurations head to the [API reference](https://python.langchain.com/api_reference/community/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html).\n",
|
||||
"\n",
|
||||
"**[SambaNova](https://sambanova.ai/)'s** [SambaNova Cloud](https://cloud.sambanova.ai/) is a platform for performing inference with open-source models\n",
|
||||
"\n",
|
||||
@@ -28,13 +28,13 @@
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | JS support | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [ChatSambaNovaCloud](https://api.python.langchain.com/en/latest/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html) | [langchain-community](https://python.langchain.com/v0.2/api_reference/community/index.html) | ❌ | ❌ | ❌ |  |  |\n",
|
||||
"| [ChatSambaNovaCloud](https://python.langchain.com/api_reference/community/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html) | [langchain-community](https://python.langchain.com/api_reference/community/index.html) | ❌ | ❌ | ❌ |  |  |\n",
|
||||
"\n",
|
||||
"### Model features\n",
|
||||
"\n",
|
||||
"| [Tool calling](/docs/how_to/tool_calling) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n",
|
||||
"| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | \n",
|
||||
"| ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | \n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
@@ -116,14 +116,18 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models.sambanova import ChatSambaNovaCloud\n",
|
||||
"\n",
|
||||
"llm = ChatSambaNovaCloud(\n",
|
||||
" model=\"llama3-405b\", max_tokens=1024, temperature=0.7, top_k=1, top_p=0.01\n",
|
||||
" model=\"Meta-Llama-3.1-70B-Instruct\",\n",
|
||||
" max_tokens=1024,\n",
|
||||
" temperature=0.7,\n",
|
||||
" top_k=1,\n",
|
||||
" top_p=0.01,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -142,7 +146,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content=\"J'adore la programmation.\", response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 11, 'completion_tokens': 9, 'completion_tokens_after_first_per_sec': 97.07042823956884, 'completion_tokens_after_first_per_sec_first_ten': 276.3343994441849, 'completion_tokens_per_sec': 23.775192800224037, 'end_time': 1726158364.7954874, 'is_last_response': True, 'prompt_tokens': 56, 'start_time': 1726158364.3670964, 'time_to_first_token': 0.3459765911102295, 'total_latency': 0.3785458261316473, 'total_tokens': 65, 'total_tokens_per_sec': 171.70972577939582}, 'model_name': 'Meta-Llama-3.1-405B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1726158364}, id='7154b676-9d5a-4b1a-a425-73bbe69f28fc')"
|
||||
"AIMessage(content=\"J'adore la programmation.\", additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 7, 'completion_tokens': 8, 'completion_tokens_after_first_per_sec': 195.0204119588971, 'completion_tokens_after_first_per_sec_first_ten': 618.3422770734173, 'completion_tokens_per_sec': 53.25837044790076, 'end_time': 1731535338.1864908, 'is_last_response': True, 'prompt_tokens': 55, 'start_time': 1731535338.0133238, 'time_to_first_token': 0.13727331161499023, 'total_latency': 0.15021112986973353, 'total_tokens': 63, 'total_tokens_per_sec': 419.4096672772185}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731535338}, id='f04b7c2c-bc46-47e0-9c6b-19a002e8f390')"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
@@ -196,7 +200,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='Ich liebe Programmieren.', response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 11, 'completion_tokens': 6, 'completion_tokens_after_first_per_sec': 47.80258530102961, 'completion_tokens_after_first_per_sec_first_ten': 215.59002827036753, 'completion_tokens_per_sec': 5.263977583489829, 'end_time': 1726158506.3777263, 'is_last_response': True, 'prompt_tokens': 51, 'start_time': 1726158505.1611376, 'time_to_first_token': 1.1119918823242188, 'total_latency': 1.1398224830627441, 'total_tokens': 57, 'total_tokens_per_sec': 50.00778704315337}, 'model_name': 'Meta-Llama-3.1-405B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1726158505}, id='226471ac-8c52-44bb-baa7-f9d2f8c54477')"
|
||||
"AIMessage(content='Ich liebe das Programmieren.', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 2.3333333333333335, 'completion_tokens': 6, 'completion_tokens_after_first_per_sec': 106.06729752831038, 'completion_tokens_after_first_per_sec_first_ten': 204.92722183833433, 'completion_tokens_per_sec': 26.32497272023831, 'end_time': 1731535339.9997504, 'is_last_response': True, 'prompt_tokens': 50, 'start_time': 1731535339.7539687, 'time_to_first_token': 0.19864177703857422, 'total_latency': 0.22792046410696848, 'total_tokens': 56, 'total_tokens_per_sec': 245.6997453888909}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731535339}, id='dfe0bee6-b297-472e-ac9d-29906d162dcb')"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
@@ -243,17 +247,24 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Yer lookin' fer some info on owls, eh? Alright then, matey, settle yerself down with a pint o' grog and listen close.\n",
|
||||
"Yer lookin' fer some knowledge about owls, eh? Alright then, matey, settle yerself down with a pint o' grog and listen close. \n",
|
||||
"\n",
|
||||
"Owls be nocturnal birds o' prey, meanin' they do most o' their huntin' at night. They got big, round eyes that be perfect fer seein' in the dark, like a trusty lantern on a dark sea. Their ears be sharp as a cutlass, too, helpin' 'em pinpoint the slightest sound o' a scurvy rodent scurryin' through the underbrush.\n",
|
||||
"Owls be a fascinatin' lot, with their big round eyes and silent wings. They be birds o' prey, which means they hunt other creatures fer food. There be over 220 species o' owls, rangin' in size from the tiny Elf Owl (which be smaller than a parrot) to the Great Grey Owl (which be as big as a small eagle).\n",
|
||||
"\n",
|
||||
"These birds be known fer their silent flight, like a ghost ship sailin' through the night. Their feathers be special, with a soft, fringed edge that helps 'em sneak up on their prey. And when they strike, it be swift and deadly, like a pirate's sword.\n",
|
||||
"One o' the most interestin' things about owls be their eyes. They be huge, with some species havin' eyes that be as big as their brains! This lets 'em see in the dark, which be perfect fer nocturnal huntin'. They also have special feathers on their faces that help 'em hear better, and their ears be specially designed to pinpoint sounds.\n",
|
||||
"\n",
|
||||
"Owls be found all over the world, from the frozen tundras o' the north to the scorching deserts o' the south. They come in all shapes and sizes, from the tiny elf owl to the great grey owl, which be as big as a small dog.\n",
|
||||
"Owls be known fer their silent flight, which be due to the special shape o' their wings. They be able to fly without makin' a sound, which be perfect fer sneakin' up on prey. They also be very agile, with some species able to fly through tight spaces and make sharp turns.\n",
|
||||
"\n",
|
||||
"Now, I know what ye be thinkin', \"Pirate, what about their hootin'?\" Aye, owls be famous fer their hoots, which be a form o' communication. They use different hoots to warn off predators, attract a mate, or even just to say, \"Shiver me timbers, I be happy to be alive!\"\n",
|
||||
"Some o' the most common species o' owls include:\n",
|
||||
"\n",
|
||||
"So there ye have it, me hearty. Owls be fascinatin' creatures, and I hope ye found this info as interestin' as a chest overflowin' with gold doubloons. Fair winds and following seas!"
|
||||
"* Barn Owl: A medium-sized owl with a heart-shaped face and a screechin' call.\n",
|
||||
"* Tawny Owl: A large owl with a distinctive hootin' call and a reddish-brown plumage.\n",
|
||||
"* Great Horned Owl: A big owl with ear tufts and a deep hootin' call.\n",
|
||||
"* Snowy Owl: A white owl with a round face and a soft, hootin' call.\n",
|
||||
"\n",
|
||||
"Owls be found all over the world, in a variety o' habitats, from forests to deserts. They be an important part o' many ecosystems, helpin' to keep populations o' small mammals and birds under control.\n",
|
||||
"\n",
|
||||
"So there ye have it, matey! Owls be amazin' creatures, with their big eyes, silent wings, and sharp talons. Now go forth and spread the word about these fascinatin' birds!"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -283,7 +294,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"AIMessage(content='The capital of France is Paris.', response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 13, 'completion_tokens': 8, 'completion_tokens_after_first_per_sec': 86.00726488715989, 'completion_tokens_after_first_per_sec_first_ten': 326.92555640828857, 'completion_tokens_per_sec': 21.74539360394493, 'end_time': 1726159287.9987085, 'is_last_response': True, 'prompt_tokens': 43, 'start_time': 1726159287.5738964, 'time_to_first_token': 0.34342360496520996, 'total_latency': 0.36789400760944074, 'total_tokens': 51, 'total_tokens_per_sec': 138.62688422514893}, 'model_name': 'Meta-Llama-3.1-405B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1726159287}, id='9b4ef015-50a2-434b-b980-29f8aa90c3e8')"
|
||||
"AIMessage(content='The capital of France is Paris.', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 1, 'completion_tokens': 7, 'completion_tokens_after_first_per_sec': 442.126212227688, 'completion_tokens_after_first_per_sec_first_ten': 0, 'completion_tokens_per_sec': 46.28540439646366, 'end_time': 1731535343.0321083, 'is_last_response': True, 'prompt_tokens': 42, 'start_time': 1731535342.8808727, 'time_to_first_token': 0.137664794921875, 'total_latency': 0.15123558044433594, 'total_tokens': 49, 'total_tokens_per_sec': 323.99783077524563}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731535342}, id='c4b8c714-df38-4206-9aa8-fc8231f7275a')"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
@@ -321,7 +332,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Quantum computers use quantum bits (qubits) to process vast amounts of data simultaneously, leveraging quantum mechanics to solve complex problems exponentially faster than classical computers."
|
||||
"Quantum computers use quantum bits (qubits) to process info, leveraging superposition and entanglement to perform calculations exponentially faster than classical computers for certain complex problems."
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -340,13 +351,202 @@
|
||||
" print(chunk.content, end=\"\", flush=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tool calling"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from langchain_core.messages import HumanMessage, ToolMessage\n",
|
||||
"from langchain_core.tools import tool\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@tool\n",
|
||||
"def get_time(kind: str = \"both\") -> str:\n",
|
||||
" \"\"\"Returns current date, current time or both.\n",
|
||||
" Args:\n",
|
||||
" kind(str): date, time or both\n",
|
||||
" Returns:\n",
|
||||
" str: current date, current time or both\n",
|
||||
" \"\"\"\n",
|
||||
" if kind == \"date\":\n",
|
||||
" date = datetime.now().strftime(\"%m/%d/%Y\")\n",
|
||||
" return f\"Current date: {date}\"\n",
|
||||
" elif kind == \"time\":\n",
|
||||
" time = datetime.now().strftime(\"%H:%M:%S\")\n",
|
||||
" return f\"Current time: {time}\"\n",
|
||||
" else:\n",
|
||||
" date = datetime.now().strftime(\"%m/%d/%Y\")\n",
|
||||
" time = datetime.now().strftime(\"%H:%M:%S\")\n",
|
||||
" return f\"Current date: {date}, Current time: {time}\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tools = [get_time]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def invoke_tools(tool_calls, messages):\n",
|
||||
" available_functions = {tool.name: tool for tool in tools}\n",
|
||||
" for tool_call in tool_calls:\n",
|
||||
" selected_tool = available_functions[tool_call[\"name\"]]\n",
|
||||
" tool_output = selected_tool.invoke(tool_call[\"args\"])\n",
|
||||
" print(f\"Tool output: {tool_output}\")\n",
|
||||
" messages.append(ToolMessage(tool_output, tool_call_id=tool_call[\"id\"]))\n",
|
||||
" return messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm_with_tools = llm.bind_tools(tools=tools)\n",
|
||||
"messages = [\n",
|
||||
" HumanMessage(\n",
|
||||
" content=\"I need to schedule a meeting for two weeks from today. Can you tell me the exact date of the meeting?\"\n",
|
||||
" )\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Intermediate model response: [{'name': 'get_time', 'args': {'kind': 'date'}, 'id': 'call_7352ce7a18e24a7c9d', 'type': 'tool_call'}]\n",
|
||||
"Tool output: Current date: 11/13/2024\n",
|
||||
"final response: The meeting should be scheduled for two weeks from November 13th, 2024.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"response = llm_with_tools.invoke(messages)\n",
|
||||
"while len(response.tool_calls) > 0:\n",
|
||||
" print(f\"Intermediate model response: {response.tool_calls}\")\n",
|
||||
" messages.append(response)\n",
|
||||
" messages = invoke_tools(response.tool_calls, messages)\n",
|
||||
" response = llm_with_tools.invoke(messages)\n",
|
||||
"\n",
|
||||
"print(f\"final response: {response.content}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Structured Outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Joke(setup='Why did the cat join a band?', punchline='Because it wanted to be the purr-cussionist!')"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Joke(BaseModel):\n",
|
||||
" \"\"\"Joke to tell user.\"\"\"\n",
|
||||
"\n",
|
||||
" setup: str = Field(description=\"The setup of the joke\")\n",
|
||||
" punchline: str = Field(description=\"The punchline to the joke\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"structured_llm = llm.with_structured_output(Joke)\n",
|
||||
"\n",
|
||||
"structured_llm.invoke(\"Tell me a joke about cats\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Input Image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"multimodal_llm = ChatSambaNovaCloud(\n",
|
||||
" model=\"Llama-3.2-11B-Vision-Instruct\",\n",
|
||||
" max_tokens=1024,\n",
|
||||
" temperature=0.7,\n",
|
||||
" top_k=1,\n",
|
||||
" top_p=0.01,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"The weather in this image is a serene and peaceful atmosphere, with a blue sky and white clouds, suggesting a pleasant day with mild temperatures and gentle breezes.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"\n",
|
||||
"import httpx\n",
|
||||
"\n",
|
||||
"image_url = (\n",
|
||||
" \"https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg\"\n",
|
||||
")\n",
|
||||
"image_data = base64.b64encode(httpx.get(image_url).content).decode(\"utf-8\")\n",
|
||||
"\n",
|
||||
"message = HumanMessage(\n",
|
||||
" content=[\n",
|
||||
" {\"type\": \"text\", \"text\": \"describe the weather in this image in 1 sentence\"},\n",
|
||||
" {\n",
|
||||
" \"type\": \"image_url\",\n",
|
||||
" \"image_url\": {\"url\": f\"data:image/jpeg;base64,{image_data}\"},\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
")\n",
|
||||
"response = multimodal_llm.invoke([message])\n",
|
||||
"print(response.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatSambaNovaCloud features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html"
|
||||
"For detailed documentation of all ChatSambaNovaCloud features and configurations head to the API reference: https://python.langchain.com/api_reference/community/chat_models/langchain_community.chat_models.sambanova.ChatSambaNovaCloud.html"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -366,7 +566,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.19"
|
||||
"version": "3.9.20"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
268
docs/docs/integrations/llms/outlines.ipynb
Normal file
268
docs/docs/integrations/llms/outlines.ipynb
Normal file
@@ -0,0 +1,268 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Outlines\n",
|
||||
"\n",
|
||||
"This will help you getting started with Outlines LLM. For detailed documentation of all Outlines features and configurations head to the [API reference](https://api.python.langchain.com/en/latest/llms/outlines.llms.Outlines.html).\n",
|
||||
"\n",
|
||||
"[Outlines](https://github.com/outlines-dev/outlines) is a library for constrained language generation. It allows you to use large language models (LLMs) with various backends while applying constraints to the generated output.\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"### Integration details\n",
|
||||
"| Class | Package | Local | Serializable | JS support | Package downloads | Package latest |\n",
|
||||
"| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n",
|
||||
"| [Outlines](https://python.langchain.com/api_reference/community/llms/langchain_community.llms.outlines.Outlines.html) | [langchain-community](https://python.langchain.com/api_reference/community/index.html) | ✅ | beta | ❌ |  |  |\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"To access Outlines models you'll need to have an internet connection to download the model weights from huggingface. Depending on the backend you need to install the required dependencies (see [Outlines docs](https://dottxt-ai.github.io/outlines/latest/installation/))\n",
|
||||
"\n",
|
||||
"### Credentials\n",
|
||||
"\n",
|
||||
"There is no built-in auth mechanism for Outlines.\n",
|
||||
"\n",
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"The LangChain Outlines integration lives in the `langchain-community` package and requires the `outlines` library:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"vscode": {
|
||||
"languageId": "shellscript"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-community outlines"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
"Now we can instantiate our model object and generate chat completions:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.llms import Outlines\n",
|
||||
"\n",
|
||||
"# For use with llamacpp backend\n",
|
||||
"model = Outlines(model=\"microsoft/Phi-3-mini-4k-instruct\", backend=\"llamacpp\")\n",
|
||||
"\n",
|
||||
"# For use with vllm backend (not available on Mac)\n",
|
||||
"model = Outlines(model=\"microsoft/Phi-3-mini-4k-instruct\", backend=\"vllm\")\n",
|
||||
"\n",
|
||||
"# For use with mlxlm backend (only available on Mac)\n",
|
||||
"model = Outlines(model=\"microsoft/Phi-3-mini-4k-instruct\", backend=\"mlxlm\")\n",
|
||||
"\n",
|
||||
"# For use with huggingface transformers backend\n",
|
||||
"model = Outlines(\n",
|
||||
" model=\"microsoft/Phi-3-mini-4k-instruct\"\n",
|
||||
") # defaults to backend=\"transformers\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.invoke(\"Hello how are you?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Chaining"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_core.prompts import PromptTemplate\n",
|
||||
"\n",
|
||||
"prompt = PromptTemplate.from_template(\"How to say {input} in {output_language}:\\n\")\n",
|
||||
"\n",
|
||||
"chain = prompt | model\n",
|
||||
"chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"output_language\": \"German\",\n",
|
||||
" \"input\": \"I love programming.\",\n",
|
||||
" }\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Streaming\n",
|
||||
"\n",
|
||||
"Outlines supports streaming of tokens:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for chunk in model.stream(\"Count to 10 in French:\"):\n",
|
||||
" print(chunk, end=\"\", flush=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Constrained Generation\n",
|
||||
"\n",
|
||||
"Outlines allows you to apply various constraints to the generated output:\n",
|
||||
"\n",
|
||||
"#### Regex Constraint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.regex = r\"((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\"\n",
|
||||
"response = model.invoke(\"What is the IP address of Google's DNS server?\")\n",
|
||||
"\n",
|
||||
"response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Type Constraints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.type_constraints = int\n",
|
||||
"response = model.invoke(\"What is the answer to life, the universe, and everything?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### JSON Schema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Person(BaseModel):\n",
|
||||
" name: str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"model.json_schema = Person\n",
|
||||
"response = model.invoke(\"Who is the author of LangChain?\")\n",
|
||||
"person = Person.model_validate_json(response)\n",
|
||||
"\n",
|
||||
"person"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Grammar Constraint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model.grammar = \"\"\"\n",
|
||||
"?start: expression\n",
|
||||
"?expression: term ((\"+\" | \"-\") term)\n",
|
||||
"?term: factor ((\"\" | \"/\") factor)\n",
|
||||
"?factor: NUMBER | \"-\" factor | \"(\" expression \")\"\n",
|
||||
"%import common.NUMBER\n",
|
||||
"%import common.WS\n",
|
||||
"%ignore WS\n",
|
||||
"\"\"\"\n",
|
||||
"response = model.invoke(\"Give me a complex arithmetic expression:\")\n",
|
||||
"\n",
|
||||
"response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all ChatOutlines features and configurations head to the API reference: https://api.python.langchain.com/en/latest/chat_models/outlines.chat_models.ChatOutlines.html\n",
|
||||
"\n",
|
||||
"## Outlines Documentation: \n",
|
||||
"\n",
|
||||
"https://dottxt-ai.github.io/outlines/latest/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
201
docs/docs/integrations/providers/outlines.mdx
Normal file
201
docs/docs/integrations/providers/outlines.mdx
Normal file
@@ -0,0 +1,201 @@
|
||||
# Outlines
|
||||
|
||||
>[Outlines](https://github.com/dottxt-ai/outlines) is a Python library for constrained language generation. It provides a unified interface to various language models and allows for structured generation using techniques like regex matching, type constraints, JSON schemas, and context-free grammars.
|
||||
|
||||
Outlines supports multiple backends, including:
|
||||
- Hugging Face Transformers
|
||||
- llama.cpp
|
||||
- vLLM
|
||||
- MLX
|
||||
|
||||
This integration allows you to use Outlines models with LangChain, providing both LLM and chat model interfaces.
|
||||
|
||||
## Installation and Setup
|
||||
|
||||
To use Outlines with LangChain, you'll need to install the Outlines library:
|
||||
|
||||
```bash
|
||||
pip install outlines
|
||||
```
|
||||
|
||||
Depending on the backend you choose, you may need to install additional dependencies:
|
||||
|
||||
- For Transformers: `pip install transformers torch datasets`
|
||||
- For llama.cpp: `pip install llama-cpp-python`
|
||||
- For vLLM: `pip install vllm`
|
||||
- For MLX: `pip install mlx`
|
||||
|
||||
## LLM
|
||||
|
||||
To use Outlines as an LLM in LangChain, you can use the `Outlines` class:
|
||||
|
||||
```python
|
||||
from langchain_community.llms import Outlines
|
||||
```
|
||||
|
||||
## Chat Models
|
||||
|
||||
To use Outlines as a chat model in LangChain, you can use the `ChatOutlines` class:
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatOutlines
|
||||
```
|
||||
|
||||
## Model Configuration
|
||||
|
||||
Both `Outlines` and `ChatOutlines` classes share similar configuration options:
|
||||
|
||||
```python
|
||||
model = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf", # Model identifier
|
||||
backend="transformers", # Backend to use (transformers, llamacpp, vllm, or mlxlm)
|
||||
max_tokens=256, # Maximum number of tokens to generate
|
||||
stop=["\n"], # Optional list of stop strings
|
||||
streaming=True, # Whether to stream the output
|
||||
# Additional parameters for structured generation:
|
||||
regex=None,
|
||||
type_constraints=None,
|
||||
json_schema=None,
|
||||
grammar=None,
|
||||
# Additional model parameters:
|
||||
model_kwargs={"temperature": 0.7}
|
||||
)
|
||||
```
|
||||
|
||||
### Model Identifier
|
||||
|
||||
The `model` parameter can be:
|
||||
- A Hugging Face model name (e.g., "meta-llama/Llama-2-7b-chat-hf")
|
||||
- A local path to a model
|
||||
- For GGUF models, the format is "repo_id/file_name" (e.g., "TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf")
|
||||
|
||||
### Backend Options
|
||||
|
||||
The `backend` parameter specifies which backend to use:
|
||||
- `"transformers"`: For Hugging Face Transformers models (default)
|
||||
- `"llamacpp"`: For GGUF models using llama.cpp
|
||||
- `"transformers_vision"`: For vision-language models (e.g., LLaVA)
|
||||
- `"vllm"`: For models using the vLLM library
|
||||
- `"mlxlm"`: For models using the MLX framework
|
||||
|
||||
### Structured Generation
|
||||
|
||||
Outlines provides several methods for structured generation:
|
||||
|
||||
1. **Regex Matching**:
|
||||
```python
|
||||
model = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
regex=r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
|
||||
)
|
||||
```
|
||||
This will ensure the generated text matches the specified regex pattern (in this case, a valid IP address).
|
||||
|
||||
2. **Type Constraints**:
|
||||
```python
|
||||
model = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
type_constraints=int
|
||||
)
|
||||
```
|
||||
This restricts the output to valid Python types (int, float, bool, datetime.date, datetime.time, datetime.datetime).
|
||||
|
||||
3. **JSON Schema**:
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
model = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
json_schema=Person
|
||||
)
|
||||
```
|
||||
This ensures the generated output adheres to the specified JSON schema or Pydantic model.
|
||||
|
||||
4. **Context-Free Grammar**:
|
||||
```python
|
||||
model = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
grammar="""
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
"""
|
||||
)
|
||||
```
|
||||
This generates text that adheres to the specified context-free grammar in EBNF format.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### LLM Example
|
||||
|
||||
```python
|
||||
from langchain_community.llms import Outlines
|
||||
|
||||
llm = Outlines(model="meta-llama/Llama-2-7b-chat-hf", max_tokens=100)
|
||||
result = llm.invoke("Tell me a short story about a robot.")
|
||||
print(result)
|
||||
```
|
||||
|
||||
### Chat Model Example
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatOutlines
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
chat = ChatOutlines(model="meta-llama/Llama-2-7b-chat-hf", max_tokens=100)
|
||||
messages = [
|
||||
SystemMessage(content="You are a helpful AI assistant."),
|
||||
HumanMessage(content="What's the capital of France?")
|
||||
]
|
||||
result = chat.invoke(messages)
|
||||
print(result.content)
|
||||
```
|
||||
|
||||
### Streaming Example
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatOutlines
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
chat = ChatOutlines(model="meta-llama/Llama-2-7b-chat-hf", streaming=True)
|
||||
for chunk in chat.stream("Tell me a joke about programming."):
|
||||
print(chunk.content, end="", flush=True)
|
||||
print()
|
||||
```
|
||||
|
||||
### Structured Output Example
|
||||
|
||||
```python
|
||||
from langchain_community.llms import Outlines
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MovieReview(BaseModel):
|
||||
title: str
|
||||
rating: int
|
||||
summary: str
|
||||
|
||||
llm = Outlines(
|
||||
model="meta-llama/Llama-2-7b-chat-hf",
|
||||
json_schema=MovieReview
|
||||
)
|
||||
result = llm.invoke("Write a short review for the movie 'Inception'.")
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Additional Features
|
||||
|
||||
### Tokenizer Access
|
||||
|
||||
You can access the underlying tokenizer for the model:
|
||||
|
||||
```python
|
||||
tokenizer = llm.tokenizer
|
||||
encoded = tokenizer.encode("Hello, world!")
|
||||
decoded = tokenizer.decode(encoded)
|
||||
```
|
||||
@@ -9,7 +9,7 @@ For more info how to get access to GigaChat [follow here](https://developers.sbe
|
||||
GigaChat package can be installed via pip from PyPI:
|
||||
|
||||
```bash
|
||||
pip install gigachat
|
||||
pip install langchain-gigachat
|
||||
```
|
||||
|
||||
## LLMs
|
||||
@@ -25,7 +25,7 @@ from langchain_community.llms import GigaChat
|
||||
See a [usage example](/docs/integrations/chat/gigachat).
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import GigaChat
|
||||
from langchain_gigachat.chat_models import GigaChat
|
||||
```
|
||||
|
||||
## Embeddings
|
||||
@@ -33,5 +33,5 @@ from langchain_community.chat_models import GigaChat
|
||||
See a [usage example](/docs/integrations/text_embedding/gigachat).
|
||||
|
||||
```python
|
||||
from langchain_community.embeddings import GigaChatEmbeddings
|
||||
from langchain_gigachat.embeddings import GigaChatEmbeddings
|
||||
```
|
||||
@@ -136,7 +136,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.chains.query_constructor.base import AttributeInfo\n",
|
||||
"from langchain.chains.query_constructor.schema import AttributeInfo\n",
|
||||
"from langchain.retrievers.self_query.base import SelfQueryRetriever\n",
|
||||
"from langchain_openai import OpenAI\n",
|
||||
"\n",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": null,
|
||||
"id": "36521c2a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -53,8 +53,10 @@
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"OPENAI_API_KEY\"):\n",
|
||||
" os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Enter your AzureOpenAI API key: \")"
|
||||
"if not os.getenv(\"AZURE_OPENAI_API_KEY\"):\n",
|
||||
" os.environ[\"AZURE_OPENAI_API_KEY\"] = getpass.getpass(\n",
|
||||
" \"Enter your AzureOpenAI API key: \"\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"id": "282239c8-e03a-4abc-86c1-ca6120231a20",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.embeddings import BedrockEmbeddings\n",
|
||||
"from langchain_aws import BedrockEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = BedrockEmbeddings(\n",
|
||||
" credentials_profile_name=\"bedrock-admin\", region_name=\"us-east-1\"\n",
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
"collapsed": false,
|
||||
"pycharm": {
|
||||
"is_executing": true
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet gigachat"
|
||||
"%pip install --upgrade --quiet langchain-gigachat"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,13 +53,13 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.embeddings import GigaChatEmbeddings\n",
|
||||
"from langchain_gigachat import GigaChatEmbeddings\n",
|
||||
"\n",
|
||||
"embeddings = GigaChatEmbeddings(verify_ssl_certs=False, scope=\"GIGACHAT_API_PERS\")"
|
||||
]
|
||||
@@ -81,13 +84,7 @@
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[0.8398333191871643,\n",
|
||||
" -0.14180311560630798,\n",
|
||||
" -0.6161925792694092,\n",
|
||||
" -0.17103666067123413,\n",
|
||||
" 1.2884578704833984]"
|
||||
]
|
||||
"text/plain": "[0.8398333191871643,\n -0.14180311560630798,\n -0.6161925792694092,\n -0.17103666067123413,\n 1.2884578704833984]"
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
|
||||
@@ -11,6 +11,14 @@ import { CategoryTable, IndexTable } from "@theme/FeatureTables";
|
||||
|
||||
This page documents integrations with various model providers that allow you to use embeddings in LangChain.
|
||||
|
||||
import EmbeddingTabs from "@theme/EmbeddingTabs";
|
||||
|
||||
<EmbeddingTabs/>
|
||||
|
||||
```python
|
||||
embeddings.embed_query("Hello, world!")
|
||||
```
|
||||
|
||||
<CategoryTable category="text_embedding" />
|
||||
|
||||
## All embedding models
|
||||
|
||||
@@ -3,12 +3,24 @@ sidebar_position: 0
|
||||
sidebar_class_name: hidden
|
||||
---
|
||||
|
||||
# Vectorstores
|
||||
# Vector stores
|
||||
|
||||
import { CategoryTable, IndexTable } from "@theme/FeatureTables";
|
||||
|
||||
A [vector store](/docs/concepts/vectorstores) stores [embedded](/docs/concepts/embedding_models) data and performs similarity search.
|
||||
|
||||
**Select embedding model:**
|
||||
|
||||
import EmbeddingTabs from "@theme/EmbeddingTabs";
|
||||
|
||||
<EmbeddingTabs/>
|
||||
|
||||
**Select vector store:**
|
||||
|
||||
import VectorStoreTabs from "@theme/VectorStoreTabs";
|
||||
|
||||
<VectorStoreTabs/>
|
||||
|
||||
<CategoryTable category="vectorstores" />
|
||||
|
||||
## All Vectorstores
|
||||
|
||||
@@ -33,7 +33,7 @@ Concretely, the framework consists of the following open-source libraries:
|
||||
- **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture.
|
||||
- **`langchain-community`**: Third-party integrations that are community maintained.
|
||||
- **[LangGraph](https://langchain-ai.github.io/langgraph)**: Build robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. Integrates smoothly with LangChain, but can be used without it.
|
||||
- **[LangServe](/docs/langserve)**: Deploy LangChain chains as REST APIs.
|
||||
- **[LangGraphPlatform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform)**: Deploy LLM applications built with LangGraph to production.
|
||||
- **[LangSmith](https://docs.smith.langchain.com)**: A developer platform that lets you debug, test, evaluate, and monitor LLM applications.
|
||||
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
"source": [
|
||||
"## Conclusion\n",
|
||||
"\n",
|
||||
"That's it! In this tutorial you've learned how to create your first simple LLM application. You've learned how to work with language models, how to how to create a prompt template, and how to get great observability into chains you create with LangSmith.\n",
|
||||
"That's it! In this tutorial you've learned how to create your first simple LLM application. You've learned how to work with language models, how to create a prompt template, and how to get great observability into chains you create with LangSmith.\n",
|
||||
"\n",
|
||||
"This just scratches the surface of what you will want to learn to become a proficient AI Engineer. Luckily - we've got a lot of other resources!\n",
|
||||
"\n",
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install --upgrade --quiet tiktoken langchain langgraph beautifulsoup4\n",
|
||||
"%pip install --upgrade --quiet tiktoken langchain langgraph beautifulsoup4 langchain-community\n",
|
||||
"\n",
|
||||
"# Set env var OPENAI_API_KEY or load from a .env file\n",
|
||||
"# import dotenv\n",
|
||||
|
||||
@@ -132,7 +132,7 @@ should ensure that they are passing Pydantic 2 objects to these APIs rather than
|
||||
Pydantic 1 objects (created via the `pydantic.v1` namespace of pydantic 2).
|
||||
|
||||
:::caution
|
||||
While `v1` objets may be accepted by some of these APIs, users are advised to
|
||||
While `v1` objects may be accepted by some of these APIs, users are advised to
|
||||
use Pydantic 2 objects to avoid future issues.
|
||||
:::
|
||||
|
||||
|
||||
@@ -184,7 +184,18 @@ if __name__ == "__main__":
|
||||
source_paths_stripped = [p.strip() for p in source_path_strs]
|
||||
source_paths = [intermediate_docs_dir / p for p in source_paths_stripped if p]
|
||||
else:
|
||||
source_paths = intermediate_docs_dir.glob("**/*.ipynb")
|
||||
original_paths = list(intermediate_docs_dir.glob("**/*.ipynb"))
|
||||
# exclude files that exist in output directory and are newer
|
||||
relative_paths = [p.relative_to(intermediate_docs_dir) for p in original_paths]
|
||||
out_paths = [
|
||||
output_docs_dir / p.parent / (p.stem + ".md") for p in relative_paths
|
||||
]
|
||||
source_paths = [
|
||||
p
|
||||
for p, o in zip(original_paths, out_paths)
|
||||
if not o.exists() or o.stat().st_mtime < p.stat().st_mtime
|
||||
]
|
||||
print(f"rebuilding {len(source_paths)}/{len(relative_paths)} notebooks")
|
||||
|
||||
with multiprocessing.Pool() as pool:
|
||||
pool.map(
|
||||
|
||||
@@ -204,34 +204,34 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "LLMs",
|
||||
label: "Retrievers",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/llms",
|
||||
dirName: "integrations/retrievers",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/llms/index",
|
||||
id: "integrations/retrievers/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Embedding models",
|
||||
label: "Tools/Toolkits",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/text_embedding",
|
||||
dirName: "integrations/tools",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/text_embedding/index",
|
||||
id: "integrations/tools/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -268,50 +268,18 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Retrievers",
|
||||
label: "Embedding models",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/retrievers",
|
||||
dirName: "integrations/text_embedding",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/retrievers/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Tools/Toolkits",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/tools",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/tools/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Key-value stores",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/stores",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/stores/index",
|
||||
id: "integrations/text_embedding/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -319,6 +287,38 @@ module.exports = {
|
||||
label: "Other",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
type: "category",
|
||||
label: "LLMs",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/llms",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/llms/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Key-value stores",
|
||||
collapsible: false,
|
||||
items: [
|
||||
{
|
||||
type: "autogenerated",
|
||||
dirName: "integrations/stores",
|
||||
className: "hidden",
|
||||
},
|
||||
],
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "integrations/stores/index",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Document transformers",
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function ChatModelTabs(props) {
|
||||
azureParams ??
|
||||
`\n azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],\n azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],\n openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],\n`;
|
||||
const nvidiaParamsOrDefault = nvidiaParams ?? `model="meta/llama3-70b-instruct"`
|
||||
const awsBedrockParamsOrDefault = awsBedrockParams ?? `model_id="anthropic.claude-3-5-sonnet-20240620-v1:0"`;
|
||||
const awsBedrockParamsOrDefault = awsBedrockParams ?? `model="anthropic.claude-3-5-sonnet-20240620-v1:0",\n beta_use_converse_api=True`;
|
||||
|
||||
const llmVarName = customVarName ?? "model";
|
||||
|
||||
@@ -114,11 +114,20 @@ export default function ChatModelTabs(props) {
|
||||
value: "Google",
|
||||
label: "Google",
|
||||
text: `from langchain_google_vertexai import ChatVertexAI\n\n${llmVarName} = ChatVertexAI(${googleParamsOrDefault})`,
|
||||
apiKeyName: "GOOGLE_API_KEY",
|
||||
apiKeyText: "# Ensure your VertexAI credentials are configured",
|
||||
packageName: "langchain-google-vertexai",
|
||||
default: false,
|
||||
shouldHide: hideGoogle,
|
||||
},
|
||||
{
|
||||
value: "AWS",
|
||||
label: "AWS",
|
||||
text: `from langchain_aws import ChatBedrock\n\n${llmVarName} = ChatBedrock(${awsBedrockParamsOrDefault})`,
|
||||
apiKeyText: "# Ensure your AWS credentials are configured",
|
||||
packageName: "langchain-aws",
|
||||
default: false,
|
||||
shouldHide: hideAWS,
|
||||
},
|
||||
{
|
||||
value: "Cohere",
|
||||
label: "Cohere",
|
||||
@@ -173,15 +182,6 @@ export default function ChatModelTabs(props) {
|
||||
default: false,
|
||||
shouldHide: hideTogether,
|
||||
},
|
||||
{
|
||||
value: "AWS",
|
||||
label: "AWS",
|
||||
text: `from langchain_aws import ChatBedrock\n\n${llmVarName} = ChatBedrock(${awsBedrockParamsOrDefault})`,
|
||||
apiKeyText: "# Ensure your AWS credentials are configured",
|
||||
packageName: "langchain-aws",
|
||||
default: false,
|
||||
shouldHide: hideAWS,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,15 +7,41 @@ export default function EmbeddingTabs(props) {
|
||||
const {
|
||||
openaiParams,
|
||||
hideOpenai,
|
||||
azureOpenaiParams,
|
||||
hideAzureOpenai,
|
||||
googleParams,
|
||||
hideGoogle,
|
||||
awsParams,
|
||||
hideAws,
|
||||
huggingFaceParams,
|
||||
hideHuggingFace,
|
||||
ollamaParams,
|
||||
hideOllama,
|
||||
cohereParams,
|
||||
hideCohere,
|
||||
mistralParams,
|
||||
hideMistral,
|
||||
nomicParams,
|
||||
hideNomic,
|
||||
nvidiaParams,
|
||||
hideNvidia,
|
||||
fakeEmbeddingParams,
|
||||
hideFakeEmbedding,
|
||||
customVarName,
|
||||
} = props;
|
||||
|
||||
const openAIParamsOrDefault = openaiParams ?? `model="text-embedding-3-large"`;
|
||||
const azureParamsOrDefault =
|
||||
azureOpenaiParams ??
|
||||
`\n azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],\n azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],\n openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],\n`;
|
||||
const googleParamsOrDefault = googleParams ?? `model="text-embedding-004"`;
|
||||
const awsParamsOrDefault = awsParams ?? `model_id="amazon.titan-embed-text-v2:0"`;
|
||||
const huggingFaceParamsOrDefault = huggingFaceParams ?? `model="sentence-transformers/all-mpnet-base-v2"`;
|
||||
const ollamaParamsOrDefault = ollamaParams ?? `model="llama3"`;
|
||||
const cohereParamsOrDefault = cohereParams ?? `model="embed-english-v3.0"`;
|
||||
const mistralParamsOrDefault = mistralParams ?? `model="mistral-embed"`;
|
||||
const nomicsParamsOrDefault = nomicParams ?? `model="nomic-embed-text-v1.5"`;
|
||||
const nvidiaParamsOrDefault = nvidiaParams ?? `model="NV-Embed-QA"`;
|
||||
const fakeEmbeddingParamsOrDefault = fakeEmbeddingParams ?? `size=4096`;
|
||||
|
||||
const embeddingVarName = customVarName ?? "embeddings";
|
||||
@@ -30,6 +56,33 @@ export default function EmbeddingTabs(props) {
|
||||
default: true,
|
||||
shouldHide: hideOpenai,
|
||||
},
|
||||
{
|
||||
value: "Azure",
|
||||
label: "Azure",
|
||||
text: `from langchain_openai import AzureOpenAIEmbeddings\n\n${embeddingVarName} = AzureOpenAIEmbeddings(${azureParamsOrDefault})`,
|
||||
apiKeyName: "AZURE_OPENAI_API_KEY",
|
||||
packageName: "langchain-openai",
|
||||
default: false,
|
||||
shouldHide: hideAzureOpenai,
|
||||
},
|
||||
{
|
||||
value: "Google",
|
||||
label: "Google",
|
||||
text: `from langchain_google_vertexai import VertexAIEmbeddings\n\n${embeddingVarName} = VertexAIEmbeddings(${googleParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-google-vertexai",
|
||||
default: false,
|
||||
shouldHide: hideGoogle,
|
||||
},
|
||||
{
|
||||
value: "AWS",
|
||||
label: "AWS",
|
||||
text: `from langchain_aws import BedrockEmbeddings\n\n${embeddingVarName} = BedrockEmbeddings(${awsParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-aws",
|
||||
default: false,
|
||||
shouldHide: hideAws,
|
||||
},
|
||||
{
|
||||
value: "HuggingFace",
|
||||
label: "HuggingFace",
|
||||
@@ -40,9 +93,54 @@ export default function EmbeddingTabs(props) {
|
||||
shouldHide: hideHuggingFace,
|
||||
},
|
||||
{
|
||||
value: "Fake Embedding",
|
||||
label: "Fake Embedding",
|
||||
text: `from langchain_core.embeddings import FakeEmbeddings\n\n${embeddingVarName} = FakeEmbeddings(${fakeEmbeddingParamsOrDefault})`,
|
||||
value: "Ollama",
|
||||
label: "Ollama",
|
||||
text: `from langchain_ollama import OllamaEmbeddings\n\n${embeddingVarName} = OllamaEmbeddings(${ollamaParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-ollama",
|
||||
default: false,
|
||||
shouldHide: hideOllama,
|
||||
},
|
||||
{
|
||||
value: "Cohere",
|
||||
label: "Cohere",
|
||||
text: `from langchain_cohere import CohereEmbeddings\n\n${embeddingVarName} = CohereEmbeddings(${cohereParamsOrDefault})`,
|
||||
apiKeyName: "COHERE_API_KEY",
|
||||
packageName: "langchain-cohere",
|
||||
default: false,
|
||||
shouldHide: hideCohere,
|
||||
},
|
||||
{
|
||||
value: "MistralAI",
|
||||
label: "MistralAI",
|
||||
text: `from langchain_mistralai import MistralAIEmbeddings\n\n${embeddingVarName} = MistralAIEmbeddings(${mistralParamsOrDefault})`,
|
||||
apiKeyName: "MISTRALAI_API_KEY",
|
||||
packageName: "langchain-mistralai",
|
||||
default: false,
|
||||
shouldHide: hideMistral,
|
||||
},
|
||||
{
|
||||
value: "Nomic",
|
||||
label: "Nomic",
|
||||
text: `from langchain_nomic import NomicEmbeddings\n\n${embeddingVarName} = NomicEmbeddings(${nomicsParamsOrDefault})`,
|
||||
apiKeyName: "NOMIC_API_KEY",
|
||||
packageName: "langchain-nomic",
|
||||
default: false,
|
||||
shouldHide: hideNomic,
|
||||
},
|
||||
{
|
||||
value: "NVIDIA",
|
||||
label: "NVIDIA",
|
||||
text: `from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings\n\n${embeddingVarName} = NVIDIAEmbeddings(${nvidiaParamsOrDefault})`,
|
||||
apiKeyName: "NVIDIA_API_KEY",
|
||||
packageName: "langchain-nvidia-ai-endpoints",
|
||||
default: false,
|
||||
shouldHide: hideNvidia,
|
||||
},
|
||||
{
|
||||
value: "Fake",
|
||||
label: "Fake",
|
||||
text: `from langchain_core.embeddings import DeterministicFakeEmbedding\n\n${embeddingVarName} = DeterministicFakeEmbedding(${fakeEmbeddingParamsOrDefault})`,
|
||||
apiKeyName: undefined,
|
||||
packageName: "langchain-core",
|
||||
default: false,
|
||||
@@ -55,9 +153,7 @@ export default function EmbeddingTabs(props) {
|
||||
{tabItems
|
||||
.filter((tabItem) => !tabItem.shouldHide)
|
||||
.map((tabItem) => {
|
||||
const apiKeyText = tabItem.apiKeyName ? `import getpass
|
||||
|
||||
os.environ["${tabItem.apiKeyName}"] = getpass.getpass()` : '';
|
||||
const apiKeyText = tabItem.apiKeyName ? `import getpass\n\nos.environ["${tabItem.apiKeyName}"] = getpass.getpass()` : '';
|
||||
return (
|
||||
<TabItem
|
||||
value={tabItem.value}
|
||||
|
||||
@@ -406,7 +406,7 @@ const FEATURE_TABLES = {
|
||||
},
|
||||
{
|
||||
name: "NVIDIA",
|
||||
link: "NVIDIAEmbeddings",
|
||||
link: "nvidia_ai_endpoints",
|
||||
package: "langchain-nvidia",
|
||||
apiLink: "https://python.langchain.com/api_reference/nvidia_ai_endpoints/embeddings/langchain_nvidia_ai_endpoints.embeddings.NVIDIAEmbeddings.html"
|
||||
},
|
||||
|
||||
92
docs/src/theme/VectorStoreTabs.js
Normal file
92
docs/src/theme/VectorStoreTabs.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import React from "react";
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
import CodeBlock from "@theme-original/CodeBlock";
|
||||
|
||||
export default function VectorStoreTabs(props) {
|
||||
const { customVarName } = props;
|
||||
|
||||
const vectorStoreVarName = customVarName ?? "vector_store";
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
value: "In-memory",
|
||||
label: "In-memory",
|
||||
text: `from langchain_core.vector_stores import InMemoryVectorStore\n\n${vectorStoreVarName} = InMemoryVectorStore(embeddings)`,
|
||||
packageName: "langchain-core",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
value: "AstraDB",
|
||||
label: "AstraDB",
|
||||
text: `from langchain_astradb import AstraDBVectorStore\n\n${vectorStoreVarName} = AstraDBVectorStore(\n embedding=embeddings,\n api_endpoint=ASTRA_DB_API_ENDPOINT,\n collection_name="astra_vector_langchain",\n token=ASTRA_DB_APPLICATION_TOKEN,\n namespace=ASTRA_DB_NAMESPACE,\n)`,
|
||||
packageName: "langchain-astradb",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "Chroma",
|
||||
label: "Chroma",
|
||||
text: `from langchain_chroma import Chroma\n\n${vectorStoreVarName} = Chroma(embedding_function=embeddings)`,
|
||||
packageName: "langchain-chroma",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "FAISS",
|
||||
label: "FAISS",
|
||||
text: `from langchain_community.vectorstores import FAISS\n\n${vectorStoreVarName} = FAISS(embedding_function=embeddings)`,
|
||||
packageName: "langchain-community",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "Milvus",
|
||||
label: "Milvus",
|
||||
text: `from langchain_milvus import Milvus\n\n${vectorStoreVarName} = Milvus(embedding_function=embeddings)`,
|
||||
packageName: "langchain-milvus",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "MongoDB",
|
||||
label: "MongoDB",
|
||||
text: `from langchain_mongodb import MongoDBAtlasVectorSearch\n\n${vectorStoreVarName} = MongoDBAtlasVectorSearch(\n embedding=embeddings,\n collection=MONGODB_COLLECTION,\n index_name=ATLAS_VECTOR_SEARCH_INDEX_NAME,\n relevance_score_fn="cosine",\n)`,
|
||||
packageName: "langchain-mongodb",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "PGVector",
|
||||
label: "PGVector",
|
||||
text: `from langchain_postgres import PGVector\n\n${vectorStoreVarName} = PGVector(\n embedding=embeddings,\n collection_name="my_docs",\n connection="postgresql+psycopg://...",\n)`,
|
||||
packageName: "langchain-postgres",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "Pinecone",
|
||||
label: "Pinecone",
|
||||
text: `from langchain_pinecone import PineconeVectorStore\nfrom pinecone import Pinecone\n\npc = Pinecone(api_key=...)\nindex = pc.Index(index_name)\n\n${vectorStoreVarName} = PineconeVectorStore(embedding=embeddings, index=index)`,
|
||||
packageName: "langchain-pinecone",
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
value: "Qdrant",
|
||||
label: "Qdrant",
|
||||
text: `from langchain_qdrant import QdrantVectorStore\nfrom qdrant_client import QdrantClient\n\nclient = QdrantClient(":memory:")\n${vectorStoreVarName} = QdrantVectorStore(\n client=client,\n collection_name="test",\n embedding=embeddings,\n)`,
|
||||
packageName: "langchain-qdrant",
|
||||
default: false,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Tabs groupId="vectorStoreTabs">
|
||||
{tabItems.map((tabItem) => (
|
||||
<TabItem
|
||||
key={tabItem.value}
|
||||
value={tabItem.value}
|
||||
label={tabItem.label}
|
||||
default={tabItem.default}
|
||||
>
|
||||
<CodeBlock language="bash">{`pip install -qU ${tabItem.packageName}`}</CodeBlock>
|
||||
<CodeBlock language="python">{tabItem.text}</CodeBlock>
|
||||
</TabItem>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
5
libs/cli/poetry.lock
generated
5
libs/cli/poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
@@ -270,6 +270,7 @@ description = "Python bindings for GritQL"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
files = [
|
||||
{file = "gritql-0.1.4-py2.py3-none-any.whl", hash = "sha256:a71d27c2bfb904d0150ccb0c28878ef640940b9540b6f1ef964133be1ddcb0e7"},
|
||||
{file = "gritql-0.1.4-py3-none-any.whl", hash = "sha256:6e9f8c638bbf3dda58222832c976c716a1ca02a920e7549df58bf1a0bb9ebeef"},
|
||||
{file = "gritql-0.1.4.tar.gz", hash = "sha256:487d0c1a98cb17cc9681121e53ac15f39e1cb87c4317a2ca1872e33d3d3a0a47"},
|
||||
]
|
||||
@@ -1365,4 +1366,4 @@ serve = []
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<4.0"
|
||||
content-hash = "733676371ee89686b906db3bc190d39128fb664bf34d9b3ccd66aac75c8cc2aa"
|
||||
content-hash = "ed6ba14db90499ff27fefaab277bb8b879a48e024f4dabf944d76d8f7375002b"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "langchain-cli"
|
||||
version = "0.0.31"
|
||||
version = "0.0.32"
|
||||
description = "CLI for interacting with LangChain"
|
||||
authors = ["Erick Friis <erick@langchain.dev>"]
|
||||
readme = "README.md"
|
||||
@@ -14,10 +14,10 @@ license = "MIT"
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<4.0"
|
||||
typer = { extras = ["all"], version = "^0.9.0" }
|
||||
gitpython = "^3.1.40"
|
||||
gitpython = "^3"
|
||||
langserve = { extras = ["all"], version = ">=0.0.51" }
|
||||
uvicorn = "^0.23.2"
|
||||
tomlkit = "^0.12.2"
|
||||
uvicorn = ">=0.23,<1.0"
|
||||
tomlkit = ">=0.12"
|
||||
gritql = "^0.1.1"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
|
||||
@@ -55,6 +55,7 @@ openai<2
|
||||
openapi-pydantic>=0.3.2,<0.4
|
||||
oracle-ads>=2.9.1,<3
|
||||
oracledb>=2.2.0,<3
|
||||
outlines[test]>=0.1.0,<0.2
|
||||
pandas>=2.0.1,<3
|
||||
pdfminer-six>=20221105,<20240706
|
||||
pgvector>=0.1.6,<0.2
|
||||
|
||||
@@ -143,6 +143,7 @@ if TYPE_CHECKING:
|
||||
from langchain_community.chat_models.openai import (
|
||||
ChatOpenAI,
|
||||
)
|
||||
from langchain_community.chat_models.outlines import ChatOutlines
|
||||
from langchain_community.chat_models.pai_eas_endpoint import (
|
||||
PaiEasChatEndpoint,
|
||||
)
|
||||
@@ -228,6 +229,7 @@ __all__ = [
|
||||
"ChatOCIModelDeploymentTGI",
|
||||
"ChatOllama",
|
||||
"ChatOpenAI",
|
||||
"ChatOutlines",
|
||||
"ChatPerplexity",
|
||||
"ChatReka",
|
||||
"ChatPremAI",
|
||||
@@ -294,6 +296,7 @@ _module_lookup = {
|
||||
"ChatOCIModelDeploymentTGI": "langchain_community.chat_models.oci_data_science",
|
||||
"ChatOllama": "langchain_community.chat_models.ollama",
|
||||
"ChatOpenAI": "langchain_community.chat_models.openai",
|
||||
"ChatOutlines": "langchain_community.chat_models.outlines",
|
||||
"ChatReka": "langchain_community.chat_models.reka",
|
||||
"ChatPerplexity": "langchain_community.chat_models.perplexity",
|
||||
"ChatSambaNovaCloud": "langchain_community.chat_models.sambanova",
|
||||
|
||||
@@ -13,6 +13,7 @@ from typing import (
|
||||
Type,
|
||||
)
|
||||
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManagerForLLMRun,
|
||||
CallbackManagerForLLMRun,
|
||||
@@ -113,6 +114,11 @@ def _convert_delta_to_message_chunk(
|
||||
return default_class(content=content) # type: ignore[call-arg]
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="0.3.5",
|
||||
removal="1.0",
|
||||
alternative_import="langchain_gigachat.GigaChat",
|
||||
)
|
||||
class GigaChat(_BaseGigaChat, BaseChatModel):
|
||||
"""`GigaChat` large language models API.
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ from langchain_core.messages import (
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.utils import convert_to_secret_str, get_from_env
|
||||
from pydantic import AliasChoices, Field, SecretStr, model_validator
|
||||
from pydantic import AliasChoices, ConfigDict, Field, SecretStr, model_validator
|
||||
from typing_extensions import Self
|
||||
|
||||
_DEFAULT_BASE_URL = "https://clovastudio.stream.ntruss.com"
|
||||
@@ -51,6 +51,12 @@ def _convert_chunk_to_message_chunk(
|
||||
role = message.get("role")
|
||||
content = message.get("content") or ""
|
||||
|
||||
if sse.event == "result":
|
||||
response_metadata = {}
|
||||
if "stopReason" in sse_data:
|
||||
response_metadata["stopReason"] = sse_data["stopReason"]
|
||||
return AIMessageChunk(content="", response_metadata=response_metadata)
|
||||
|
||||
if role == "user" or default_class == HumanMessageChunk:
|
||||
return HumanMessageChunk(content=content)
|
||||
elif role == "assistant" or default_class == AIMessageChunk:
|
||||
@@ -124,8 +130,6 @@ async def _aiter_sse(
|
||||
event_data = sse.json()
|
||||
if sse.event == "signal" and event_data.get("data", {}) == "[DONE]":
|
||||
return
|
||||
if sse.event == "result":
|
||||
return
|
||||
yield sse
|
||||
|
||||
|
||||
@@ -210,10 +214,7 @@ class ChatClovaX(BaseChatModel):
|
||||
timeout: int = Field(gt=0, default=90)
|
||||
max_retries: int = Field(ge=1, default=2)
|
||||
|
||||
class Config:
|
||||
"""Configuration for this pydantic object."""
|
||||
|
||||
populate_by_name = True
|
||||
model_config = ConfigDict(populate_by_name=True, protected_namespaces=())
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
@@ -286,7 +287,7 @@ class ChatClovaX(BaseChatModel):
|
||||
|
||||
if not self.ncp_apigw_api_key:
|
||||
self.ncp_apigw_api_key = convert_to_secret_str(
|
||||
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY")
|
||||
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY", "")
|
||||
)
|
||||
|
||||
if not self.base_url:
|
||||
@@ -311,22 +312,28 @@ class ChatClovaX(BaseChatModel):
|
||||
return self
|
||||
|
||||
def default_headers(self) -> Dict[str, Any]:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
clovastudio_api_key = (
|
||||
self.ncp_clovastudio_api_key.get_secret_value()
|
||||
if self.ncp_clovastudio_api_key
|
||||
else None
|
||||
)
|
||||
if clovastudio_api_key:
|
||||
headers["X-NCP-CLOVASTUDIO-API-KEY"] = clovastudio_api_key
|
||||
|
||||
apigw_api_key = (
|
||||
self.ncp_apigw_api_key.get_secret_value()
|
||||
if self.ncp_apigw_api_key
|
||||
else None
|
||||
)
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"X-NCP-CLOVASTUDIO-API-KEY": clovastudio_api_key,
|
||||
"X-NCP-APIGW-API-KEY": apigw_api_key,
|
||||
}
|
||||
if apigw_api_key:
|
||||
headers["X-NCP-APIGW-API-KEY"] = apigw_api_key
|
||||
|
||||
return headers
|
||||
|
||||
def _create_message_dicts(
|
||||
self, messages: List[BaseMessage], stop: Optional[List[str]]
|
||||
@@ -363,8 +370,6 @@ class ChatClovaX(BaseChatModel):
|
||||
and event_data.get("data", {}) == "[DONE]"
|
||||
):
|
||||
return
|
||||
if sse.event == "result":
|
||||
return
|
||||
if sse.event == "error":
|
||||
raise SSEError(message=sse.data)
|
||||
yield sse
|
||||
|
||||
532
libs/community/langchain_community/chat_models/outlines.py
Normal file
532
libs/community/langchain_community/chat_models/outlines.py
Normal file
@@ -0,0 +1,532 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import platform
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
TypedDict,
|
||||
TypeVar,
|
||||
Union,
|
||||
get_origin,
|
||||
)
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.callbacks.manager import AsyncCallbackManagerForLLMRun
|
||||
from langchain_core.language_models import LanguageModelInput
|
||||
from langchain_core.language_models.chat_models import BaseChatModel
|
||||
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
|
||||
from langchain_core.output_parsers import JsonOutputParser, PydanticOutputParser
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.runnables import Runnable
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.utils.function_calling import convert_to_openai_tool
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
from typing_extensions import Literal
|
||||
|
||||
from langchain_community.adapters.openai import convert_message_to_dict
|
||||
|
||||
_BM = TypeVar("_BM", bound=BaseModel)
|
||||
_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM], Type]
|
||||
|
||||
|
||||
class ChatOutlines(BaseChatModel):
|
||||
"""Outlines chat model integration.
|
||||
|
||||
Setup:
|
||||
pip install outlines
|
||||
|
||||
Key init args — client params:
|
||||
backend: Literal["llamacpp", "transformers", "transformers_vision", "vllm", "mlxlm"] = "transformers"
|
||||
Specifies the backend to use for the model.
|
||||
|
||||
Key init args — completion params:
|
||||
model: str
|
||||
Identifier for the model to use with Outlines.
|
||||
max_tokens: int = 256
|
||||
The maximum number of tokens to generate.
|
||||
stop: Optional[List[str]] = None
|
||||
A list of strings to stop generation when encountered.
|
||||
streaming: bool = True
|
||||
Whether to stream the results, token by token.
|
||||
|
||||
See full list of supported init args and their descriptions in the params section.
|
||||
|
||||
Instantiate:
|
||||
from langchain_community.chat_models import ChatOutlines
|
||||
chat = ChatOutlines(model="meta-llama/Llama-2-7b-chat-hf")
|
||||
|
||||
Invoke:
|
||||
chat.invoke([HumanMessage(content="Say foo:")])
|
||||
|
||||
Stream:
|
||||
for chunk in chat.stream([HumanMessage(content="Count to 10:")]):
|
||||
print(chunk.content, end="", flush=True)
|
||||
|
||||
""" # noqa: E501
|
||||
|
||||
client: Any = None # :meta private:
|
||||
|
||||
model: str
|
||||
"""Identifier for the model to use with Outlines.
|
||||
|
||||
The model identifier should be a string specifying:
|
||||
- A Hugging Face model name (e.g., "meta-llama/Llama-2-7b-chat-hf")
|
||||
- A local path to a model
|
||||
- For GGUF models, the format is "repo_id/file_name"
|
||||
(e.g., "TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf")
|
||||
|
||||
Examples:
|
||||
- "TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf"
|
||||
- "meta-llama/Llama-2-7b-chat-hf"
|
||||
"""
|
||||
|
||||
backend: Literal[
|
||||
"llamacpp", "transformers", "transformers_vision", "vllm", "mlxlm"
|
||||
] = "transformers"
|
||||
"""Specifies the backend to use for the model.
|
||||
|
||||
Supported backends are:
|
||||
- "llamacpp": For GGUF models using llama.cpp
|
||||
- "transformers": For Hugging Face Transformers models (default)
|
||||
- "transformers_vision": For vision-language models (e.g., LLaVA)
|
||||
- "vllm": For models using the vLLM library
|
||||
- "mlxlm": For models using the MLX framework
|
||||
|
||||
Note: Ensure you have the necessary dependencies installed for the chosen backend.
|
||||
The system will attempt to import required packages and may raise an ImportError
|
||||
if they are not available.
|
||||
"""
|
||||
|
||||
max_tokens: int = 256
|
||||
"""The maximum number of tokens to generate."""
|
||||
|
||||
stop: Optional[List[str]] = None
|
||||
"""A list of strings to stop generation when encountered."""
|
||||
|
||||
streaming: bool = True
|
||||
"""Whether to stream the results, token by token."""
|
||||
|
||||
regex: Optional[str] = None
|
||||
"""Regular expression for structured generation.
|
||||
|
||||
If provided, Outlines will guarantee that the generated text matches this regex.
|
||||
This can be useful for generating structured outputs like IP addresses, dates, etc.
|
||||
|
||||
Example: (valid IP address)
|
||||
regex = r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
|
||||
|
||||
Note: Computing the regex index can take some time, so it's recommended to reuse
|
||||
the same regex for multiple generations if possible.
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/regex/
|
||||
"""
|
||||
|
||||
type_constraints: Optional[Union[type, str]] = None
|
||||
"""Type constraints for structured generation.
|
||||
|
||||
Restricts the output to valid Python types. Supported types include:
|
||||
int, float, bool, datetime.date, datetime.time, datetime.datetime.
|
||||
|
||||
Example:
|
||||
type_constraints = int
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/format/
|
||||
"""
|
||||
|
||||
json_schema: Optional[Union[Any, Dict, Callable]] = None
|
||||
"""Pydantic model, JSON Schema, or callable (function signature)
|
||||
for structured JSON generation.
|
||||
|
||||
Outlines can generate JSON output that follows a specified structure,
|
||||
which is useful for:
|
||||
1. Parsing the answer (e.g., with Pydantic), storing it, or returning it to a user.
|
||||
2. Calling a function with the result.
|
||||
|
||||
You can provide:
|
||||
- A Pydantic model
|
||||
- A JSON Schema (as a Dict)
|
||||
- A callable (function signature)
|
||||
|
||||
The generated JSON will adhere to the specified structure.
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/json/
|
||||
"""
|
||||
|
||||
grammar: Optional[str] = None
|
||||
"""Context-free grammar for structured generation.
|
||||
|
||||
If provided, Outlines will generate text that adheres to the specified grammar.
|
||||
The grammar should be defined in EBNF format.
|
||||
|
||||
This can be useful for generating structured outputs like mathematical expressions,
|
||||
programming languages, or custom domain-specific languages.
|
||||
|
||||
Example:
|
||||
grammar = '''
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
'''
|
||||
|
||||
Note: Grammar-based generation is currently experimental and may have performance
|
||||
limitations. It uses greedy generation to mitigate these issues.
|
||||
|
||||
For more details and examples, see:
|
||||
https://dottxt-ai.github.io/outlines/reference/generation/cfg/
|
||||
"""
|
||||
|
||||
custom_generator: Optional[Any] = None
|
||||
"""Set your own outlines generator object to override the default behavior."""
|
||||
|
||||
model_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
||||
"""Additional parameters to pass to the underlying model.
|
||||
|
||||
Example:
|
||||
model_kwargs = {"temperature": 0.8, "seed": 42}
|
||||
"""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_environment(self) -> "ChatOutlines":
|
||||
"""Validate that outlines is installed and create a model instance."""
|
||||
num_constraints = sum(
|
||||
[
|
||||
bool(self.regex),
|
||||
bool(self.type_constraints),
|
||||
bool(self.json_schema),
|
||||
bool(self.grammar),
|
||||
]
|
||||
)
|
||||
if num_constraints > 1:
|
||||
raise ValueError(
|
||||
"Either none or exactly one of regex, type_constraints, "
|
||||
"json_schema, or grammar can be provided."
|
||||
)
|
||||
return self.build_client()
|
||||
|
||||
def build_client(self) -> "ChatOutlines":
|
||||
try:
|
||||
import outlines.models as models
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import the Outlines library. "
|
||||
"Please install it with `pip install outlines`."
|
||||
)
|
||||
|
||||
def check_packages_installed(
|
||||
packages: List[Union[str, Tuple[str, str]]],
|
||||
) -> None:
|
||||
missing_packages = [
|
||||
pkg if isinstance(pkg, str) else pkg[0]
|
||||
for pkg in packages
|
||||
if importlib.util.find_spec(pkg[1] if isinstance(pkg, tuple) else pkg)
|
||||
is None
|
||||
]
|
||||
if missing_packages:
|
||||
raise ImportError(
|
||||
f"Missing packages: {', '.join(missing_packages)}. "
|
||||
"You can install them with:\n\n"
|
||||
f" pip install {' '.join(missing_packages)}"
|
||||
)
|
||||
|
||||
if self.backend == "llamacpp":
|
||||
check_packages_installed([("llama-cpp-python", "llama_cpp")])
|
||||
if ".gguf" in self.model:
|
||||
creator, repo_name, file_name = self.model.split("/", 2)
|
||||
repo_id = f"{creator}/{repo_name}"
|
||||
else:
|
||||
raise ValueError("GGUF file_name must be provided for llama.cpp.")
|
||||
self.client = models.llamacpp(repo_id, file_name, **self.model_kwargs)
|
||||
elif self.backend == "transformers":
|
||||
check_packages_installed(["transformers", "torch", "datasets"])
|
||||
self.client = models.transformers(
|
||||
model_name=self.model, **self.model_kwargs
|
||||
)
|
||||
elif self.backend == "transformers_vision":
|
||||
if hasattr(models, "transformers_vision"):
|
||||
from transformers import LlavaNextForConditionalGeneration
|
||||
|
||||
self.client = models.transformers_vision(
|
||||
self.model,
|
||||
model_class=LlavaNextForConditionalGeneration,
|
||||
**self.model_kwargs,
|
||||
)
|
||||
else:
|
||||
raise ValueError("transformers_vision backend is not supported")
|
||||
elif self.backend == "vllm":
|
||||
if platform.system() == "Darwin":
|
||||
raise ValueError("vLLM backend is not supported on macOS.")
|
||||
check_packages_installed(["vllm"])
|
||||
self.client = models.vllm(self.model, **self.model_kwargs)
|
||||
elif self.backend == "mlxlm":
|
||||
check_packages_installed(["mlx"])
|
||||
self.client = models.mlxlm(self.model, **self.model_kwargs)
|
||||
else:
|
||||
raise ValueError(f"Unsupported backend: {self.backend}")
|
||||
return self
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "outlines-chat"
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop_at": self.stop,
|
||||
**self.model_kwargs,
|
||||
}
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"model": self.model,
|
||||
"backend": self.backend,
|
||||
"regex": self.regex,
|
||||
"type_constraints": self.type_constraints,
|
||||
"json_schema": self.json_schema,
|
||||
"grammar": self.grammar,
|
||||
**self._default_params,
|
||||
}
|
||||
|
||||
@property
|
||||
def _generator(self) -> Any:
|
||||
from outlines import generate
|
||||
|
||||
if self.custom_generator:
|
||||
return self.custom_generator
|
||||
constraints = [
|
||||
self.regex,
|
||||
self.type_constraints,
|
||||
self.json_schema,
|
||||
self.grammar,
|
||||
]
|
||||
|
||||
num_constraints = sum(constraint is not None for constraint in constraints)
|
||||
if num_constraints != 1 and num_constraints != 0:
|
||||
raise ValueError(
|
||||
"Either none or exactly one of regex, type_constraints, "
|
||||
"json_schema, or grammar can be provided."
|
||||
)
|
||||
if self.regex:
|
||||
return generate.regex(self.client, regex_str=self.regex)
|
||||
if self.type_constraints:
|
||||
return generate.format(self.client, python_type=self.type_constraints)
|
||||
if self.json_schema:
|
||||
return generate.json(self.client, schema_object=self.json_schema)
|
||||
if self.grammar:
|
||||
return generate.cfg(self.client, cfg_str=self.grammar)
|
||||
return generate.text(self.client)
|
||||
|
||||
def _convert_messages_to_openai_format(
|
||||
self, messages: list[BaseMessage]
|
||||
) -> list[dict]:
|
||||
return [convert_message_to_dict(message) for message in messages]
|
||||
|
||||
def _convert_messages_to_prompt(self, messages: list[BaseMessage]) -> str:
|
||||
"""Convert a list of messages to a single prompt."""
|
||||
if self.backend == "llamacpp": # get base_model_name from gguf repo_id
|
||||
from huggingface_hub import ModelCard
|
||||
|
||||
repo_creator, gguf_repo_name, file_name = self.model.split("/")
|
||||
model_card = ModelCard.load(f"{repo_creator}/{gguf_repo_name}")
|
||||
if hasattr(model_card.data, "base_model"):
|
||||
model_name = model_card.data.base_model
|
||||
else:
|
||||
raise ValueError(f"Base model name not found for {self.model}")
|
||||
else:
|
||||
model_name = self.model
|
||||
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
return AutoTokenizer.from_pretrained(model_name).apply_chat_template(
|
||||
self._convert_messages_to_openai_format(messages),
|
||||
tokenize=False,
|
||||
add_generation_prompt=True,
|
||||
)
|
||||
|
||||
def bind_tools(
|
||||
self,
|
||||
tools: Sequence[Dict[str, Any] | type | Callable[..., Any] | BaseTool],
|
||||
*,
|
||||
tool_choice: Optional[Union[Dict, bool, str]] = None,
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, BaseMessage]:
|
||||
"""Bind tool-like objects to this chat model
|
||||
|
||||
tool_choice: does not currently support "any", "auto" choices like OpenAI
|
||||
tool-calling API. should be a dict of the form to force this tool
|
||||
{"type": "function", "function": {"name": <<tool_name>>}}.
|
||||
"""
|
||||
formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
|
||||
tool_names = [ft["function"]["name"] for ft in formatted_tools]
|
||||
if tool_choice:
|
||||
if isinstance(tool_choice, dict):
|
||||
if not any(
|
||||
tool_choice["function"]["name"] == name for name in tool_names
|
||||
):
|
||||
raise ValueError(
|
||||
f"Tool choice {tool_choice=} was specified, but the only "
|
||||
f"provided tools were {tool_names}."
|
||||
)
|
||||
elif isinstance(tool_choice, str):
|
||||
chosen = [
|
||||
f for f in formatted_tools if f["function"]["name"] == tool_choice
|
||||
]
|
||||
if not chosen:
|
||||
raise ValueError(
|
||||
f"Tool choice {tool_choice=} was specified, but the only "
|
||||
f"provided tools were {tool_names}."
|
||||
)
|
||||
elif isinstance(tool_choice, bool):
|
||||
if len(formatted_tools) > 1:
|
||||
raise ValueError(
|
||||
"tool_choice=True can only be specified when a single tool is "
|
||||
f"passed in. Received {len(tools)} tools."
|
||||
)
|
||||
tool_choice = formatted_tools[0]
|
||||
|
||||
kwargs["tool_choice"] = tool_choice
|
||||
formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
|
||||
return super().bind_tools(tools=formatted_tools, **kwargs)
|
||||
|
||||
def with_structured_output(
|
||||
self,
|
||||
schema: Optional[_DictOrPydanticClass],
|
||||
*,
|
||||
include_raw: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]:
|
||||
if get_origin(schema) is TypedDict:
|
||||
raise NotImplementedError("TypedDict is not supported yet by Outlines")
|
||||
|
||||
self.json_schema = schema
|
||||
|
||||
if isinstance(schema, type) and issubclass(schema, BaseModel):
|
||||
parser: Union[PydanticOutputParser, JsonOutputParser] = (
|
||||
PydanticOutputParser(pydantic_object=schema)
|
||||
)
|
||||
else:
|
||||
parser = JsonOutputParser()
|
||||
|
||||
if include_raw: # TODO
|
||||
raise NotImplementedError("include_raw is not yet supported")
|
||||
|
||||
return self | parser
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
prompt = self._convert_messages_to_prompt(messages)
|
||||
|
||||
response = ""
|
||||
if self.streaming:
|
||||
for chunk in self._stream(
|
||||
messages=messages,
|
||||
stop=stop,
|
||||
run_manager=run_manager,
|
||||
**kwargs,
|
||||
):
|
||||
if isinstance(chunk.message.content, str):
|
||||
response += chunk.message.content
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid content type, only str is supported, "
|
||||
f"got {type(chunk.message.content)}"
|
||||
)
|
||||
else:
|
||||
response = self._generator(prompt, **params)
|
||||
|
||||
message = AIMessage(content=response)
|
||||
generation = ChatGeneration(message=message)
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
prompt = self._convert_messages_to_prompt(messages)
|
||||
|
||||
for token in self._generator.stream(prompt, **params):
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(token)
|
||||
message_chunk = AIMessageChunk(content=token)
|
||||
chunk = ChatGenerationChunk(message=message_chunk)
|
||||
yield chunk
|
||||
|
||||
async def _agenerate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: List[str] | None = None,
|
||||
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
if hasattr(self._generator, "agenerate"):
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
prompt = self._convert_messages_to_prompt(messages)
|
||||
response = await self._generator.agenerate(prompt, **params)
|
||||
|
||||
message = AIMessage(content=response)
|
||||
generation = ChatGeneration(message=message)
|
||||
return ChatResult(generations=[generation])
|
||||
elif self.streaming:
|
||||
response = ""
|
||||
async for chunk in self._astream(messages, stop, run_manager, **kwargs):
|
||||
response += chunk.message.content or ""
|
||||
message = AIMessage(content=response)
|
||||
generation = ChatGeneration(message=message)
|
||||
return ChatResult(generations=[generation])
|
||||
else:
|
||||
return await super()._agenerate(messages, stop, run_manager, **kwargs)
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: List[str] | None = None,
|
||||
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[ChatGenerationChunk]:
|
||||
if hasattr(self._generator, "astream"):
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
prompt = self._convert_messages_to_prompt(messages)
|
||||
|
||||
async for token in self._generator.astream(prompt, **params):
|
||||
if run_manager:
|
||||
await run_manager.on_llm_new_token(token)
|
||||
message_chunk = AIMessageChunk(content=token)
|
||||
chunk = ChatGenerationChunk(message=message_chunk)
|
||||
yield chunk
|
||||
else:
|
||||
async for chunk in super()._astream(messages, stop, run_manager, **kwargs):
|
||||
yield chunk
|
||||
@@ -1,10 +1,25 @@
|
||||
import json
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
||||
from operator import itemgetter
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
import requests
|
||||
from langchain_core.callbacks import (
|
||||
CallbackManagerForLLMRun,
|
||||
)
|
||||
from langchain_core.language_models import LanguageModelInput
|
||||
from langchain_core.language_models.chat_models import (
|
||||
BaseChatModel,
|
||||
generate_from_stream,
|
||||
@@ -19,9 +34,24 @@ from langchain_core.messages import (
|
||||
SystemMessage,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.output_parsers import (
|
||||
JsonOutputParser,
|
||||
PydanticOutputParser,
|
||||
)
|
||||
from langchain_core.output_parsers.base import OutputParserLike
|
||||
from langchain_core.output_parsers.openai_tools import (
|
||||
JsonOutputKeyToolsParser,
|
||||
PydanticToolsParser,
|
||||
make_invalid_tool_call,
|
||||
parse_tool_call,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
||||
from langchain_core.runnables import Runnable, RunnableMap, RunnablePassthrough
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env
|
||||
from pydantic import Field, SecretStr
|
||||
from langchain_core.utils.function_calling import convert_to_openai_tool
|
||||
from langchain_core.utils.pydantic import is_basemodel_subclass
|
||||
from pydantic import BaseModel, Field, SecretStr
|
||||
from requests import Response
|
||||
|
||||
|
||||
@@ -35,6 +65,7 @@ def _convert_message_to_dict(message: BaseMessage) -> Dict[str, Any]:
|
||||
Returns:
|
||||
messages_dict: role / content dict
|
||||
"""
|
||||
message_dict: Dict[str, Any] = {}
|
||||
if isinstance(message, ChatMessage):
|
||||
message_dict = {"role": message.role, "content": message.content}
|
||||
elif isinstance(message, SystemMessage):
|
||||
@@ -43,8 +74,16 @@ def _convert_message_to_dict(message: BaseMessage) -> Dict[str, Any]:
|
||||
message_dict = {"role": "user", "content": message.content}
|
||||
elif isinstance(message, AIMessage):
|
||||
message_dict = {"role": "assistant", "content": message.content}
|
||||
if "tool_calls" in message.additional_kwargs:
|
||||
message_dict["tool_calls"] = message.additional_kwargs["tool_calls"]
|
||||
if message_dict["content"] == "":
|
||||
message_dict["content"] = None
|
||||
elif isinstance(message, ToolMessage):
|
||||
message_dict = {"role": "tool", "content": message.content}
|
||||
message_dict = {
|
||||
"role": "tool",
|
||||
"content": message.content,
|
||||
"tool_call_id": message.tool_call_id,
|
||||
}
|
||||
else:
|
||||
raise TypeError(f"Got unknown type {message}")
|
||||
return message_dict
|
||||
@@ -64,14 +103,18 @@ def _create_message_dicts(messages: List[BaseMessage]) -> List[Dict[str, Any]]:
|
||||
return message_dicts
|
||||
|
||||
|
||||
def _is_pydantic_class(obj: Any) -> bool:
|
||||
return isinstance(obj, type) and is_basemodel_subclass(obj)
|
||||
|
||||
|
||||
class ChatSambaNovaCloud(BaseChatModel):
|
||||
"""
|
||||
SambaNova Cloud chat model.
|
||||
|
||||
Setup:
|
||||
To use, you should have the environment variables:
|
||||
``SAMBANOVA_URL`` set with your SambaNova Cloud URL.
|
||||
``SAMBANOVA_API_KEY`` set with your SambaNova Cloud API Key.
|
||||
`SAMBANOVA_URL` set with your SambaNova Cloud URL.
|
||||
`SAMBANOVA_API_KEY` set with your SambaNova Cloud API Key.
|
||||
http://cloud.sambanova.ai/
|
||||
Example:
|
||||
.. code-block:: python
|
||||
@@ -123,8 +166,10 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
top_k = model top k,
|
||||
stream_options = include usage to get generation metrics
|
||||
)
|
||||
|
||||
Invoke:
|
||||
.. code-block:: python
|
||||
|
||||
messages = [
|
||||
SystemMessage(content="your are an AI assistant."),
|
||||
HumanMessage(content="tell me a joke."),
|
||||
@@ -134,26 +179,78 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
Stream:
|
||||
.. code-block:: python
|
||||
|
||||
for chunk in chat.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
for chunk in chat.stream(messages):
|
||||
print(chunk.content, end="", flush=True)
|
||||
|
||||
Async:
|
||||
.. code-block:: python
|
||||
|
||||
response = chat.ainvoke(messages)
|
||||
await response
|
||||
response = chat.ainvoke(messages)
|
||||
await response
|
||||
|
||||
Tool calling:
|
||||
.. code-block:: python
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class GetWeather(BaseModel):
|
||||
'''Get the current weather in a given location'''
|
||||
|
||||
location: str = Field(
|
||||
...,
|
||||
description="The city and state, e.g. Los Angeles, CA"
|
||||
)
|
||||
|
||||
llm_with_tools = llm.bind_tools([GetWeather, GetPopulation])
|
||||
ai_msg = llm_with_tools.invoke("Should I bring my umbrella today in LA?")
|
||||
ai_msg.tool_calls
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[
|
||||
{
|
||||
'name': 'GetWeather',
|
||||
'args': {'location': 'Los Angeles, CA'},
|
||||
'id': 'call_adf61180ea2b4d228a'
|
||||
}
|
||||
]
|
||||
|
||||
Structured output:
|
||||
.. code-block:: python
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Joke(BaseModel):
|
||||
'''Joke to tell user.'''
|
||||
|
||||
setup: str = Field(description="The setup of the joke")
|
||||
punchline: str = Field(description="The punchline to the joke")
|
||||
|
||||
structured_model = llm.with_structured_output(Joke)
|
||||
structured_model.invoke("Tell me a joke about cats")
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Joke(setup="Why did the cat join a band?",
|
||||
punchline="Because it wanted to be the purr-cussionist!")
|
||||
|
||||
See `ChatSambanovaCloud.with_structured_output()` for more.
|
||||
|
||||
Token usage:
|
||||
.. code-block:: python
|
||||
response = chat.invoke(messages)
|
||||
print(response.response_metadata["usage"]["prompt_tokens"]
|
||||
print(response.response_metadata["usage"]["total_tokens"]
|
||||
|
||||
response = chat.invoke(messages)
|
||||
print(response.response_metadata["usage"]["prompt_tokens"]
|
||||
print(response.response_metadata["usage"]["total_tokens"]
|
||||
|
||||
Response metadata
|
||||
.. code-block:: python
|
||||
|
||||
response = chat.invoke(messages)
|
||||
print(response.response_metadata)
|
||||
response = chat.invoke(messages)
|
||||
print(response.response_metadata)
|
||||
|
||||
"""
|
||||
|
||||
sambanova_url: str = Field(default="")
|
||||
@@ -180,9 +277,12 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
top_k: Optional[int] = Field(default=None)
|
||||
"""model top k"""
|
||||
|
||||
stream_options: dict = Field(default={"include_usage": True})
|
||||
stream_options: Dict[str, Any] = Field(default={"include_usage": True})
|
||||
"""stream options, include usage to get generation metrics"""
|
||||
|
||||
additional_headers: Dict[str, Any] = Field(default={})
|
||||
"""Additional headers to sent in request"""
|
||||
|
||||
class Config:
|
||||
populate_by_name = True
|
||||
|
||||
@@ -230,36 +330,409 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def bind_tools(
|
||||
self,
|
||||
tools: Sequence[Union[Dict[str, Any], Type[Any], Callable[..., Any], BaseTool]],
|
||||
*,
|
||||
tool_choice: Optional[Union[Dict[str, Any], bool, str]] = None,
|
||||
parallel_tool_calls: Optional[bool] = False,
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, BaseMessage]:
|
||||
"""Bind tool-like objects to this chat model
|
||||
|
||||
tool_choice: does not currently support "any", choice like
|
||||
should be one of ["auto", "none", "required"]
|
||||
"""
|
||||
|
||||
formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
|
||||
|
||||
if tool_choice:
|
||||
if isinstance(tool_choice, str):
|
||||
# tool_choice is a tool/function name
|
||||
if tool_choice not in ("auto", "none", "required"):
|
||||
tool_choice = "auto"
|
||||
elif isinstance(tool_choice, bool):
|
||||
if tool_choice:
|
||||
tool_choice = "required"
|
||||
elif isinstance(tool_choice, dict):
|
||||
raise ValueError(
|
||||
"tool_choice must be one of ['auto', 'none', 'required']"
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unrecognized tool_choice type. Expected str, bool"
|
||||
f"Received: {tool_choice}"
|
||||
)
|
||||
else:
|
||||
tool_choice = "auto"
|
||||
kwargs["tool_choice"] = tool_choice
|
||||
kwargs["parallel_tool_calls"] = parallel_tool_calls
|
||||
return super().bind(tools=formatted_tools, **kwargs)
|
||||
|
||||
def with_structured_output(
|
||||
self,
|
||||
schema: Optional[Union[Dict[str, Any], Type[BaseModel]]] = None,
|
||||
*,
|
||||
method: Literal[
|
||||
"function_calling", "json_mode", "json_schema"
|
||||
] = "function_calling",
|
||||
include_raw: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> Runnable[LanguageModelInput, Union[Dict[str, Any], BaseModel]]:
|
||||
"""Model wrapper that returns outputs formatted to match the given schema.
|
||||
|
||||
Args:
|
||||
schema:
|
||||
The output schema. Can be passed in as:
|
||||
- an OpenAI function/tool schema,
|
||||
- a JSON Schema,
|
||||
- a TypedDict class,
|
||||
- or a Pydantic.BaseModel class.
|
||||
If `schema` is a Pydantic class then the model output will be a
|
||||
Pydantic instance of that class, and the model-generated fields will be
|
||||
validated by the Pydantic class. Otherwise the model output will be a
|
||||
dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool`
|
||||
for more on how to properly specify types and descriptions of
|
||||
schema fields when specifying a Pydantic or TypedDict class.
|
||||
|
||||
method:
|
||||
The method for steering model generation, either "function_calling"
|
||||
"json_mode" or "json_schema".
|
||||
If "function_calling" then the schema will be converted
|
||||
to an OpenAI function and the returned model will make use of the
|
||||
function-calling API. If "json_mode" or "json_schema" then OpenAI's
|
||||
JSON mode will be used.
|
||||
Note that if using "json_mode" or "json_schema" then you must include instructions
|
||||
for formatting the output into the desired schema into the model call.
|
||||
|
||||
include_raw:
|
||||
If False then only the parsed structured output is returned. If
|
||||
an error occurs during model output parsing it will be raised. If True
|
||||
then both the raw model response (a BaseMessage) and the parsed model
|
||||
response will be returned. If an error occurs during output parsing it
|
||||
will be caught and returned as well. The final output is always a dict
|
||||
with keys "raw", "parsed", and "parsing_error".
|
||||
|
||||
Returns:
|
||||
A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`.
|
||||
|
||||
If `include_raw` is False and `schema` is a Pydantic class, Runnable outputs
|
||||
an instance of `schema` (i.e., a Pydantic object).
|
||||
|
||||
Otherwise, if `include_raw` is False then Runnable outputs a dict.
|
||||
|
||||
If `include_raw` is True, then Runnable outputs a dict with keys:
|
||||
- `"raw"`: BaseMessage
|
||||
- `"parsed"`: None if there was a parsing error, otherwise the type depends on the `schema` as described above.
|
||||
- `"parsing_error"`: Optional[BaseException]
|
||||
|
||||
Example: schema=Pydantic class, method="function_calling", include_raw=False:
|
||||
.. code-block:: python
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class AnswerWithJustification(BaseModel):
|
||||
'''An answer to the user question along with justification for the answer.'''
|
||||
|
||||
answer: str
|
||||
justification: str = Field(
|
||||
description="A justification for the answer."
|
||||
)
|
||||
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(AnswerWithJustification)
|
||||
|
||||
structured_llm.invoke(
|
||||
"What weighs more a pound of bricks or a pound of feathers"
|
||||
)
|
||||
|
||||
# -> AnswerWithJustification(
|
||||
# answer='They weigh the same',
|
||||
# justification='A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same.'
|
||||
# )
|
||||
|
||||
Example: schema=Pydantic class, method="function_calling", include_raw=True:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AnswerWithJustification(BaseModel):
|
||||
'''An answer to the user question along with justification for the answer.'''
|
||||
|
||||
answer: str
|
||||
justification: str
|
||||
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(
|
||||
AnswerWithJustification, include_raw=True
|
||||
)
|
||||
|
||||
structured_llm.invoke(
|
||||
"What weighs more a pound of bricks or a pound of feathers"
|
||||
)
|
||||
# -> {
|
||||
# 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'function': {'arguments': '{"answer": "They weigh the same.", "justification": "A pound is a unit of weight or mass, so one pound of bricks and one pound of feathers both weigh the same amount."}', 'name': 'AnswerWithJustification'}, 'id': 'call_17a431fc6a4240e1bd', 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'usage': {'acceptance_rate': 5, 'completion_tokens': 53, 'completion_tokens_after_first_per_sec': 343.7964936837758, 'completion_tokens_after_first_per_sec_first_ten': 439.1205661878638, 'completion_tokens_per_sec': 162.8511306784833, 'end_time': 1731527851.0698032, 'is_last_response': True, 'prompt_tokens': 213, 'start_time': 1731527850.7137961, 'time_to_first_token': 0.20475482940673828, 'total_latency': 0.32545061111450196, 'total_tokens': 266, 'total_tokens_per_sec': 817.3283162354066}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731527850}, id='95667eaf-447f-4b53-bb6e-b6e1094ded88', tool_calls=[{'name': 'AnswerWithJustification', 'args': {'answer': 'They weigh the same.', 'justification': 'A pound is a unit of weight or mass, so one pound of bricks and one pound of feathers both weigh the same amount.'}, 'id': 'call_17a431fc6a4240e1bd', 'type': 'tool_call'}]),
|
||||
# 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='A pound is a unit of weight or mass, so one pound of bricks and one pound of feathers both weigh the same amount.'),
|
||||
# 'parsing_error': None
|
||||
# }
|
||||
|
||||
Example: schema=TypedDict class, method="function_calling", include_raw=False:
|
||||
.. code-block:: python
|
||||
|
||||
# IMPORTANT: If you are using Python <=3.8, you need to import Annotated
|
||||
# from typing_extensions, not from typing.
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
|
||||
|
||||
class AnswerWithJustification(TypedDict):
|
||||
'''An answer to the user question along with justification for the answer.'''
|
||||
|
||||
answer: str
|
||||
justification: Annotated[
|
||||
Optional[str], None, "A justification for the answer."
|
||||
]
|
||||
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(AnswerWithJustification)
|
||||
|
||||
structured_llm.invoke(
|
||||
"What weighs more a pound of bricks or a pound of feathers"
|
||||
)
|
||||
# -> {
|
||||
# 'answer': 'They weigh the same',
|
||||
# 'justification': 'A pound is a unit of weight or mass, so one pound of bricks and one pound of feathers both weigh the same amount.'
|
||||
# }
|
||||
|
||||
Example: schema=OpenAI function schema, method="function_calling", include_raw=False:
|
||||
.. code-block:: python
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
|
||||
oai_schema = {
|
||||
'name': 'AnswerWithJustification',
|
||||
'description': 'An answer to the user question along with justification for the answer.',
|
||||
'parameters': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'answer': {'type': 'string'},
|
||||
'justification': {'description': 'A justification for the answer.', 'type': 'string'}
|
||||
},
|
||||
'required': ['answer']
|
||||
}
|
||||
}
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(oai_schema)
|
||||
|
||||
structured_llm.invoke(
|
||||
"What weighs more a pound of bricks or a pound of feathers"
|
||||
)
|
||||
# -> {
|
||||
# 'answer': 'They weigh the same',
|
||||
# 'justification': 'A pound is a unit of weight or mass, so one pound of bricks and one pound of feathers both weigh the same amount.'
|
||||
# }
|
||||
|
||||
Example: schema=Pydantic class, method="json_mode", include_raw=True:
|
||||
.. code-block::
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
from pydantic import BaseModel
|
||||
|
||||
class AnswerWithJustification(BaseModel):
|
||||
answer: str
|
||||
justification: str
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(
|
||||
AnswerWithJustification,
|
||||
method="json_mode",
|
||||
include_raw=True
|
||||
)
|
||||
|
||||
structured_llm.invoke(
|
||||
"Answer the following question. "
|
||||
"Make sure to return a JSON blob with keys 'answer' and 'justification'.\n\n"
|
||||
"What's heavier a pound of bricks or a pound of feathers?"
|
||||
)
|
||||
# -> {
|
||||
# 'raw': AIMessage(content='{\n "answer": "They are the same weight",\n "justification": "A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities."\n}', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 5.3125, 'completion_tokens': 79, 'completion_tokens_after_first_per_sec': 292.65701089829776, 'completion_tokens_after_first_per_sec_first_ten': 346.43324678555325, 'completion_tokens_per_sec': 200.012158915008, 'end_time': 1731528071.1708555, 'is_last_response': True, 'prompt_tokens': 70, 'start_time': 1731528070.737394, 'time_to_first_token': 0.16693782806396484, 'total_latency': 0.3949759876026827, 'total_tokens': 149, 'total_tokens_per_sec': 377.2381225105847}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731528070}, id='83208297-3eb9-4021-a856-ca78a15758df'),
|
||||
# 'parsed': AnswerWithJustification(answer='They are the same weight', justification='A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities.'),
|
||||
# 'parsing_error': None
|
||||
# }
|
||||
|
||||
Example: schema=None, method="json_mode", include_raw=True:
|
||||
.. code-block::
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(method="json_mode", include_raw=True)
|
||||
|
||||
structured_llm.invoke(
|
||||
"Answer the following question. "
|
||||
"Make sure to return a JSON blob with keys 'answer' and 'justification'.\n\n"
|
||||
"What's heavier a pound of bricks or a pound of feathers?"
|
||||
)
|
||||
# -> {
|
||||
# 'raw': AIMessage(content='{\n "answer": "They are the same weight",\n "justification": "A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities."\n}', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 4.722222222222222, 'completion_tokens': 79, 'completion_tokens_after_first_per_sec': 357.1315485254867, 'completion_tokens_after_first_per_sec_first_ten': 416.83279609305305, 'completion_tokens_per_sec': 240.92819585198137, 'end_time': 1731528164.8474727, 'is_last_response': True, 'prompt_tokens': 70, 'start_time': 1731528164.4906917, 'time_to_first_token': 0.13837409019470215, 'total_latency': 0.3278985247892492, 'total_tokens': 149, 'total_tokens_per_sec': 454.4088757208256}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731528164}, id='15261eaf-8a25-42ef-8ed5-f63d8bf5b1b0'),
|
||||
# 'parsed': {
|
||||
# 'answer': 'They are the same weight',
|
||||
# 'justification': 'A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities.'},
|
||||
# },
|
||||
# 'parsing_error': None
|
||||
# }
|
||||
|
||||
Example: schema=None, method="json_schema", include_raw=True:
|
||||
.. code-block::
|
||||
|
||||
from langchain_community.chat_models import ChatSambaNovaCloud
|
||||
|
||||
class AnswerWithJustification(BaseModel):
|
||||
answer: str
|
||||
justification: str
|
||||
|
||||
llm = ChatSambaNovaCloud(model="Meta-Llama-3.1-70B-Instruct", temperature=0)
|
||||
structured_llm = llm.with_structured_output(AnswerWithJustification, method="json_schema", include_raw=True)
|
||||
|
||||
structured_llm.invoke(
|
||||
"Answer the following question. "
|
||||
"Make sure to return a JSON blob with keys 'answer' and 'justification'.\n\n"
|
||||
"What's heavier a pound of bricks or a pound of feathers?"
|
||||
)
|
||||
# -> {
|
||||
# 'raw': AIMessage(content='{\n "answer": "They are the same weight",\n "justification": "A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities."\n}', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'usage': {'acceptance_rate': 5.3125, 'completion_tokens': 79, 'completion_tokens_after_first_per_sec': 292.65701089829776, 'completion_tokens_after_first_per_sec_first_ten': 346.43324678555325, 'completion_tokens_per_sec': 200.012158915008, 'end_time': 1731528071.1708555, 'is_last_response': True, 'prompt_tokens': 70, 'start_time': 1731528070.737394, 'time_to_first_token': 0.16693782806396484, 'total_latency': 0.3949759876026827, 'total_tokens': 149, 'total_tokens_per_sec': 377.2381225105847}, 'model_name': 'Meta-Llama-3.1-70B-Instruct', 'system_fingerprint': 'fastcoe', 'created': 1731528070}, id='83208297-3eb9-4021-a856-ca78a15758df'),
|
||||
# 'parsed': AnswerWithJustification(answer='They are the same weight', justification='A pound is a unit of weight or mass, so a pound of bricks and a pound of feathers both weigh the same amount, one pound. The difference is in their density and volume. A pound of feathers would take up more space than a pound of bricks due to the difference in their densities.'),
|
||||
# 'parsing_error': None
|
||||
# }
|
||||
""" # noqa: E501
|
||||
if kwargs is not None:
|
||||
raise ValueError(f"Received unsupported arguments {kwargs}")
|
||||
is_pydantic_schema = _is_pydantic_class(schema)
|
||||
if method == "function_calling":
|
||||
if schema is None:
|
||||
raise ValueError(
|
||||
"`schema` must be specified when method is `function_calling`. "
|
||||
"Received None."
|
||||
)
|
||||
tool_name = convert_to_openai_tool(schema)["function"]["name"]
|
||||
llm = self.bind_tools([schema], tool_choice=tool_name)
|
||||
if is_pydantic_schema:
|
||||
output_parser: OutputParserLike[Any] = PydanticToolsParser(
|
||||
tools=[schema],
|
||||
first_tool_only=True,
|
||||
)
|
||||
else:
|
||||
output_parser = JsonOutputKeyToolsParser(
|
||||
key_name=tool_name, first_tool_only=True
|
||||
)
|
||||
elif method == "json_mode":
|
||||
llm = self
|
||||
# TODO bind response format when json mode available by API
|
||||
# llm = self.bind(response_format={"type": "json_object"})
|
||||
if is_pydantic_schema:
|
||||
schema = cast(Type[BaseModel], schema)
|
||||
output_parser = PydanticOutputParser(pydantic_object=schema)
|
||||
else:
|
||||
output_parser = JsonOutputParser()
|
||||
|
||||
elif method == "json_schema":
|
||||
if schema is None:
|
||||
raise ValueError(
|
||||
"`schema` must be specified when method is not `json_mode`. "
|
||||
"Received None."
|
||||
)
|
||||
llm = self
|
||||
# TODO bind response format when json schema available by API,
|
||||
# update example
|
||||
# llm = self.bind(
|
||||
# response_format={"type": "json_object", "json_schema": schema}
|
||||
# )
|
||||
if is_pydantic_schema:
|
||||
schema = cast(Type[BaseModel], schema)
|
||||
output_parser = PydanticOutputParser(pydantic_object=schema)
|
||||
else:
|
||||
output_parser = JsonOutputParser()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unrecognized method argument. Expected one of `function_calling` or "
|
||||
f"`json_mode`. Received: `{method}`"
|
||||
)
|
||||
|
||||
if include_raw:
|
||||
parser_assign = RunnablePassthrough.assign(
|
||||
parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None
|
||||
)
|
||||
parser_none = RunnablePassthrough.assign(parsed=lambda _: None)
|
||||
parser_with_fallback = parser_assign.with_fallbacks(
|
||||
[parser_none], exception_key="parsing_error"
|
||||
)
|
||||
return RunnableMap(raw=llm) | parser_with_fallback
|
||||
else:
|
||||
return llm | output_parser
|
||||
|
||||
def _handle_request(
|
||||
self, messages_dicts: List[Dict], stop: Optional[List[str]] = None
|
||||
) -> Dict[str, Any]:
|
||||
self,
|
||||
messages_dicts: List[Dict[str, Any]],
|
||||
stop: Optional[List[str]] = None,
|
||||
streaming: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> Response:
|
||||
"""
|
||||
Performs a post request to the LLM API.
|
||||
|
||||
Args:
|
||||
messages_dicts: List of role / content dicts to use as input.
|
||||
stop: list of stop tokens
|
||||
streaming: wether to do a streaming call
|
||||
|
||||
Returns:
|
||||
An iterator of response dicts.
|
||||
"""
|
||||
data = {
|
||||
"messages": messages_dicts,
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop": stop,
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"top_k": self.top_k,
|
||||
}
|
||||
if streaming:
|
||||
data = {
|
||||
"messages": messages_dicts,
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop": stop,
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"top_k": self.top_k,
|
||||
"stream": True,
|
||||
"stream_options": self.stream_options,
|
||||
**kwargs,
|
||||
}
|
||||
else:
|
||||
data = {
|
||||
"messages": messages_dicts,
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop": stop,
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"top_k": self.top_k,
|
||||
**kwargs,
|
||||
}
|
||||
http_session = requests.Session()
|
||||
response = http_session.post(
|
||||
self.sambanova_url,
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.sambanova_api_key.get_secret_value()}",
|
||||
"Content-Type": "application/json",
|
||||
**self.additional_headers,
|
||||
},
|
||||
json=data,
|
||||
stream=streaming,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
raise RuntimeError(
|
||||
@@ -267,27 +740,78 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
f"{response.status_code}.",
|
||||
f"{response.text}.",
|
||||
)
|
||||
response_dict = response.json()
|
||||
if response_dict.get("error"):
|
||||
raise RuntimeError(
|
||||
f"Sambanova /complete call failed with status code "
|
||||
f"{response.status_code}.",
|
||||
f"{response_dict}.",
|
||||
)
|
||||
return response_dict
|
||||
return response
|
||||
|
||||
def _handle_streaming_request(
|
||||
self, messages_dicts: List[Dict], stop: Optional[List[str]] = None
|
||||
) -> Iterator[Dict]:
|
||||
def _process_response(self, response: Response) -> AIMessage:
|
||||
"""
|
||||
Performs an streaming post request to the LLM API.
|
||||
Process a non streaming response from the api
|
||||
|
||||
Args:
|
||||
messages_dicts: List of role / content dicts to use as input.
|
||||
stop: list of stop tokens
|
||||
response: A request Response object
|
||||
|
||||
Returns
|
||||
generation: an AIMessage with model generation
|
||||
"""
|
||||
try:
|
||||
response_dict = response.json()
|
||||
if response_dict.get("error"):
|
||||
raise RuntimeError(
|
||||
f"Sambanova /complete call failed with status code "
|
||||
f"{response.status_code}.",
|
||||
f"{response_dict}.",
|
||||
)
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
f"Sambanova /complete call failed couldn't get JSON response {e}"
|
||||
f"response: {response.text}"
|
||||
)
|
||||
content = response_dict["choices"][0]["message"].get("content", "")
|
||||
if content is None:
|
||||
content = ""
|
||||
additional_kwargs: Dict[str, Any] = {}
|
||||
tool_calls = []
|
||||
invalid_tool_calls = []
|
||||
raw_tool_calls = response_dict["choices"][0]["message"].get("tool_calls")
|
||||
if raw_tool_calls:
|
||||
additional_kwargs["tool_calls"] = raw_tool_calls
|
||||
for raw_tool_call in raw_tool_calls:
|
||||
if isinstance(raw_tool_call["function"]["arguments"], dict):
|
||||
raw_tool_call["function"]["arguments"] = json.dumps(
|
||||
raw_tool_call["function"].get("arguments", {})
|
||||
)
|
||||
try:
|
||||
tool_calls.append(parse_tool_call(raw_tool_call, return_id=True))
|
||||
except Exception as e:
|
||||
invalid_tool_calls.append(
|
||||
make_invalid_tool_call(raw_tool_call, str(e))
|
||||
)
|
||||
message = AIMessage(
|
||||
content=content,
|
||||
additional_kwargs=additional_kwargs,
|
||||
tool_calls=tool_calls,
|
||||
invalid_tool_calls=invalid_tool_calls,
|
||||
response_metadata={
|
||||
"finish_reason": response_dict["choices"][0]["finish_reason"],
|
||||
"usage": response_dict.get("usage"),
|
||||
"model_name": response_dict["model"],
|
||||
"system_fingerprint": response_dict["system_fingerprint"],
|
||||
"created": response_dict["created"],
|
||||
},
|
||||
id=response_dict["id"],
|
||||
)
|
||||
return message
|
||||
|
||||
def _process_stream_response(
|
||||
self, response: Response
|
||||
) -> Iterator[BaseMessageChunk]:
|
||||
"""
|
||||
Process a streaming response from the api
|
||||
|
||||
Args:
|
||||
response: An iterable request Response object
|
||||
|
||||
Yields:
|
||||
An iterator of response dicts.
|
||||
generation: an AIMessageChunk with model partial generation
|
||||
"""
|
||||
try:
|
||||
import sseclient
|
||||
@@ -296,37 +820,9 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
"could not import sseclient library"
|
||||
"Please install it with `pip install sseclient-py`."
|
||||
)
|
||||
data = {
|
||||
"messages": messages_dicts,
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop": stop,
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"top_p": self.top_p,
|
||||
"top_k": self.top_k,
|
||||
"stream": True,
|
||||
"stream_options": self.stream_options,
|
||||
}
|
||||
http_session = requests.Session()
|
||||
response = http_session.post(
|
||||
self.sambanova_url,
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.sambanova_api_key.get_secret_value()}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
json=data,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
client = sseclient.SSEClient(response)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise RuntimeError(
|
||||
f"Sambanova /complete call failed with status code "
|
||||
f"{response.status_code}."
|
||||
f"{response.text}."
|
||||
)
|
||||
|
||||
for event in client.events():
|
||||
if event.event == "error_event":
|
||||
raise RuntimeError(
|
||||
@@ -353,7 +849,31 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
f"{response.status_code}."
|
||||
f"{event.data}."
|
||||
)
|
||||
yield data
|
||||
if len(data["choices"]) > 0:
|
||||
finish_reason = data["choices"][0].get("finish_reason")
|
||||
content = data["choices"][0]["delta"]["content"]
|
||||
id = data["id"]
|
||||
chunk = AIMessageChunk(
|
||||
content=content, id=id, additional_kwargs={}
|
||||
)
|
||||
else:
|
||||
content = ""
|
||||
id = data["id"]
|
||||
metadata = {
|
||||
"finish_reason": finish_reason,
|
||||
"usage": data.get("usage"),
|
||||
"model_name": data["model"],
|
||||
"system_fingerprint": data["system_fingerprint"],
|
||||
"created": data["created"],
|
||||
}
|
||||
chunk = AIMessageChunk(
|
||||
content=content,
|
||||
id=id,
|
||||
response_metadata=metadata,
|
||||
additional_kwargs={},
|
||||
)
|
||||
yield chunk
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
f"Error getting content chunk raw streamed response: {e}"
|
||||
@@ -390,21 +910,14 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
if stream_iter:
|
||||
return generate_from_stream(stream_iter)
|
||||
messages_dicts = _create_message_dicts(messages)
|
||||
response = self._handle_request(messages_dicts, stop)
|
||||
message = AIMessage(
|
||||
content=response["choices"][0]["message"]["content"],
|
||||
additional_kwargs={},
|
||||
response_metadata={
|
||||
"finish_reason": response["choices"][0]["finish_reason"],
|
||||
"usage": response.get("usage"),
|
||||
"model_name": response["model"],
|
||||
"system_fingerprint": response["system_fingerprint"],
|
||||
"created": response["created"],
|
||||
response = self._handle_request(messages_dicts, stop, streaming=False, **kwargs)
|
||||
message = self._process_response(response)
|
||||
generation = ChatGeneration(
|
||||
message=message,
|
||||
generation_info={
|
||||
"finish_reason": message.response_metadata["finish_reason"]
|
||||
},
|
||||
id=response["id"],
|
||||
)
|
||||
|
||||
generation = ChatGeneration(message=message)
|
||||
return ChatResult(generations=[generation])
|
||||
|
||||
def _stream(
|
||||
@@ -431,34 +944,9 @@ class ChatSambaNovaCloud(BaseChatModel):
|
||||
chunk: ChatGenerationChunk with model partial generation
|
||||
"""
|
||||
messages_dicts = _create_message_dicts(messages)
|
||||
finish_reason = None
|
||||
for partial_response in self._handle_streaming_request(messages_dicts, stop):
|
||||
if len(partial_response["choices"]) > 0:
|
||||
finish_reason = partial_response["choices"][0].get("finish_reason")
|
||||
content = partial_response["choices"][0]["delta"]["content"]
|
||||
id = partial_response["id"]
|
||||
chunk = ChatGenerationChunk(
|
||||
message=AIMessageChunk(content=content, id=id, additional_kwargs={})
|
||||
)
|
||||
else:
|
||||
content = ""
|
||||
id = partial_response["id"]
|
||||
metadata = {
|
||||
"finish_reason": finish_reason,
|
||||
"usage": partial_response.get("usage"),
|
||||
"model_name": partial_response["model"],
|
||||
"system_fingerprint": partial_response["system_fingerprint"],
|
||||
"created": partial_response["created"],
|
||||
}
|
||||
chunk = ChatGenerationChunk(
|
||||
message=AIMessageChunk(
|
||||
content=content,
|
||||
id=id,
|
||||
response_metadata=metadata,
|
||||
additional_kwargs={},
|
||||
)
|
||||
)
|
||||
|
||||
response = self._handle_request(messages_dicts, stop, streaming=True, **kwargs)
|
||||
for ai_message_chunk in self._process_stream_response(response):
|
||||
chunk = ChatGenerationChunk(message=ai_message_chunk)
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(chunk.text, chunk=chunk)
|
||||
yield chunk
|
||||
@@ -617,10 +1105,10 @@ class ChatSambaStudio(BaseChatModel):
|
||||
process_prompt: Optional[bool] = Field(default=True)
|
||||
"""whether process prompt (for CoE generic v1 and v2 endpoints)"""
|
||||
|
||||
stream_options: dict = Field(default={"include_usage": True})
|
||||
stream_options: Dict[str, Any] = Field(default={"include_usage": True})
|
||||
"""stream options, include usage to get generation metrics"""
|
||||
|
||||
special_tokens: dict = Field(
|
||||
special_tokens: Dict[str, Any] = Field(
|
||||
default={
|
||||
"start": "<|begin_of_text|>",
|
||||
"start_role": "<|begin_of_text|><|start_header_id|>{role}<|end_header_id|>",
|
||||
|
||||
@@ -4,6 +4,7 @@ import logging
|
||||
from functools import cached_property
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langchain_core._api.deprecation import deprecated
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_core.utils import pre_init
|
||||
from langchain_core.utils.pydantic import get_fields
|
||||
@@ -15,6 +16,11 @@ MAX_BATCH_SIZE_CHARS = 1000000
|
||||
MAX_BATCH_SIZE_PARTS = 90
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="0.3.5",
|
||||
removal="1.0",
|
||||
alternative_import="langchain_gigachat.GigaChatEmbeddings",
|
||||
)
|
||||
class GigaChatEmbeddings(BaseModel, Embeddings):
|
||||
"""GigaChat Embeddings models.
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from langchain_core.utils import convert_to_secret_str, get_from_env
|
||||
from pydantic import (
|
||||
AliasChoices,
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
SecretStr,
|
||||
model_validator,
|
||||
@@ -86,8 +87,7 @@ class ClovaXEmbeddings(BaseModel, Embeddings):
|
||||
|
||||
timeout: int = Field(gt=0, default=60)
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True, protected_namespaces=())
|
||||
|
||||
@property
|
||||
def lc_secrets(self) -> Dict[str, str]:
|
||||
@@ -115,7 +115,7 @@ class ClovaXEmbeddings(BaseModel, Embeddings):
|
||||
|
||||
if not self.ncp_apigw_api_key:
|
||||
self.ncp_apigw_api_key = convert_to_secret_str(
|
||||
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY")
|
||||
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY", "")
|
||||
)
|
||||
|
||||
if not self.base_url:
|
||||
@@ -143,22 +143,28 @@ class ClovaXEmbeddings(BaseModel, Embeddings):
|
||||
return self
|
||||
|
||||
def default_headers(self) -> Dict[str, Any]:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
clovastudio_api_key = (
|
||||
self.ncp_clovastudio_api_key.get_secret_value()
|
||||
if self.ncp_clovastudio_api_key
|
||||
else None
|
||||
)
|
||||
if clovastudio_api_key:
|
||||
headers["X-NCP-CLOVASTUDIO-API-KEY"] = clovastudio_api_key
|
||||
|
||||
apigw_api_key = (
|
||||
self.ncp_apigw_api_key.get_secret_value()
|
||||
if self.ncp_apigw_api_key
|
||||
else None
|
||||
)
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"X-NCP-CLOVASTUDIO-API-KEY": clovastudio_api_key,
|
||||
"X-NCP-APIGW-API-KEY": apigw_api_key,
|
||||
}
|
||||
if apigw_api_key:
|
||||
headers["X-NCP-APIGW-API-KEY"] = apigw_api_key
|
||||
|
||||
return headers
|
||||
|
||||
def _embed_text(self, text: str) -> List[float]:
|
||||
payload = {"text": text}
|
||||
|
||||
@@ -458,6 +458,12 @@ def _import_openlm() -> Type[BaseLLM]:
|
||||
return OpenLM
|
||||
|
||||
|
||||
def _import_outlines() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.outlines import Outlines
|
||||
|
||||
return Outlines
|
||||
|
||||
|
||||
def _import_pai_eas_endpoint() -> Type[BaseLLM]:
|
||||
from langchain_community.llms.pai_eas_endpoint import PaiEasEndpoint
|
||||
|
||||
@@ -807,6 +813,8 @@ def __getattr__(name: str) -> Any:
|
||||
return _import_openllm()
|
||||
elif name == "OpenLM":
|
||||
return _import_openlm()
|
||||
elif name == "Outlines":
|
||||
return _import_outlines()
|
||||
elif name == "PaiEasEndpoint":
|
||||
return _import_pai_eas_endpoint()
|
||||
elif name == "Petals":
|
||||
@@ -954,6 +962,7 @@ __all__ = [
|
||||
"OpenAIChat",
|
||||
"OpenLLM",
|
||||
"OpenLM",
|
||||
"Outlines",
|
||||
"PaiEasEndpoint",
|
||||
"Petals",
|
||||
"PipelineAI",
|
||||
@@ -1076,6 +1085,7 @@ def get_type_to_cls_dict() -> Dict[str, Callable[[], Type[BaseLLM]]]:
|
||||
"vertexai_model_garden": _import_vertex_model_garden,
|
||||
"openllm": _import_openllm,
|
||||
"openllm_client": _import_openllm,
|
||||
"outlines": _import_outlines,
|
||||
"vllm": _import_vllm,
|
||||
"vllm_openai": _import_vllm_openai,
|
||||
"watsonxllm": _import_watsonxllm,
|
||||
|
||||
314
libs/community/langchain_community/llms/outlines.py
Normal file
314
libs/community/langchain_community/llms/outlines.py
Normal file
@@ -0,0 +1,314 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import logging
|
||||
import platform
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, Optional, Tuple, Union
|
||||
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.language_models.llms import LLM
|
||||
from langchain_core.outputs import GenerationChunk
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Outlines(LLM):
|
||||
"""LLM wrapper for the Outlines library."""
|
||||
|
||||
client: Any = None # :meta private:
|
||||
|
||||
model: str
|
||||
"""Identifier for the model to use with Outlines.
|
||||
|
||||
The model identifier should be a string specifying:
|
||||
- A Hugging Face model name (e.g., "meta-llama/Llama-2-7b-chat-hf")
|
||||
- A local path to a model
|
||||
- For GGUF models, the format is "repo_id/file_name"
|
||||
(e.g., "TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf")
|
||||
|
||||
Examples:
|
||||
- "TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf"
|
||||
- "meta-llama/Llama-2-7b-chat-hf"
|
||||
"""
|
||||
|
||||
backend: Literal[
|
||||
"llamacpp", "transformers", "transformers_vision", "vllm", "mlxlm"
|
||||
] = "transformers"
|
||||
"""Specifies the backend to use for the model.
|
||||
|
||||
Supported backends are:
|
||||
- "llamacpp": For GGUF models using llama.cpp
|
||||
- "transformers": For Hugging Face Transformers models (default)
|
||||
- "transformers_vision": For vision-language models (e.g., LLaVA)
|
||||
- "vllm": For models using the vLLM library
|
||||
- "mlxlm": For models using the MLX framework
|
||||
|
||||
Note: Ensure you have the necessary dependencies installed for the chosen backend.
|
||||
The system will attempt to import required packages and may raise an ImportError
|
||||
if they are not available.
|
||||
"""
|
||||
|
||||
max_tokens: int = 256
|
||||
"""The maximum number of tokens to generate."""
|
||||
|
||||
stop: Optional[List[str]] = None
|
||||
"""A list of strings to stop generation when encountered."""
|
||||
|
||||
streaming: bool = True
|
||||
"""Whether to stream the results, token by token."""
|
||||
|
||||
regex: Optional[str] = None
|
||||
"""Regular expression for structured generation.
|
||||
|
||||
If provided, Outlines will guarantee that the generated text matches this regex.
|
||||
This can be useful for generating structured outputs like IP addresses, dates, etc.
|
||||
|
||||
Example: (valid IP address)
|
||||
regex = r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
|
||||
|
||||
Note: Computing the regex index can take some time, so it's recommended to reuse
|
||||
the same regex for multiple generations if possible.
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/regex/
|
||||
"""
|
||||
|
||||
type_constraints: Optional[Union[type, str]] = None
|
||||
"""Type constraints for structured generation.
|
||||
|
||||
Restricts the output to valid Python types. Supported types include:
|
||||
int, float, bool, datetime.date, datetime.time, datetime.datetime.
|
||||
|
||||
Example:
|
||||
type_constraints = int
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/format/
|
||||
"""
|
||||
|
||||
json_schema: Optional[Union[BaseModel, Dict, Callable]] = None
|
||||
"""Pydantic model, JSON Schema, or callable (function signature)
|
||||
for structured JSON generation.
|
||||
|
||||
Outlines can generate JSON output that follows a specified structure,
|
||||
which is useful for:
|
||||
1. Parsing the answer (e.g., with Pydantic), storing it, or returning it to a user.
|
||||
2. Calling a function with the result.
|
||||
|
||||
You can provide:
|
||||
- A Pydantic model
|
||||
- A JSON Schema (as a Dict)
|
||||
- A callable (function signature)
|
||||
|
||||
The generated JSON will adhere to the specified structure.
|
||||
|
||||
For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/json/
|
||||
"""
|
||||
|
||||
grammar: Optional[str] = None
|
||||
"""Context-free grammar for structured generation.
|
||||
|
||||
If provided, Outlines will generate text that adheres to the specified grammar.
|
||||
The grammar should be defined in EBNF format.
|
||||
|
||||
This can be useful for generating structured outputs like mathematical expressions,
|
||||
programming languages, or custom domain-specific languages.
|
||||
|
||||
Example:
|
||||
grammar = '''
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
'''
|
||||
|
||||
Note: Grammar-based generation is currently experimental and may have performance
|
||||
limitations. It uses greedy generation to mitigate these issues.
|
||||
|
||||
For more details and examples, see:
|
||||
https://dottxt-ai.github.io/outlines/reference/generation/cfg/
|
||||
"""
|
||||
|
||||
custom_generator: Optional[Any] = None
|
||||
"""Set your own outlines generator object to override the default behavior."""
|
||||
|
||||
model_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
||||
"""Additional parameters to pass to the underlying model.
|
||||
|
||||
Example:
|
||||
model_kwargs = {"temperature": 0.8, "seed": 42}
|
||||
"""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_environment(self) -> "Outlines":
|
||||
"""Validate that outlines is installed and create a model instance."""
|
||||
num_constraints = sum(
|
||||
[
|
||||
bool(self.regex),
|
||||
bool(self.type_constraints),
|
||||
bool(self.json_schema),
|
||||
bool(self.grammar),
|
||||
]
|
||||
)
|
||||
if num_constraints > 1:
|
||||
raise ValueError(
|
||||
"Either none or exactly one of regex, type_constraints, "
|
||||
"json_schema, or grammar can be provided."
|
||||
)
|
||||
return self.build_client()
|
||||
|
||||
def build_client(self) -> "Outlines":
|
||||
try:
|
||||
import outlines.models as models
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Could not import the Outlines library. "
|
||||
"Please install it with `pip install outlines`."
|
||||
)
|
||||
|
||||
def check_packages_installed(
|
||||
packages: List[Union[str, Tuple[str, str]]],
|
||||
) -> None:
|
||||
missing_packages = [
|
||||
pkg if isinstance(pkg, str) else pkg[0]
|
||||
for pkg in packages
|
||||
if importlib.util.find_spec(pkg[1] if isinstance(pkg, tuple) else pkg)
|
||||
is None
|
||||
]
|
||||
if missing_packages:
|
||||
raise ImportError( # todo this is displaying wrong
|
||||
f"Missing packages: {', '.join(missing_packages)}. "
|
||||
"You can install them with:\n\n"
|
||||
f" pip install {' '.join(missing_packages)}"
|
||||
)
|
||||
|
||||
if self.backend == "llamacpp":
|
||||
if ".gguf" in self.model:
|
||||
creator, repo_name, file_name = self.model.split("/", 2)
|
||||
repo_id = f"{creator}/{repo_name}"
|
||||
else: # todo add auto-file-selection if no file is given
|
||||
raise ValueError("GGUF file_name must be provided for llama.cpp.")
|
||||
check_packages_installed([("llama-cpp-python", "llama_cpp")])
|
||||
self.client = models.llamacpp(repo_id, file_name, **self.model_kwargs)
|
||||
elif self.backend == "transformers":
|
||||
check_packages_installed(["transformers", "torch", "datasets"])
|
||||
self.client = models.transformers(self.model, **self.model_kwargs)
|
||||
elif self.backend == "transformers_vision":
|
||||
check_packages_installed(
|
||||
["transformers", "datasets", "torchvision", "PIL", "flash_attn"]
|
||||
)
|
||||
from transformers import LlavaNextForConditionalGeneration
|
||||
|
||||
if not hasattr(models, "transformers_vision"):
|
||||
raise ValueError(
|
||||
"transformers_vision backend is not supported, "
|
||||
"please install the correct outlines version."
|
||||
)
|
||||
self.client = models.transformers_vision(
|
||||
self.model,
|
||||
model_class=LlavaNextForConditionalGeneration,
|
||||
**self.model_kwargs,
|
||||
)
|
||||
elif self.backend == "vllm":
|
||||
if platform.system() == "Darwin":
|
||||
raise ValueError("vLLM backend is not supported on macOS.")
|
||||
check_packages_installed(["vllm"])
|
||||
self.client = models.vllm(self.model, **self.model_kwargs)
|
||||
elif self.backend == "mlxlm":
|
||||
check_packages_installed(["mlx"])
|
||||
self.client = models.mlxlm(self.model, **self.model_kwargs)
|
||||
else:
|
||||
raise ValueError(f"Unsupported backend: {self.backend}")
|
||||
|
||||
return self
|
||||
|
||||
@property
|
||||
def _llm_type(self) -> str:
|
||||
return "outlines"
|
||||
|
||||
@property
|
||||
def _default_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"max_tokens": self.max_tokens,
|
||||
"stop_at": self.stop,
|
||||
**self.model_kwargs,
|
||||
}
|
||||
|
||||
@property
|
||||
def _identifying_params(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"model": self.model,
|
||||
"backend": self.backend,
|
||||
"regex": self.regex,
|
||||
"type_constraints": self.type_constraints,
|
||||
"json_schema": self.json_schema,
|
||||
"grammar": self.grammar,
|
||||
**self._default_params,
|
||||
}
|
||||
|
||||
@property
|
||||
def _generator(self) -> Any:
|
||||
from outlines import generate
|
||||
|
||||
if self.custom_generator:
|
||||
return self.custom_generator
|
||||
if self.regex:
|
||||
return generate.regex(self.client, regex_str=self.regex)
|
||||
if self.type_constraints:
|
||||
return generate.format(self.client, python_type=self.type_constraints)
|
||||
if self.json_schema:
|
||||
return generate.json(self.client, schema_object=self.json_schema)
|
||||
if self.grammar:
|
||||
return generate.cfg(self.client, cfg_str=self.grammar)
|
||||
return generate.text(self.client)
|
||||
|
||||
def _call(
|
||||
self,
|
||||
prompt: str,
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> str:
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
response = ""
|
||||
if self.streaming:
|
||||
for chunk in self._stream(
|
||||
prompt=prompt,
|
||||
stop=params["stop_at"],
|
||||
run_manager=run_manager,
|
||||
**params,
|
||||
):
|
||||
response += chunk.text
|
||||
else:
|
||||
response = self._generator(prompt, **params)
|
||||
return response
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
prompt: str,
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[GenerationChunk]:
|
||||
params = {**self._default_params, **kwargs}
|
||||
if stop:
|
||||
params["stop_at"] = stop
|
||||
|
||||
for token in self._generator.stream(prompt, **params):
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(token)
|
||||
yield GenerationChunk(text=token)
|
||||
|
||||
@property
|
||||
def tokenizer(self) -> Any:
|
||||
"""Access the tokenizer for the underlying model.
|
||||
|
||||
.encode() to tokenize text.
|
||||
.decode() to convert tokens back to text.
|
||||
"""
|
||||
if hasattr(self.client, "tokenizer"):
|
||||
return self.client.tokenizer
|
||||
raise ValueError("Tokenizer not found")
|
||||
@@ -1798,7 +1798,9 @@ def _result_to_document(result: Dict) -> Document:
|
||||
fields_metadata = json.loads(result[FIELDS_METADATA])
|
||||
else:
|
||||
fields_metadata = {
|
||||
key: value for key, value in result.items() if key != FIELDS_CONTENT_VECTOR
|
||||
key: value
|
||||
for key, value in result.items()
|
||||
if key not in [FIELDS_CONTENT_VECTOR, FIELDS_CONTENT]
|
||||
}
|
||||
# IDs
|
||||
if FIELDS_ID in result:
|
||||
@@ -1806,7 +1808,7 @@ def _result_to_document(result: Dict) -> Document:
|
||||
else:
|
||||
fields_id = {}
|
||||
return Document(
|
||||
page_content=result.pop(FIELDS_CONTENT),
|
||||
page_content=result[FIELDS_CONTENT],
|
||||
metadata={
|
||||
**fields_id,
|
||||
**fields_metadata,
|
||||
|
||||
@@ -401,7 +401,7 @@ class OpenSearchVectorSearch(VectorStore):
|
||||
self.is_aoss = _is_aoss_enabled(http_auth=http_auth)
|
||||
self.client = _get_opensearch_client(opensearch_url, **kwargs)
|
||||
self.async_client = _get_async_opensearch_client(opensearch_url, **kwargs)
|
||||
self.engine = kwargs.get("engine")
|
||||
self.engine = kwargs.get("engine", "nmslib")
|
||||
|
||||
@property
|
||||
def embeddings(self) -> Embeddings:
|
||||
@@ -420,7 +420,7 @@ class OpenSearchVectorSearch(VectorStore):
|
||||
index_name = kwargs.get("index_name", self.index_name)
|
||||
text_field = kwargs.get("text_field", "text")
|
||||
dim = len(embeddings[0])
|
||||
engine = kwargs.get("engine", "nmslib")
|
||||
engine = kwargs.get("engine", self.engine)
|
||||
space_type = kwargs.get("space_type", "l2")
|
||||
ef_search = kwargs.get("ef_search", 512)
|
||||
ef_construction = kwargs.get("ef_construction", 512)
|
||||
@@ -461,7 +461,7 @@ class OpenSearchVectorSearch(VectorStore):
|
||||
index_name = kwargs.get("index_name", self.index_name)
|
||||
text_field = kwargs.get("text_field", "text")
|
||||
dim = len(embeddings[0])
|
||||
engine = kwargs.get("engine", "nmslib")
|
||||
engine = kwargs.get("engine", self.engine)
|
||||
space_type = kwargs.get("space_type", "l2")
|
||||
ef_search = kwargs.get("ef_search", 512)
|
||||
ef_construction = kwargs.get("ef_construction", 512)
|
||||
@@ -530,7 +530,7 @@ class OpenSearchVectorSearch(VectorStore):
|
||||
)
|
||||
|
||||
if is_appx_search:
|
||||
engine = kwargs.get("engine", "nmslib")
|
||||
engine = kwargs.get("engine", self.engine)
|
||||
space_type = kwargs.get("space_type", "l2")
|
||||
ef_search = kwargs.get("ef_search", 512)
|
||||
ef_construction = kwargs.get("ef_construction", 512)
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Type
|
||||
|
||||
import pytest
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.integration_tests import ChatModelIntegrationTests
|
||||
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
||||
|
||||
from langchain_community.chat_models.litellm import ChatLiteLLM
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Type, cast
|
||||
|
||||
import pytest
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.integration_tests import ChatModelIntegrationTests
|
||||
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
||||
from pydantic import SecretStr
|
||||
|
||||
from langchain_community.chat_models.moonshot import MoonshotChat
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# flake8: noqa
|
||||
"""Test ChatOutlines wrapper."""
|
||||
|
||||
from typing import Generator
|
||||
import re
|
||||
import platform
|
||||
import pytest
|
||||
|
||||
from langchain_community.chat_models.outlines import ChatOutlines
|
||||
from langchain_core.messages import AIMessage, HumanMessage, BaseMessage
|
||||
from langchain_core.messages import BaseMessageChunk
|
||||
from pydantic import BaseModel
|
||||
|
||||
from tests.unit_tests.callbacks.fake_callback_handler import FakeCallbackHandler
|
||||
|
||||
|
||||
MODEL = "microsoft/Phi-3-mini-4k-instruct"
|
||||
LLAMACPP_MODEL = "bartowski/qwen2.5-7b-ins-v3-GGUF/qwen2.5-7b-ins-v3-Q4_K_M.gguf"
|
||||
|
||||
BACKENDS = ["transformers", "llamacpp"]
|
||||
if platform.system() != "Darwin":
|
||||
BACKENDS.append("vllm")
|
||||
if platform.system() == "Darwin":
|
||||
BACKENDS.append("mlxlm")
|
||||
|
||||
|
||||
@pytest.fixture(params=BACKENDS)
|
||||
def chat_model(request: pytest.FixtureRequest) -> ChatOutlines:
|
||||
if request.param == "llamacpp":
|
||||
return ChatOutlines(model=LLAMACPP_MODEL, backend=request.param)
|
||||
else:
|
||||
return ChatOutlines(model=MODEL, backend=request.param)
|
||||
|
||||
|
||||
def test_chat_outlines_inference(chat_model: ChatOutlines) -> None:
|
||||
"""Test valid ChatOutlines inference."""
|
||||
messages = [HumanMessage(content="Say foo:")]
|
||||
output = chat_model.invoke(messages)
|
||||
assert isinstance(output, AIMessage)
|
||||
assert len(output.content) > 1
|
||||
|
||||
|
||||
def test_chat_outlines_streaming(chat_model: ChatOutlines) -> None:
|
||||
"""Test streaming tokens from ChatOutlines."""
|
||||
messages = [HumanMessage(content="How do you say 'hello' in Spanish?")]
|
||||
generator = chat_model.stream(messages)
|
||||
stream_results_string = ""
|
||||
assert isinstance(generator, Generator)
|
||||
|
||||
for chunk in generator:
|
||||
assert isinstance(chunk, BaseMessageChunk)
|
||||
if isinstance(chunk.content, str):
|
||||
stream_results_string += chunk.content
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid content type, only str is supported, "
|
||||
f"got {type(chunk.content)}"
|
||||
)
|
||||
assert len(stream_results_string.strip()) > 1
|
||||
|
||||
|
||||
def test_chat_outlines_streaming_callback(chat_model: ChatOutlines) -> None:
|
||||
"""Test that streaming correctly invokes on_llm_new_token callback."""
|
||||
MIN_CHUNKS = 5
|
||||
callback_handler = FakeCallbackHandler()
|
||||
chat_model.callbacks = [callback_handler]
|
||||
chat_model.verbose = True
|
||||
messages = [HumanMessage(content="Can you count to 10?")]
|
||||
chat_model.invoke(messages)
|
||||
assert callback_handler.llm_streams >= MIN_CHUNKS
|
||||
|
||||
|
||||
def test_chat_outlines_regex(chat_model: ChatOutlines) -> None:
|
||||
"""Test regex for generating a valid IP address"""
|
||||
ip_regex = r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
|
||||
chat_model.regex = ip_regex
|
||||
assert chat_model.regex == ip_regex
|
||||
|
||||
messages = [HumanMessage(content="What is the IP address of Google's DNS server?")]
|
||||
output = chat_model.invoke(messages)
|
||||
|
||||
assert isinstance(output, AIMessage)
|
||||
assert re.match(
|
||||
ip_regex, str(output.content)
|
||||
), f"Generated output '{output.content}' is not a valid IP address"
|
||||
|
||||
|
||||
def test_chat_outlines_type_constraints(chat_model: ChatOutlines) -> None:
|
||||
"""Test type constraints for generating an integer"""
|
||||
chat_model.type_constraints = int
|
||||
messages = [
|
||||
HumanMessage(
|
||||
content="What is the answer to life, the universe, and everything?"
|
||||
)
|
||||
]
|
||||
output = chat_model.invoke(messages)
|
||||
assert isinstance(int(str(output.content)), int)
|
||||
|
||||
|
||||
def test_chat_outlines_json(chat_model: ChatOutlines) -> None:
|
||||
"""Test json for generating a valid JSON object"""
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
|
||||
chat_model.json_schema = Person
|
||||
messages = [HumanMessage(content="Who are the main contributors to LangChain?")]
|
||||
output = chat_model.invoke(messages)
|
||||
person = Person.model_validate_json(str(output.content))
|
||||
assert isinstance(person, Person)
|
||||
|
||||
|
||||
def test_chat_outlines_grammar(chat_model: ChatOutlines) -> None:
|
||||
"""Test grammar for generating a valid arithmetic expression"""
|
||||
if chat_model.backend == "mlxlm":
|
||||
pytest.skip("MLX grammars not yet supported.")
|
||||
|
||||
chat_model.grammar = """
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
%import common.WS
|
||||
%ignore WS
|
||||
"""
|
||||
|
||||
messages = [HumanMessage(content="Give me a complex arithmetic expression:")]
|
||||
output = chat_model.invoke(messages)
|
||||
|
||||
# Validate the output is a non-empty string
|
||||
assert (
|
||||
isinstance(output.content, str) and output.content.strip()
|
||||
), "Output should be a non-empty string"
|
||||
|
||||
# Use a simple regex to check if the output contains basic arithmetic operations and numbers
|
||||
assert re.search(
|
||||
r"[\d\+\-\*/\(\)]+", output.content
|
||||
), f"Generated output '{output.content}' does not appear to be a valid arithmetic expression"
|
||||
|
||||
|
||||
def test_chat_outlines_with_structured_output(chat_model: ChatOutlines) -> None:
|
||||
"""Test that ChatOutlines can generate structured outputs"""
|
||||
|
||||
class AnswerWithJustification(BaseModel):
|
||||
"""An answer to the user question along with justification for the answer."""
|
||||
|
||||
answer: str
|
||||
justification: str
|
||||
|
||||
structured_chat_model = chat_model.with_structured_output(AnswerWithJustification)
|
||||
|
||||
result = structured_chat_model.invoke(
|
||||
"What weighs more, a pound of bricks or a pound of feathers?"
|
||||
)
|
||||
|
||||
assert isinstance(result, AnswerWithJustification)
|
||||
assert isinstance(result.answer, str)
|
||||
assert isinstance(result.justification, str)
|
||||
assert len(result.answer) > 0
|
||||
assert len(result.justification) > 0
|
||||
|
||||
structured_chat_model_with_raw = chat_model.with_structured_output(
|
||||
AnswerWithJustification, include_raw=True
|
||||
)
|
||||
|
||||
result_with_raw = structured_chat_model_with_raw.invoke(
|
||||
"What weighs more, a pound of bricks or a pound of feathers?"
|
||||
)
|
||||
|
||||
assert isinstance(result_with_raw, dict)
|
||||
assert "raw" in result_with_raw
|
||||
assert "parsed" in result_with_raw
|
||||
assert "parsing_error" in result_with_raw
|
||||
assert isinstance(result_with_raw["raw"], BaseMessage)
|
||||
assert isinstance(result_with_raw["parsed"], AnswerWithJustification)
|
||||
assert result_with_raw["parsing_error"] is None
|
||||
123
libs/community/tests/integration_tests/llms/test_outlines.py
Normal file
123
libs/community/tests/integration_tests/llms/test_outlines.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# flake8: noqa
|
||||
"""Test Outlines wrapper."""
|
||||
|
||||
from typing import Generator
|
||||
import re
|
||||
import platform
|
||||
import pytest
|
||||
|
||||
from langchain_community.llms.outlines import Outlines
|
||||
from pydantic import BaseModel
|
||||
|
||||
from tests.unit_tests.callbacks.fake_callback_handler import FakeCallbackHandler
|
||||
|
||||
|
||||
MODEL = "microsoft/Phi-3-mini-4k-instruct"
|
||||
LLAMACPP_MODEL = "microsoft/Phi-3-mini-4k-instruct-gguf/Phi-3-mini-4k-instruct-q4.gguf"
|
||||
|
||||
BACKENDS = ["transformers", "llamacpp"]
|
||||
if platform.system() != "Darwin":
|
||||
BACKENDS.append("vllm")
|
||||
if platform.system() == "Darwin":
|
||||
BACKENDS.append("mlxlm")
|
||||
|
||||
|
||||
@pytest.fixture(params=BACKENDS)
|
||||
def llm(request: pytest.FixtureRequest) -> Outlines:
|
||||
if request.param == "llamacpp":
|
||||
return Outlines(model=LLAMACPP_MODEL, backend=request.param, max_tokens=100)
|
||||
else:
|
||||
return Outlines(model=MODEL, backend=request.param, max_tokens=100)
|
||||
|
||||
|
||||
def test_outlines_inference(llm: Outlines) -> None:
|
||||
"""Test valid outlines inference."""
|
||||
output = llm.invoke("Say foo:")
|
||||
assert isinstance(output, str)
|
||||
assert len(output) > 1
|
||||
|
||||
|
||||
def test_outlines_streaming(llm: Outlines) -> None:
|
||||
"""Test streaming tokens from Outlines."""
|
||||
generator = llm.stream("Q: How do you say 'hello' in Spanish?\n\nA:")
|
||||
stream_results_string = ""
|
||||
assert isinstance(generator, Generator)
|
||||
|
||||
for chunk in generator:
|
||||
print(chunk)
|
||||
assert isinstance(chunk, str)
|
||||
stream_results_string += chunk
|
||||
print(stream_results_string)
|
||||
assert len(stream_results_string.strip()) > 1
|
||||
|
||||
|
||||
def test_outlines_streaming_callback(llm: Outlines) -> None:
|
||||
"""Test that streaming correctly invokes on_llm_new_token callback."""
|
||||
MIN_CHUNKS = 5
|
||||
|
||||
callback_handler = FakeCallbackHandler()
|
||||
llm.callbacks = [callback_handler]
|
||||
llm.verbose = True
|
||||
llm.invoke("Q: Can you count to 10? A:'1, ")
|
||||
assert callback_handler.llm_streams >= MIN_CHUNKS
|
||||
|
||||
|
||||
def test_outlines_regex(llm: Outlines) -> None:
|
||||
"""Test regex for generating a valid IP address"""
|
||||
ip_regex = r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)"
|
||||
llm.regex = ip_regex
|
||||
assert llm.regex == ip_regex
|
||||
|
||||
output = llm.invoke("Q: What is the IP address of googles dns server?\n\nA: ")
|
||||
|
||||
assert isinstance(output, str)
|
||||
|
||||
assert re.match(
|
||||
ip_regex, output
|
||||
), f"Generated output '{output}' is not a valid IP address"
|
||||
|
||||
|
||||
def test_outlines_type_constraints(llm: Outlines) -> None:
|
||||
"""Test type constraints for generating an integer"""
|
||||
llm.type_constraints = int
|
||||
output = llm.invoke(
|
||||
"Q: What is the answer to life, the universe, and everything?\n\nA: "
|
||||
)
|
||||
assert int(output)
|
||||
|
||||
|
||||
def test_outlines_json(llm: Outlines) -> None:
|
||||
"""Test json for generating a valid JSON object"""
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
|
||||
llm.json_schema = Person
|
||||
output = llm.invoke("Q: Who is the author of LangChain?\n\nA: ")
|
||||
person = Person.model_validate_json(output)
|
||||
assert isinstance(person, Person)
|
||||
|
||||
|
||||
def test_outlines_grammar(llm: Outlines) -> None:
|
||||
"""Test grammar for generating a valid arithmetic expression"""
|
||||
llm.grammar = """
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
%import common.WS
|
||||
%ignore WS
|
||||
"""
|
||||
|
||||
output = llm.invoke("Here is a complex arithmetic expression: ")
|
||||
|
||||
# Validate the output is a non-empty string
|
||||
assert (
|
||||
isinstance(output, str) and output.strip()
|
||||
), "Output should be a non-empty string"
|
||||
|
||||
# Use a simple regex to check if the output contains basic arithmetic operations and numbers
|
||||
assert re.search(
|
||||
r"[\d\+\-\*/\(\)]+", output
|
||||
), f"Generated output '{output}' does not appear to be a valid arithmetic expression"
|
||||
@@ -2,7 +2,7 @@ from typing import Generator, Tuple
|
||||
|
||||
import pytest
|
||||
from langchain_core.documents import Document
|
||||
from langchain_standard_tests.integration_tests.base_store import BaseStoreSyncTests
|
||||
from langchain_tests.integration_tests.base_store import BaseStoreSyncTests
|
||||
|
||||
from langchain_community.storage.mongodb import MongoDBByteStore, MongoDBStore
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from langchain_standard_tests.integration_tests.vectorstores import (
|
||||
from langchain_tests.integration_tests.vectorstores import (
|
||||
AsyncReadWriteTestSuite,
|
||||
ReadWriteTestSuite,
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ from langchain_core.messages import (
|
||||
SystemMessage,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_standard_tests.unit_tests import ChatModelUnitTests
|
||||
from langchain_tests.unit_tests import ChatModelUnitTests
|
||||
|
||||
from langchain_community.chat_models.cloudflare_workersai import (
|
||||
ChatCloudflareWorkersAI,
|
||||
|
||||
@@ -36,6 +36,7 @@ EXPECTED_ALL = [
|
||||
"ChatOCIModelDeploymentTGI",
|
||||
"ChatOllama",
|
||||
"ChatOpenAI",
|
||||
"ChatOutlines",
|
||||
"ChatPerplexity",
|
||||
"ChatPremAI",
|
||||
"ChatSambaNovaCloud",
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Type
|
||||
|
||||
import pytest
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.unit_tests import ChatModelUnitTests
|
||||
from langchain_tests.unit_tests import ChatModelUnitTests
|
||||
|
||||
from langchain_community.chat_models.litellm import ChatLiteLLM
|
||||
|
||||
|
||||
91
libs/community/tests/unit_tests/chat_models/test_outlines.py
Normal file
91
libs/community/tests/unit_tests/chat_models/test_outlines.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import pytest
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from langchain_community.chat_models.outlines import ChatOutlines
|
||||
|
||||
|
||||
def test_chat_outlines_initialization(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
|
||||
chat = ChatOutlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct",
|
||||
max_tokens=42,
|
||||
stop=["\n"],
|
||||
)
|
||||
assert chat.model == "microsoft/Phi-3-mini-4k-instruct"
|
||||
assert chat.max_tokens == 42
|
||||
assert chat.backend == "transformers"
|
||||
assert chat.stop == ["\n"]
|
||||
|
||||
|
||||
def test_chat_outlines_backend_llamacpp(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
chat = ChatOutlines(
|
||||
model="TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf",
|
||||
backend="llamacpp",
|
||||
)
|
||||
assert chat.backend == "llamacpp"
|
||||
|
||||
|
||||
def test_chat_outlines_backend_vllm(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
chat = ChatOutlines(model="microsoft/Phi-3-mini-4k-instruct", backend="vllm")
|
||||
assert chat.backend == "vllm"
|
||||
|
||||
|
||||
def test_chat_outlines_backend_mlxlm(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
chat = ChatOutlines(model="microsoft/Phi-3-mini-4k-instruct", backend="mlxlm")
|
||||
assert chat.backend == "mlxlm"
|
||||
|
||||
|
||||
def test_chat_outlines_with_regex(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
regex = r"\d{3}-\d{3}-\d{4}"
|
||||
chat = ChatOutlines(model="microsoft/Phi-3-mini-4k-instruct", regex=regex)
|
||||
assert chat.regex == regex
|
||||
|
||||
|
||||
def test_chat_outlines_with_type_constraints(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
chat = ChatOutlines(model="microsoft/Phi-3-mini-4k-instruct", type_constraints=int)
|
||||
assert chat.type_constraints == int # noqa
|
||||
|
||||
|
||||
def test_chat_outlines_with_json_schema(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
|
||||
class TestSchema(BaseModel):
|
||||
name: str = Field(description="A person's name")
|
||||
age: int = Field(description="A person's age")
|
||||
|
||||
chat = ChatOutlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct", json_schema=TestSchema
|
||||
)
|
||||
assert chat.json_schema == TestSchema
|
||||
|
||||
|
||||
def test_chat_outlines_with_grammar(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
|
||||
grammar = """
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
"""
|
||||
chat = ChatOutlines(model="microsoft/Phi-3-mini-4k-instruct", grammar=grammar)
|
||||
assert chat.grammar == grammar
|
||||
|
||||
|
||||
def test_raise_for_multiple_output_constraints(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ChatOutlines, "build_client", lambda self: self)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
ChatOutlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct",
|
||||
type_constraints=int,
|
||||
regex=r"\d{3}-\d{3}-\d{4}",
|
||||
)
|
||||
@@ -67,6 +67,7 @@ EXPECT_ALL = [
|
||||
"OpenAIChat",
|
||||
"OpenLLM",
|
||||
"OpenLM",
|
||||
"Outlines",
|
||||
"PaiEasEndpoint",
|
||||
"Petals",
|
||||
"PipelineAI",
|
||||
|
||||
92
libs/community/tests/unit_tests/llms/test_outlines.py
Normal file
92
libs/community/tests/unit_tests/llms/test_outlines.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import pytest
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
|
||||
from langchain_community.llms.outlines import Outlines
|
||||
|
||||
|
||||
def test_outlines_initialization(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
|
||||
llm = Outlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct",
|
||||
max_tokens=42,
|
||||
stop=["\n"],
|
||||
)
|
||||
assert llm.model == "microsoft/Phi-3-mini-4k-instruct"
|
||||
assert llm.max_tokens == 42
|
||||
assert llm.backend == "transformers"
|
||||
assert llm.stop == ["\n"]
|
||||
|
||||
|
||||
def test_outlines_backend_llamacpp(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
llm = Outlines(
|
||||
model="TheBloke/Llama-2-7B-Chat-GGUF/llama-2-7b-chat.Q4_K_M.gguf",
|
||||
backend="llamacpp",
|
||||
)
|
||||
assert llm.backend == "llamacpp"
|
||||
|
||||
|
||||
def test_outlines_backend_vllm(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", backend="vllm")
|
||||
assert llm.backend == "vllm"
|
||||
|
||||
|
||||
def test_outlines_backend_mlxlm(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", backend="mlxlm")
|
||||
assert llm.backend == "mlxlm"
|
||||
|
||||
|
||||
def test_outlines_with_regex(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
regex = r"\d{3}-\d{3}-\d{4}"
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", regex=regex)
|
||||
assert llm.regex == regex
|
||||
|
||||
|
||||
def test_outlines_with_type_constraints(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", type_constraints=int)
|
||||
assert llm.type_constraints == int # noqa
|
||||
|
||||
|
||||
def test_outlines_with_json_schema(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class TestSchema(BaseModel):
|
||||
name: str = Field(description="A person's name")
|
||||
age: int = Field(description="A person's age")
|
||||
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", json_schema=TestSchema)
|
||||
assert llm.json_schema == TestSchema
|
||||
|
||||
|
||||
def test_outlines_with_grammar(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
grammar = """
|
||||
?start: expression
|
||||
?expression: term (("+" | "-") term)*
|
||||
?term: factor (("*" | "/") factor)*
|
||||
?factor: NUMBER | "-" factor | "(" expression ")"
|
||||
%import common.NUMBER
|
||||
"""
|
||||
llm = Outlines(model="microsoft/Phi-3-mini-4k-instruct", grammar=grammar)
|
||||
assert llm.grammar == grammar
|
||||
|
||||
|
||||
def test_raise_for_multiple_output_constraints(monkeypatch: MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(Outlines, "build_client", lambda self: self)
|
||||
with pytest.raises(ValueError):
|
||||
Outlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct",
|
||||
type_constraints=int,
|
||||
regex=r"\d{3}-\d{3}-\d{4}",
|
||||
)
|
||||
Outlines(
|
||||
model="microsoft/Phi-3-mini-4k-instruct",
|
||||
type_constraints=int,
|
||||
regex=r"\d{3}-\d{3}-\d{4}",
|
||||
)
|
||||
@@ -3,7 +3,7 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
from langchain_core.documents import Document
|
||||
from langchain_standard_tests.integration_tests.vectorstores import (
|
||||
from langchain_tests.integration_tests.vectorstores import (
|
||||
AsyncReadWriteTestSuite,
|
||||
ReadWriteTestSuite,
|
||||
)
|
||||
|
||||
@@ -47,7 +47,6 @@ def print_sys_info(*, additional_pkgs: Sequence[str] = ()) -> None:
|
||||
# Packages that do not start with "langchain" prefix.
|
||||
other_langchain_packages = [
|
||||
"langserve",
|
||||
"langgraph",
|
||||
"langsmith",
|
||||
]
|
||||
|
||||
@@ -55,8 +54,17 @@ def print_sys_info(*, additional_pkgs: Sequence[str] = ()) -> None:
|
||||
name for _, name, _ in pkgutil.iter_modules() if name.startswith("langchain")
|
||||
]
|
||||
|
||||
langgraph_pkgs = [
|
||||
name for _, name, _ in pkgutil.iter_modules() if name.startswith("langgraph")
|
||||
]
|
||||
|
||||
all_packages = sorted(
|
||||
set(langchain_pkgs + other_langchain_packages + list(additional_pkgs))
|
||||
set(
|
||||
langchain_pkgs
|
||||
+ langgraph_pkgs
|
||||
+ other_langchain_packages
|
||||
+ list(additional_pkgs)
|
||||
)
|
||||
)
|
||||
|
||||
# Always surface these packages to the top
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from collections.abc import AsyncGenerator, Generator
|
||||
|
||||
import pytest
|
||||
from langchain_standard_tests.integration_tests.indexer import (
|
||||
from langchain_tests.integration_tests.indexer import (
|
||||
AsyncDocumentIndexTestSuite,
|
||||
DocumentIndexerTestSuite,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from langchain_standard_tests.integration_tests.base_store import (
|
||||
from langchain_tests.integration_tests.base_store import (
|
||||
BaseStoreAsyncTests,
|
||||
BaseStoreSyncTests,
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from pathlib import Path
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from langchain_standard_tests.integration_tests.vectorstores import (
|
||||
from langchain_tests.integration_tests.vectorstores import (
|
||||
AsyncReadWriteTestSuite,
|
||||
ReadWriteTestSuite,
|
||||
)
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# This is a Dockerfile for running unit tests
|
||||
|
||||
ARG POETRY_HOME=/opt/poetry
|
||||
|
||||
# Use the Python base image
|
||||
FROM python:3.11.2-bullseye AS builder
|
||||
|
||||
# Define the version of Poetry to install (default is 1.4.2)
|
||||
ARG POETRY_VERSION=1.4.2
|
||||
|
||||
# Define the directory to install Poetry to (default is /opt/poetry)
|
||||
ARG POETRY_HOME
|
||||
|
||||
# Create a Python virtual environment for Poetry and install it
|
||||
RUN python3 -m venv ${POETRY_HOME} && \
|
||||
$POETRY_HOME/bin/pip install --upgrade pip && \
|
||||
$POETRY_HOME/bin/pip install poetry==${POETRY_VERSION}
|
||||
|
||||
# Test if Poetry is installed in the expected path
|
||||
RUN echo "Poetry version:" && $POETRY_HOME/bin/poetry --version
|
||||
|
||||
# Set the working directory for the app
|
||||
WORKDIR /app
|
||||
|
||||
# Use a multi-stage build to install dependencies
|
||||
FROM builder AS dependencies
|
||||
|
||||
ARG POETRY_HOME
|
||||
|
||||
# Copy only the dependency files for installation
|
||||
COPY pyproject.toml poetry.lock poetry.toml ./
|
||||
|
||||
# Install the Poetry dependencies (this layer will be cached as long as the dependencies don't change)
|
||||
RUN $POETRY_HOME/bin/poetry install --no-interaction --no-ansi --with test
|
||||
|
||||
# Use a multi-stage build to run tests
|
||||
FROM dependencies AS tests
|
||||
|
||||
# Copy the rest of the app source code (this layer will be invalidated and rebuilt whenever the source code changes)
|
||||
COPY . .
|
||||
|
||||
RUN /opt/poetry/bin/poetry install --no-interaction --no-ansi --with test
|
||||
|
||||
# Set the entrypoint to run tests using Poetry
|
||||
ENTRYPOINT ["/opt/poetry/bin/poetry", "run", "pytest"]
|
||||
|
||||
# Set the default command to run all unit tests
|
||||
CMD ["tests/unit_tests"]
|
||||
@@ -1,63 +0,0 @@
|
||||
# This is a Dockerfile for the Development Container
|
||||
|
||||
# Use the Python base image
|
||||
ARG VARIANT="3.11-bullseye"
|
||||
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT} AS langchain-dev-base
|
||||
|
||||
USER vscode
|
||||
|
||||
# Define the version of Poetry to install (default is 1.4.2)
|
||||
# Define the directory of python virtual environment
|
||||
ARG PYTHON_VIRTUALENV_HOME=/home/vscode/langchain-py-env \
|
||||
POETRY_VERSION=1.3.2
|
||||
|
||||
ENV POETRY_VIRTUALENVS_IN_PROJECT=false \
|
||||
POETRY_NO_INTERACTION=true
|
||||
|
||||
# Install Poetry outside of the v`irtual environment to avoid conflicts
|
||||
RUN python3 -m pip install --user pipx && \
|
||||
python3 -m pipx ensurepath && \
|
||||
pipx install poetry==${POETRY_VERSION}
|
||||
|
||||
# Create a Python virtual environment for the project
|
||||
RUN python3 -m venv ${PYTHON_VIRTUALENV_HOME} && \
|
||||
$PYTHON_VIRTUALENV_HOME/bin/pip install --upgrade pip
|
||||
|
||||
ENV PATH="$PYTHON_VIRTUALENV_HOME/bin:$PATH" \
|
||||
VIRTUAL_ENV=$PYTHON_VIRTUALENV_HOME
|
||||
|
||||
# Setup for bash
|
||||
RUN poetry completions bash >> /home/vscode/.bash_completion && \
|
||||
echo "export PATH=$PYTHON_VIRTUALENV_HOME/bin:$PATH" >> ~/.bashrc
|
||||
|
||||
# Set the working directory for the app
|
||||
WORKDIR /workspaces/langchain
|
||||
|
||||
# Use a multi-stage build to install dependencies
|
||||
FROM langchain-dev-base AS langchain-dev-dependencies
|
||||
|
||||
ARG PYTHON_VIRTUALENV_HOME
|
||||
|
||||
# Copy only the dependency files for installation
|
||||
COPY libs/langchain/pyproject.toml libs/langchain/poetry.toml libs/langchain/poetry.lock ./
|
||||
|
||||
# Copy the langchain library for installation
|
||||
COPY libs/langchain/ libs/langchain/
|
||||
|
||||
# Copy the core library for installation
|
||||
COPY libs/core ../core
|
||||
|
||||
# Copy the community library for installation
|
||||
COPY libs/community/ ../community/
|
||||
|
||||
# Copy the text-splitters library for installation
|
||||
COPY libs/text-splitters/ ../text-splitters/
|
||||
|
||||
# Copy the partners library for installation
|
||||
COPY libs/partners ../partners/
|
||||
|
||||
# Copy the standard-tests library for installation
|
||||
COPY libs/standard-tests ../standard-tests/
|
||||
|
||||
# Install the Poetry dependencies (this layer will be cached as long as the dependencies don't change)
|
||||
RUN poetry install --no-interaction --no-ansi --with dev,test,docs
|
||||
@@ -9,7 +9,7 @@ from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
|
||||
|
||||
FINAL_ANSWER_ACTION = "Final Answer:"
|
||||
MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE = (
|
||||
"Invalid Format: Missing 'Action:' after 'Thought:"
|
||||
"Invalid Format: Missing 'Action:' after 'Thought:'"
|
||||
)
|
||||
MISSING_ACTION_INPUT_AFTER_ACTION_ERROR_MESSAGE = (
|
||||
"Invalid Format: Missing 'Action Input:' after 'Action:'"
|
||||
|
||||
@@ -5,7 +5,7 @@ from langchain_core.language_models import BaseChatModel
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_standard_tests.integration_tests import ChatModelIntegrationTests
|
||||
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langchain.chat_models import init_chat_model
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Dict, List, Literal, Type, cast
|
||||
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_standard_tests.integration_tests import ChatModelIntegrationTests
|
||||
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from typing import Type
|
||||
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.unit_tests import ChatModelUnitTests
|
||||
from langchain_tests.unit_tests import ChatModelUnitTests
|
||||
|
||||
from langchain_anthropic import ChatAnthropic
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"""Test Chroma functionality."""
|
||||
|
||||
import uuid
|
||||
from typing import Generator
|
||||
from typing import (
|
||||
Generator,
|
||||
cast,
|
||||
)
|
||||
|
||||
import chromadb
|
||||
import pytest # type: ignore[import-not-found]
|
||||
import requests
|
||||
from chromadb.api.client import SharedSystemClient
|
||||
from chromadb.api.types import Embeddable
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.embeddings.fake import FakeEmbeddings as Fak
|
||||
|
||||
@@ -17,6 +21,15 @@ from tests.integration_tests.fake_embeddings import (
|
||||
)
|
||||
|
||||
|
||||
class MyEmbeddingFunction:
|
||||
def __init__(self, fak: Fak):
|
||||
self.fak = fak
|
||||
|
||||
def __call__(self, input: Embeddable) -> list[list[float]]:
|
||||
texts = cast(list[str], input)
|
||||
return self.fak.embed_documents(texts=texts)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client() -> Generator[chromadb.ClientAPI, None, None]:
|
||||
SharedSystemClient.clear_system_cache()
|
||||
@@ -254,8 +267,8 @@ def test_chroma_update_document() -> None:
|
||||
# Assert that the updated document is returned by the search
|
||||
assert output == [Document(page_content=updated_content, metadata={"page": "0"})]
|
||||
|
||||
assert new_embedding == embedding.embed_documents([updated_content])[0]
|
||||
assert new_embedding != old_embedding
|
||||
assert list(new_embedding) == list(embedding.embed_documents([updated_content])[0])
|
||||
assert list(new_embedding) != list(old_embedding)
|
||||
|
||||
|
||||
# TODO: RELEVANCE SCORE IS BROKEN. FIX TEST
|
||||
@@ -341,17 +354,17 @@ def batch_support_chroma_version() -> bool:
|
||||
)
|
||||
def test_chroma_large_batch() -> None:
|
||||
client = chromadb.HttpClient()
|
||||
embedding_function = Fak(size=255)
|
||||
embedding_function = MyEmbeddingFunction(fak=Fak(size=255))
|
||||
col = client.get_or_create_collection(
|
||||
"my_collection",
|
||||
embedding_function=embedding_function.embed_documents, # type: ignore
|
||||
embedding_function=embedding_function, # type: ignore
|
||||
)
|
||||
docs = ["This is a test document"] * (client.max_batch_size + 100) # type: ignore
|
||||
docs = ["This is a test document"] * (client.get_max_batch_size() + 100) # type: ignore
|
||||
db = Chroma.from_texts(
|
||||
client=client,
|
||||
collection_name=col.name,
|
||||
texts=docs,
|
||||
embedding=embedding_function,
|
||||
embedding=embedding_function.fak,
|
||||
ids=[str(uuid.uuid4()) for _ in range(len(docs))],
|
||||
)
|
||||
|
||||
@@ -369,18 +382,18 @@ def test_chroma_large_batch() -> None:
|
||||
)
|
||||
def test_chroma_large_batch_update() -> None:
|
||||
client = chromadb.HttpClient()
|
||||
embedding_function = Fak(size=255)
|
||||
embedding_function = MyEmbeddingFunction(fak=Fak(size=255))
|
||||
col = client.get_or_create_collection(
|
||||
"my_collection",
|
||||
embedding_function=embedding_function.embed_documents, # type: ignore
|
||||
embedding_function=embedding_function, # type: ignore
|
||||
)
|
||||
docs = ["This is a test document"] * (client.max_batch_size + 100) # type: ignore
|
||||
docs = ["This is a test document"] * (client.get_max_batch_size() + 100) # type: ignore
|
||||
ids = [str(uuid.uuid4()) for _ in range(len(docs))]
|
||||
db = Chroma.from_texts(
|
||||
client=client,
|
||||
collection_name=col.name,
|
||||
texts=docs,
|
||||
embedding=embedding_function,
|
||||
embedding=embedding_function.fak,
|
||||
ids=ids,
|
||||
)
|
||||
new_docs = [
|
||||
@@ -408,7 +421,7 @@ def test_chroma_legacy_batching() -> None:
|
||||
embedding_function = Fak(size=255)
|
||||
col = client.get_or_create_collection(
|
||||
"my_collection",
|
||||
embedding_function=embedding_function.embed_documents, # type: ignore
|
||||
embedding_function=MyEmbeddingFunction, # type: ignore
|
||||
)
|
||||
docs = ["This is a test document"] * 100
|
||||
db = Chroma.from_texts(
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Type
|
||||
|
||||
import pytest
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.integration_tests import ( # type: ignore[import-not-found]
|
||||
from langchain_tests.integration_tests import ( # type: ignore[import-not-found]
|
||||
ChatModelIntegrationTests, # type: ignore[import-not-found]
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from typing import Tuple, Type
|
||||
|
||||
from langchain_core.embeddings import Embeddings
|
||||
from langchain_standard_tests.unit_tests.embeddings import EmbeddingsUnitTests
|
||||
from langchain_tests.unit_tests.embeddings import EmbeddingsUnitTests
|
||||
|
||||
from langchain_fireworks import FireworksEmbeddings
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from typing import Tuple, Type
|
||||
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.unit_tests import ( # type: ignore[import-not-found]
|
||||
from langchain_tests.unit_tests import ( # type: ignore[import-not-found]
|
||||
ChatModelUnitTests, # type: ignore[import-not-found]
|
||||
)
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ def test_json_mode_structured_output() -> None:
|
||||
|
||||
|
||||
def test_tool_calling_no_arguments() -> None:
|
||||
# Note: this is a variant of a test in langchain_standard_tests
|
||||
# Note: this is a variant of a test in langchain_tests
|
||||
# that as of 2024-08-19 fails with "Failed to call a function. Please
|
||||
# adjust your prompt." when `tool_choice="any"` is specified, but
|
||||
# passes when `tool_choice` is not specified.
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Optional, Type
|
||||
import pytest
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_core.rate_limiters import InMemoryRateLimiter
|
||||
from langchain_standard_tests.integration_tests import (
|
||||
from langchain_tests.integration_tests import (
|
||||
ChatModelIntegrationTests,
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from typing import Type
|
||||
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_standard_tests.unit_tests.chat_models import (
|
||||
from langchain_tests.unit_tests.chat_models import (
|
||||
ChatModelUnitTests,
|
||||
)
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings):
|
||||
texts = [text.replace("\n", " ") for text in texts]
|
||||
_model_kwargs = self.model_kwargs or {}
|
||||
responses = await self.async_client.post(
|
||||
json={"inputs": texts, "parameters": _model_kwargs}, task=self.task
|
||||
json={"inputs": texts, **_model_kwargs}, task=self.task
|
||||
)
|
||||
return json.loads(responses.decode())
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user