diff --git a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py index c43712325c7..0e077cd4856 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py @@ -29,10 +29,10 @@ class Chat__ModuleName__(BaseChatModel): Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Populate with relevant params. Key init args — completion params: @@ -57,216 +57,214 @@ class Chat__ModuleName__(BaseChatModel): # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from __module_name__ import Chat__ModuleName__ - from __module_name__ import Chat__ModuleName__ - - llm = Chat__ModuleName__( - model="...", - temperature=0, - max_tokens=None, - timeout=None, - max_retries=2, - # api_key="...", - # other params... - ) + llm = Chat__ModuleName__( + model="...", + temperature=0, + max_tokens=None, + timeout=None, + max_retries=2, + # api_key="...", + # other params... + ) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ("system", "You are a helpful translator. Translate the user sentence to French."), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` - messages = [ - ("system", "You are a helpful translator. Translate the user sentence to French."), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if token-level streaming isn't supported. Stream: - .. code-block:: python + ```python + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` - for chunk in llm.stream(messages): - print(chunk.text, end="") + ```python + # TODO: Example output. + ``` - .. code-block:: python + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` - # TODO: Example output. - - .. code-block:: python - - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if native async isn't supported. Async: - .. code-block:: python + ```python + await llm.ainvoke(messages) - await llm.ainvoke(messages) + # stream: + # async for chunk in (await llm.astream(messages)) - # stream: - # async for chunk in (await llm.astream(messages)) - - # batch: - # await llm.abatch([messages]) - - .. code-block:: python - - # TODO: Example output. + # batch: + # await llm.abatch([messages]) + ``` + ```python + # TODO: Example output. + ``` # TODO: Delete if .bind_tools() isn't supported. Tool calling: - .. code-block:: python + ```python + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") + ai_msg.tool_calls + ``` - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") - ai_msg.tool_calls - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` See `Chat__ModuleName__.bind_tools()` method for more. # TODO: Delete if .with_structured_output() isn't supported. Structured output: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field + class Joke(BaseModel): + '''Joke to tell user.''' - 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") + rating: int | None = Field(description="How funny the joke is, from 1 to 10") - setup: str = Field(description="The setup of the joke") - punchline: str = Field(description="The punchline to the joke") - rating: int | None = Field(description="How funny the joke is, from 1 to 10") + structured_llm = llm.with_structured_output(Joke) + structured_llm.invoke("Tell me a joke about cats") + ``` - structured_llm = llm.with_structured_output(Joke) - structured_llm.invoke("Tell me a joke about cats") - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` See `Chat__ModuleName__.with_structured_output()` for more. # TODO: Delete if JSON mode response format isn't supported. JSON mode: - .. code-block:: python + ```python + # TODO: Replace with appropriate bind arg. + json_llm = llm.bind(response_format={"type": "json_object"}) + ai_msg = json_llm.invoke("Return a JSON object with key 'random_ints' and a value of 10 random ints in [0-99]") + ai_msg.content + ``` - # TODO: Replace with appropriate bind arg. - json_llm = llm.bind(response_format={"type": "json_object"}) - ai_msg = json_llm.invoke("Return a JSON object with key 'random_ints' and a value of 10 random ints in [0-99]") - ai_msg.content - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if image inputs aren't supported. Image input: - .. code-block:: python + ```python + import base64 + import httpx + from langchain_core.messages import HumanMessage - import base64 - import httpx - from langchain_core.messages import HumanMessage + image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") + # TODO: Replace with appropriate message content format. + message = HumanMessage( + content=[ + {"type": "text", "text": "describe the weather in this image"}, + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ], + ) + ai_msg = llm.invoke([message]) + ai_msg.content + ``` - image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" - image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") - # TODO: Replace with appropriate message content format. - message = HumanMessage( - content=[ - {"type": "text", "text": "describe the weather in this image"}, - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ], - ) - ai_msg = llm.invoke([message]) - ai_msg.content - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if audio inputs aren't supported. Audio input: - .. code-block:: python + ```python + # TODO: Example input + ``` - # TODO: Example input - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Delete if video inputs aren't supported. Video input: - .. code-block:: python + ```python + # TODO: Example input + ``` - # TODO: Example input - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Delete if token usage metadata isn't supported. Token usage: - .. code-block:: python + ```python + ai_msg = llm.invoke(messages) + ai_msg.usage_metadata + ``` - ai_msg = llm.invoke(messages) - ai_msg.usage_metadata - - .. code-block:: python - - {'input_tokens': 28, 'output_tokens': 5, 'total_tokens': 33} + ```python + {'input_tokens': 28, 'output_tokens': 5, 'total_tokens': 33} + ``` # TODO: Delete if logprobs aren't supported. Logprobs: - .. code-block:: python - - # TODO: Replace with appropriate bind arg. - logprobs_llm = llm.bind(logprobs=True) - ai_msg = logprobs_llm.invoke(messages) - ai_msg.response_metadata["logprobs"] - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Replace with appropriate bind arg. + logprobs_llm = llm.bind(logprobs=True) + ai_msg = logprobs_llm.invoke(messages) + ai_msg.response_metadata["logprobs"] + ``` + ```python + # TODO: Example output. + ``` Response metadata - .. code-block:: python + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` """ # noqa: E501 model_name: str = Field(alias="model") diff --git a/libs/cli/langchain_cli/integration_template/integration_template/document_loaders.py b/libs/cli/langchain_cli/integration_template/integration_template/document_loaders.py index 0d879f37ba1..c8be4a8db94 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/document_loaders.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/document_loaders.py @@ -17,52 +17,52 @@ class __ModuleName__Loader(BaseLoader): Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from langchain_community.document_loaders import __ModuleName__Loader - from langchain_community.document_loaders import __ModuleName__Loader - - loader = __ModuleName__Loader( - # required params = ... - # other params = ... - ) + loader = __ModuleName__Loader( + # required params = ... + # other params = ... + ) + ``` Lazy load: - .. code-block:: python + ```python + docs = [] + docs_lazy = loader.lazy_load() - docs = [] - docs_lazy = loader.lazy_load() + # async variant: + # docs_lazy = await loader.alazy_load() - # async variant: - # docs_lazy = await loader.alazy_load() + for doc in docs_lazy: + docs.append(doc) + print(docs[0].page_content[:100]) + print(docs[0].metadata) + ``` - for doc in docs_lazy: - docs.append(doc) - print(docs[0].page_content[:100]) - print(docs[0].metadata) - - .. code-block:: python - - TODO: Example output + ```python + TODO: Example output + ``` # TODO: Delete if async load is not implemented Async load: - .. code-block:: python + ```python + docs = await loader.aload() + print(docs[0].page_content[:100]) + print(docs[0].metadata) + ``` - docs = await loader.aload() - print(docs[0].page_content[:100]) - print(docs[0].metadata) - - .. code-block:: python - - TODO: Example output + ```python + TODO: Example output + ``` """ # TODO: This method must be implemented to load documents. diff --git a/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py b/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py index ff9d8482f96..e21c3d2ee9f 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py @@ -11,10 +11,10 @@ class __ModuleName__Embeddings(Embeddings): Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Populate with relevant params. Key init args — completion params: @@ -25,50 +25,50 @@ class __ModuleName__Embeddings(Embeddings): # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from __module_name__ import __ModuleName__Embeddings - from __module_name__ import __ModuleName__Embeddings - - embed = __ModuleName__Embeddings( - model="...", - # api_key="...", - # other params... - ) + embed = __ModuleName__Embeddings( + model="...", + # api_key="...", + # other params... + ) + ``` Embed single text: - .. code-block:: python + ```python + input_text = "The meaning of life is 42" + embed.embed_query(input_text) + ``` - input_text = "The meaning of life is 42" - embed.embed_query(input_text) - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if token-level streaming isn't supported. Embed multiple text: - .. code-block:: python + ```python + input_texts = ["Document 1...", "Document 2..."] + embed.embed_documents(input_texts) + ``` - input_texts = ["Document 1...", "Document 2..."] - embed.embed_documents(input_texts) - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` # TODO: Delete if native async isn't supported. Async: - .. code-block:: python + ```python + await embed.aembed_query(input_text) - await embed.aembed_query(input_text) + # multiple: + # await embed.aembed_documents(input_texts) + ``` - # multiple: - # await embed.aembed_documents(input_texts) - - .. code-block:: python - - # TODO: Example output. + ```python + # TODO: Example output. + ``` """ def __init__(self, model: str): diff --git a/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py b/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py index 5888f43fbcb..48c5f735788 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py @@ -17,10 +17,10 @@ class __ModuleName__Retriever(BaseRetriever): Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Populate with relevant params. Key init args: @@ -31,58 +31,58 @@ class __ModuleName__Retriever(BaseRetriever): # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from __package_name__ import __ModuleName__Retriever - from __package_name__ import __ModuleName__Retriever - - retriever = __ModuleName__Retriever( - # ... - ) + retriever = __ModuleName__Retriever( + # ... + ) + ``` Usage: - .. code-block:: python + ```python + query = "..." - query = "..." + retriever.invoke(query) + ``` - retriever.invoke(query) - - .. code-block:: - - # TODO: Example output. + ```txt + # TODO: Example output. + ``` Use within a chain: - .. code-block:: python + ```python + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.runnables import RunnablePassthrough + from langchain_openai import ChatOpenAI - from langchain_core.output_parsers import StrOutputParser - from langchain_core.prompts import ChatPromptTemplate - from langchain_core.runnables import RunnablePassthrough - from langchain_openai import ChatOpenAI + prompt = ChatPromptTemplate.from_template( + \"\"\"Answer the question based only on the context provided. - prompt = ChatPromptTemplate.from_template( - \"\"\"Answer the question based only on the context provided. + Context: {context} - Context: {context} + Question: {question}\"\"\" + ) - Question: {question}\"\"\" - ) + llm = ChatOpenAI(model="gpt-3.5-turbo-0125") - llm = ChatOpenAI(model="gpt-3.5-turbo-0125") + def format_docs(docs): + return "\\n\\n".join(doc.page_content for doc in docs) - def format_docs(docs): - return "\\n\\n".join(doc.page_content for doc in docs) + chain = ( + {"context": retriever | format_docs, "question": RunnablePassthrough()} + | prompt + | llm + | StrOutputParser() + ) - chain = ( - {"context": retriever | format_docs, "question": RunnablePassthrough()} - | prompt - | llm - | StrOutputParser() - ) + chain.invoke("...") + ``` - chain.invoke("...") - - .. code-block:: - - # TODO: Example output. + ``` + # TODO: Example output. + ``` """ diff --git a/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py b/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py index 95dd4530b53..370b5d6b6c8 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py @@ -15,10 +15,10 @@ class __ModuleName__Toolkit(BaseToolkit): Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Populate with relevant params. Key init args: @@ -29,42 +29,42 @@ class __ModuleName__Toolkit(BaseToolkit): # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from __package_name__ import __ModuleName__Toolkit - from __package_name__ import __ModuleName__Toolkit - - toolkit = __ModuleName__Toolkit( - # ... - ) + toolkit = __ModuleName__Toolkit( + # ... + ) + ``` Tools: - .. code-block:: python + ```python + toolkit.get_tools() + ``` - toolkit.get_tools() - - .. code-block:: - - # TODO: Example output. + ```txt + # TODO: Example output. + ``` Use within an agent: - .. code-block:: python + ```python + from langgraph.prebuilt import create_react_agent - from langgraph.prebuilt import create_react_agent + agent_executor = create_react_agent(llm, tools) - agent_executor = create_react_agent(llm, tools) + example_query = "..." - example_query = "..." + events = agent_executor.stream( + {"messages": [("user", example_query)]}, + stream_mode="values", + ) + for event in events: + event["messages"][-1].pretty_print() + ``` - events = agent_executor.stream( - {"messages": [("user", example_query)]}, - stream_mode="values", - ) - for event in events: - event["messages"][-1].pretty_print() - - .. code-block:: - - # TODO: Example output. + ```txt + # TODO: Example output. + ``` """ diff --git a/libs/cli/langchain_cli/integration_template/integration_template/tools.py b/libs/cli/langchain_cli/integration_template/integration_template/tools.py index cd9fca64b13..19cc822ddd0 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/tools.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/tools.py @@ -30,39 +30,39 @@ class __ModuleName__Tool(BaseTool): # type: ignore[override] Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` Instantiation: - .. code-block:: python - - tool = __ModuleName__Tool( - # TODO: init params - ) + ```python + tool = __ModuleName__Tool( + # TODO: init params + ) + ``` Invocation with args: - .. code-block:: python + ```python + # TODO: invoke args + tool.invoke({...}) + ``` - # TODO: invoke args - tool.invoke({...}) - - .. code-block:: python - - # TODO: output of invocation + ```python + # TODO: output of invocation + ``` Invocation with ToolCall: - .. code-block:: python + ```python + # TODO: invoke args + tool.invoke({"args": {...}, "id": "1", "name": tool.name, "type": "tool_call"}) + ``` - # TODO: invoke args - tool.invoke({"args": {...}, "id": "1", "name": tool.name, "type": "tool_call"}) - - .. code-block:: python - - # TODO: output of invocation + ```python + # TODO: output of invocation + ``` """ # noqa: E501 # TODO: Set tool name and description diff --git a/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py b/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py index 03cb70e36b6..9969932f8da 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py @@ -30,10 +30,10 @@ class __ModuleName__VectorStore(VectorStore): Setup: Install `__package_name__` and set environment variable `__MODULE_NAME___API_KEY`. - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" + ```bash + pip install -U __package_name__ + export __MODULE_NAME___API_KEY="your-api-key" + ``` # TODO: Populate with relevant params. Key init args — indexing params: @@ -51,110 +51,110 @@ class __ModuleName__VectorStore(VectorStore): # TODO: Replace with relevant init params. Instantiate: - .. code-block:: python + ```python + from __module_name__.vectorstores import __ModuleName__VectorStore + from langchain_openai import OpenAIEmbeddings - from __module_name__.vectorstores import __ModuleName__VectorStore - from langchain_openai import OpenAIEmbeddings - - vector_store = __ModuleName__VectorStore( - collection_name="foo", - embedding_function=OpenAIEmbeddings(), - connection_args={"uri": "./foo.db"}, - # other params... - ) + vector_store = __ModuleName__VectorStore( + collection_name="foo", + embedding_function=OpenAIEmbeddings(), + connection_args={"uri": "./foo.db"}, + # other params... + ) + ``` # TODO: Populate with relevant variables. Add Documents: - .. code-block:: python + ```python + from langchain_core.documents import Document - from langchain_core.documents import Document + document_1 = Document(page_content="foo", metadata={"baz": "bar"}) + document_2 = Document(page_content="thud", metadata={"bar": "baz"}) + document_3 = Document(page_content="i will be deleted :(") - document_1 = Document(page_content="foo", metadata={"baz": "bar"}) - document_2 = Document(page_content="thud", metadata={"bar": "baz"}) - document_3 = Document(page_content="i will be deleted :(") - - documents = [document_1, document_2, document_3] - ids = ["1", "2", "3"] - vector_store.add_documents(documents=documents, ids=ids) + documents = [document_1, document_2, document_3] + ids = ["1", "2", "3"] + vector_store.add_documents(documents=documents, ids=ids) + ``` # TODO: Populate with relevant variables. Delete Documents: - .. code-block:: python - - vector_store.delete(ids=["3"]) + ```python + vector_store.delete(ids=["3"]) + ``` # TODO: Fill out with relevant variables and example output. Search: - .. code-block:: python + ```python + results = vector_store.similarity_search(query="thud",k=1) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search(query="thud",k=1) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Fill out with relevant variables and example output. Search with filter: - .. code-block:: python + ```python + results = vector_store.similarity_search(query="thud",k=1,filter={"bar": "baz"}) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search(query="thud",k=1,filter={"bar": "baz"}) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Fill out with relevant variables and example output. Search with score: - .. code-block:: python + ```python + results = vector_store.similarity_search_with_score(query="qux",k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search_with_score(query="qux",k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Fill out with relevant variables and example output. Async: - .. code-block:: python + ```python + # add documents + # await vector_store.aadd_documents(documents=documents, ids=ids) - # add documents - # await vector_store.aadd_documents(documents=documents, ids=ids) + # delete documents + # await vector_store.adelete(ids=["3"]) - # delete documents - # await vector_store.adelete(ids=["3"]) + # search + # results = vector_store.asimilarity_search(query="thud",k=1) - # search - # results = vector_store.asimilarity_search(query="thud",k=1) + # search with score + results = await vector_store.asimilarity_search_with_score(query="qux",k=1) + for doc,score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - # search with score - results = await vector_store.asimilarity_search_with_score(query="qux",k=1) - for doc,score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` # TODO: Fill out with relevant variables and example output. Use as Retriever: - .. code-block:: python + ```python + retriever = vector_store.as_retriever( + search_type="mmr", + search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, + ) + retriever.invoke("thud") + ``` - retriever = vector_store.as_retriever( - search_type="mmr", - search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, - ) - retriever.invoke("thud") - - .. code-block:: python - - # TODO: Example output + ```python + # TODO: Example output + ``` """ # noqa: E501 def __init__(self, embedding: Embeddings) -> None: diff --git a/libs/cli/langchain_cli/namespaces/migrate/generate/partner.py b/libs/cli/langchain_cli/namespaces/migrate/generate/partner.py index ef8a34409db..9c9c5db84ba 100644 --- a/libs/cli/langchain_cli/namespaces/migrate/generate/partner.py +++ b/libs/cli/langchain_cli/namespaces/migrate/generate/partner.py @@ -24,7 +24,7 @@ def get_migrations_for_partner_package(pkg_name: str) -> list[tuple[str, str]]: This code works Args: - pkg_name (str): The name of the partner package. + pkg_name: The name of the partner package. Returns: List of 2-tuples containing old and new import paths. diff --git a/libs/core/langchain_core/_api/__init__.py b/libs/core/langchain_core/_api/__init__.py index 27bec397342..54de1d18b77 100644 --- a/libs/core/langchain_core/_api/__init__.py +++ b/libs/core/langchain_core/_api/__init__.py @@ -6,7 +6,6 @@ This module is only relevant for LangChain developers, not for users. This module and its submodules are for internal use only. Do not use them in your own code. We may change the API at any time with no warning. - """ from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/_api/beta_decorator.py b/libs/core/langchain_core/_api/beta_decorator.py index eef83f04d24..4db13fa3b62 100644 --- a/libs/core/langchain_core/_api/beta_decorator.py +++ b/libs/core/langchain_core/_api/beta_decorator.py @@ -66,14 +66,11 @@ def beta( Returns: A decorator which can be used to mark functions or classes as beta. - Examples: - - .. code-block:: python - - @beta - def the_function_to_annotate(): - pass - + ```python + @beta + def the_function_to_annotate(): + pass + ``` """ def beta( diff --git a/libs/core/langchain_core/_api/deprecation.py b/libs/core/langchain_core/_api/deprecation.py index 91efcbae541..5a3e90b5398 100644 --- a/libs/core/langchain_core/_api/deprecation.py +++ b/libs/core/langchain_core/_api/deprecation.py @@ -130,14 +130,11 @@ def deprecated( Returns: A decorator to mark a function or class as deprecated. - Examples: - - .. code-block:: python - - @deprecated("1.4.0") - def the_function_to_deprecate(): - pass - + ```python + @deprecated("1.4.0") + def the_function_to_deprecate(): + pass + ``` """ _validate_deprecation_params( removal, alternative, alternative_import, pending=pending @@ -550,12 +547,10 @@ def rename_parameter( A decorator indicating that a parameter was renamed. Example: - - .. code-block:: python - - @_api.rename_parameter("3.1", "bad_name", "good_name") - def func(good_name): ... - + ```python + @_api.rename_parameter("3.1", "bad_name", "good_name") + def func(good_name): ... + ``` """ def decorator(f: Callable[_P, _R]) -> Callable[_P, _R]: diff --git a/libs/core/langchain_core/agents.py b/libs/core/langchain_core/agents.py index a0ad88f219f..62a44623972 100644 --- a/libs/core/langchain_core/agents.py +++ b/libs/core/langchain_core/agents.py @@ -1,6 +1,7 @@ """Schema definitions for representing agent actions, observations, and return values. -**ATTENTION** The schema definitions are provided for backwards compatibility. +!!! warning + The schema definitions are provided for backwards compatibility. !!! warning New agents should be built using the @@ -16,10 +17,10 @@ Agents use language models to choose a sequence of actions to take. A basic agent works in the following manner: 1. Given a prompt an agent uses an LLM to request an action to take - (e.g., a tool to run). + (e.g., a tool to run). 2. The agent executes the action (e.g., runs the tool), and receives an observation. 3. The agent returns the observation to the LLM, which can then be used to generate - the next action. + the next action. 4. When the agent reaches a stopping condition, it returns a final return value. The schemas for the agents themselves are defined in langchain.agents.agent. diff --git a/libs/core/langchain_core/caches.py b/libs/core/langchain_core/caches.py index 7e8e03e0389..9db037e913b 100644 --- a/libs/core/langchain_core/caches.py +++ b/libs/core/langchain_core/caches.py @@ -1,24 +1,18 @@ """Cache classes. !!! warning - Beta Feature! + Beta Feature! **Cache** provides an optional caching layer for LLMs. Cache is useful for two reasons: - It can save you money by reducing the number of API calls you make to the LLM - provider if you're often requesting the same completion multiple times. + provider if you're often requesting the same completion multiple times. - It can speed up your application by reducing the number of API calls you make - to the LLM provider. + to the LLM provider. Cache directly competes with Memory. See documentation for Pros and Cons. - -**Class hierarchy:** - -.. code-block:: - - BaseCache --> Cache # Examples: InMemoryCache, RedisCache, GPTCache """ from __future__ import annotations diff --git a/libs/core/langchain_core/callbacks/__init__.py b/libs/core/langchain_core/callbacks/__init__.py index cce9d67df6a..ef42894bacf 100644 --- a/libs/core/langchain_core/callbacks/__init__.py +++ b/libs/core/langchain_core/callbacks/__init__.py @@ -1,11 +1,4 @@ -"""**Callback handlers** allow listening to events in LangChain. - -**Class hierarchy:** - -.. code-block:: - - BaseCallbackHandler --> CallbackHandler # Example: AimCallbackHandler -""" +"""**Callback handlers** allow listening to events in LangChain.""" from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/callbacks/base.py b/libs/core/langchain_core/callbacks/base.py index 41c37ef9c65..4265902d0c3 100644 --- a/libs/core/langchain_core/callbacks/base.py +++ b/libs/core/langchain_core/callbacks/base.py @@ -945,30 +945,25 @@ class BaseCallbackManager(CallbackManagerMixin): Example: Merging two callback managers. - .. code-block:: python + ```python + from langchain_core.callbacks.manager import ( + CallbackManager, + trace_as_chain_group, + ) + from langchain_core.callbacks.stdout import StdOutCallbackHandler - from langchain_core.callbacks.manager import ( - CallbackManager, - trace_as_chain_group, - ) - from langchain_core.callbacks.stdout import StdOutCallbackHandler - - manager = CallbackManager( - handlers=[StdOutCallbackHandler()], tags=["tag2"] - ) - with trace_as_chain_group( - "My Group Name", tags=["tag1"] - ) as group_manager: - merged_manager = group_manager.merge(manager) - print(merged_manager.handlers) - # [ - # , - # , - # ] - - print(merged_manager.tags) - # ['tag2', 'tag1'] + manager = CallbackManager(handlers=[StdOutCallbackHandler()], tags=["tag2"]) + with trace_as_chain_group("My Group Name", tags=["tag1"]) as group_manager: + merged_manager = group_manager.merge(manager) + print(merged_manager.handlers) + # [ + # , + # , + # ] + print(merged_manager.tags) + # ['tag2', 'tag1'] + ``` """ # noqa: E501 manager = self.__class__( parent_run_id=self.parent_run_id or other.parent_run_id, diff --git a/libs/core/langchain_core/callbacks/file.py b/libs/core/langchain_core/callbacks/file.py index f51f92c0e14..e1bfc05dcb8 100644 --- a/libs/core/langchain_core/callbacks/file.py +++ b/libs/core/langchain_core/callbacks/file.py @@ -27,22 +27,22 @@ class FileCallbackHandler(BaseCallbackHandler): Examples: Using as a context manager (recommended): - .. code-block:: python - - with FileCallbackHandler("output.txt") as handler: - # Use handler with your chain/agent - chain.invoke(inputs, config={"callbacks": [handler]}) + ```python + with FileCallbackHandler("output.txt") as handler: + # Use handler with your chain/agent + chain.invoke(inputs, config={"callbacks": [handler]}) + ``` Direct instantiation (deprecated): - .. code-block:: python - - handler = FileCallbackHandler("output.txt") - # File remains open until handler is garbage collected - try: - chain.invoke(inputs, config={"callbacks": [handler]}) - finally: - handler.close() # Explicit cleanup recommended + ```python + handler = FileCallbackHandler("output.txt") + # File remains open until handler is garbage collected + try: + chain.invoke(inputs, config={"callbacks": [handler]}) + finally: + handler.close() # Explicit cleanup recommended + ``` Args: filename: The file path to write to. diff --git a/libs/core/langchain_core/callbacks/manager.py b/libs/core/langchain_core/callbacks/manager.py index 7061adc67fe..428bd7cdb23 100644 --- a/libs/core/langchain_core/callbacks/manager.py +++ b/libs/core/langchain_core/callbacks/manager.py @@ -95,16 +95,13 @@ def trace_as_chain_group( The callback manager for the chain group. Example: - .. code-block:: python - - llm_input = "Foo" - with trace_as_chain_group( - "group_name", inputs={"input": llm_input} - ) as manager: - # Use the callback manager for the chain group - res = llm.invoke(llm_input, {"callbacks": manager}) - manager.on_chain_end({"output": res}) - + ```python + llm_input = "Foo" + with trace_as_chain_group("group_name", inputs={"input": llm_input}) as manager: + # Use the callback manager for the chain group + res = llm.invoke(llm_input, {"callbacks": manager}) + manager.on_chain_end({"output": res}) + ``` """ cb = _get_trace_callbacks( project_name, example_id, callback_manager=callback_manager @@ -174,16 +171,15 @@ async def atrace_as_chain_group( LangSmith. Example: - .. code-block:: python - - llm_input = "Foo" - async with atrace_as_chain_group( - "group_name", inputs={"input": llm_input} - ) as manager: - # Use the async callback manager for the chain group - res = await llm.ainvoke(llm_input, {"callbacks": manager}) - await manager.on_chain_end({"output": res}) - + ```python + llm_input = "Foo" + async with atrace_as_chain_group( + "group_name", inputs={"input": llm_input} + ) as manager: + # Use the async callback manager for the chain group + res = await llm.ainvoke(llm_input, {"callbacks": manager}) + await manager.on_chain_end({"output": res}) + ``` """ cb = _get_trace_callbacks( project_name, example_id, callback_manager=callback_manager @@ -1693,33 +1689,28 @@ class CallbackManagerForChainGroup(CallbackManager): Example: Merging two callback managers. - .. code-block:: python + ```python + from langchain_core.callbacks.manager import ( + CallbackManager, + trace_as_chain_group, + ) + from langchain_core.callbacks.stdout import StdOutCallbackHandler - from langchain_core.callbacks.manager import ( - CallbackManager, - trace_as_chain_group, - ) - from langchain_core.callbacks.stdout import StdOutCallbackHandler + manager = CallbackManager(handlers=[StdOutCallbackHandler()], tags=["tag2"]) + with trace_as_chain_group("My Group Name", tags=["tag1"]) as group_manager: + merged_manager = group_manager.merge(manager) + print(type(merged_manager)) + # - manager = CallbackManager( - handlers=[StdOutCallbackHandler()], tags=["tag2"] - ) - with trace_as_chain_group( - "My Group Name", tags=["tag1"] - ) as group_manager: - merged_manager = group_manager.merge(manager) - print(type(merged_manager)) - # - - print(merged_manager.handlers) - # [ - # , - # , - # ] - - print(merged_manager.tags) - # ['tag2', 'tag1'] + print(merged_manager.handlers) + # [ + # , + # , + # ] + print(merged_manager.tags) + # ['tag2', 'tag1'] + ``` """ # noqa: E501 manager = self.__class__( parent_run_id=self.parent_run_id or other.parent_run_id, @@ -2220,33 +2211,30 @@ class AsyncCallbackManagerForChainGroup(AsyncCallbackManager): Example: Merging two callback managers. - .. code-block:: python + ```python + from langchain_core.callbacks.manager import ( + CallbackManager, + atrace_as_chain_group, + ) + from langchain_core.callbacks.stdout import StdOutCallbackHandler - from langchain_core.callbacks.manager import ( - CallbackManager, - atrace_as_chain_group, - ) - from langchain_core.callbacks.stdout import StdOutCallbackHandler + manager = CallbackManager(handlers=[StdOutCallbackHandler()], tags=["tag2"]) + async with atrace_as_chain_group( + "My Group Name", tags=["tag1"] + ) as group_manager: + merged_manager = group_manager.merge(manager) + print(type(merged_manager)) + # - manager = CallbackManager( - handlers=[StdOutCallbackHandler()], tags=["tag2"] - ) - async with atrace_as_chain_group( - "My Group Name", tags=["tag1"] - ) as group_manager: - merged_manager = group_manager.merge(manager) - print(type(merged_manager)) - # - - print(merged_manager.handlers) - # [ - # , - # , - # ] - - print(merged_manager.tags) - # ['tag2', 'tag1'] + print(merged_manager.handlers) + # [ + # , + # , + # ] + print(merged_manager.tags) + # ['tag2', 'tag1'] + ``` """ # noqa: E501 manager = self.__class__( parent_run_id=self.parent_run_id or other.parent_run_id, @@ -2492,75 +2480,74 @@ async def adispatch_custom_event( the event with. Example: + ```python + from langchain_core.callbacks import ( + AsyncCallbackHandler, + adispatch_custom_event + ) + from langchain_core.runnable import RunnableLambda - .. code-block:: python + class CustomCallbackManager(AsyncCallbackHandler): + async def on_custom_event( + self, + name: str, + data: Any, + *, + run_id: UUID, + tags: list[str] | None = None, + metadata: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + print(f"Received custom event: {name} with data: {data}") - from langchain_core.callbacks import ( - AsyncCallbackHandler, - adispatch_custom_event - ) - from langchain_core.runnable import RunnableLambda + callback = CustomCallbackManager() - class CustomCallbackManager(AsyncCallbackHandler): - async def on_custom_event( - self, - name: str, - data: Any, - *, - run_id: UUID, - tags: list[str] | None = None, - metadata: dict[str, Any] | None = None, - **kwargs: Any, - ) -> None: - print(f"Received custom event: {name} with data: {data}") + async def foo(inputs): + await adispatch_custom_event("my_event", {"bar": "buzz}) + return inputs - callback = CustomCallbackManager() - - async def foo(inputs): - await adispatch_custom_event("my_event", {"bar": "buzz}) - return inputs - - foo_ = RunnableLambda(foo) - await foo_.ainvoke({"a": "1"}, {"callbacks": [CustomCallbackManager()]}) + foo_ = RunnableLambda(foo) + await foo_.ainvoke({"a": "1"}, {"callbacks": [CustomCallbackManager()]}) + ``` Example: Use with astream events - .. code-block:: python + ```python + from langchain_core.callbacks import ( + AsyncCallbackHandler, + adispatch_custom_event + ) + from langchain_core.runnable import RunnableLambda - from langchain_core.callbacks import ( - AsyncCallbackHandler, - adispatch_custom_event - ) - from langchain_core.runnable import RunnableLambda + class CustomCallbackManager(AsyncCallbackHandler): + async def on_custom_event( + self, + name: str, + data: Any, + *, + run_id: UUID, + tags: list[str] | None = None, + metadata: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + print(f"Received custom event: {name} with data: {data}") - class CustomCallbackManager(AsyncCallbackHandler): - async def on_custom_event( - self, - name: str, - data: Any, - *, - run_id: UUID, - tags: list[str] | None = None, - metadata: dict[str, Any] | None = None, - **kwargs: Any, - ) -> None: - print(f"Received custom event: {name} with data: {data}") + callback = CustomCallbackManager() - callback = CustomCallbackManager() + async def foo(inputs): + await adispatch_custom_event("event_type_1", {"bar": "buzz}) + await adispatch_custom_event("event_type_2", 5) + return inputs - async def foo(inputs): - await adispatch_custom_event("event_type_1", {"bar": "buzz}) - await adispatch_custom_event("event_type_2", 5) - return inputs + foo_ = RunnableLambda(foo) - foo_ = RunnableLambda(foo) - - async for event in foo_.ainvoke_stream( - {"a": "1"}, - version="v2", - config={"callbacks": [CustomCallbackManager()]} - ): - print(event) + async for event in foo_.ainvoke_stream( + {"a": "1"}, + version="v2", + config={"callbacks": [CustomCallbackManager()]} + ): + print(event) + ``` !!! warning If using python <= 3.10 and async, you MUST @@ -2618,32 +2605,31 @@ def dispatch_custom_event( the event with. Example: + ```python + from langchain_core.callbacks import BaseCallbackHandler + from langchain_core.callbacks import dispatch_custom_event + from langchain_core.runnable import RunnableLambda - .. code-block:: python + class CustomCallbackManager(BaseCallbackHandler): + def on_custom_event( + self, + name: str, + data: Any, + *, + run_id: UUID, + tags: list[str] | None = None, + metadata: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + print(f"Received custom event: {name} with data: {data}") - from langchain_core.callbacks import BaseCallbackHandler - from langchain_core.callbacks import dispatch_custom_event - from langchain_core.runnable import RunnableLambda + def foo(inputs): + dispatch_custom_event("my_event", {"bar": "buzz}) + return inputs - class CustomCallbackManager(BaseCallbackHandler): - def on_custom_event( - self, - name: str, - data: Any, - *, - run_id: UUID, - tags: list[str] | None = None, - metadata: dict[str, Any] | None = None, - **kwargs: Any, - ) -> None: - print(f"Received custom event: {name} with data: {data}") - - def foo(inputs): - dispatch_custom_event("my_event", {"bar": "buzz}) - return inputs - - foo_ = RunnableLambda(foo) - foo_.invoke({"a": "1"}, {"callbacks": [CustomCallbackManager()]}) + foo_ = RunnableLambda(foo) + foo_.invoke({"a": "1"}, {"callbacks": [CustomCallbackManager()]}) + ``` !!! version-added "Added in version 0.2.15" diff --git a/libs/core/langchain_core/callbacks/usage.py b/libs/core/langchain_core/callbacks/usage.py index 29f07a2dad8..f5b20aef0c1 100644 --- a/libs/core/langchain_core/callbacks/usage.py +++ b/libs/core/langchain_core/callbacks/usage.py @@ -19,30 +19,29 @@ class UsageMetadataCallbackHandler(BaseCallbackHandler): """Callback Handler that tracks AIMessage.usage_metadata. Example: - .. code-block:: python + ```python + from langchain.chat_models import init_chat_model + from langchain_core.callbacks import UsageMetadataCallbackHandler - from langchain.chat_models import init_chat_model - from langchain_core.callbacks import UsageMetadataCallbackHandler + llm_1 = init_chat_model(model="openai:gpt-4o-mini") + llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest") - llm_1 = init_chat_model(model="openai:gpt-4o-mini") - llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest") - - callback = UsageMetadataCallbackHandler() - result_1 = llm_1.invoke("Hello", config={"callbacks": [callback]}) - result_2 = llm_2.invoke("Hello", config={"callbacks": [callback]}) - callback.usage_metadata - - .. code-block:: - - {'gpt-4o-mini-2024-07-18': {'input_tokens': 8, - 'output_tokens': 10, - 'total_tokens': 18, - 'input_token_details': {'audio': 0, 'cache_read': 0}, - 'output_token_details': {'audio': 0, 'reasoning': 0}}, - 'claude-3-5-haiku-20241022': {'input_tokens': 8, - 'output_tokens': 21, - 'total_tokens': 29, - 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}} + callback = UsageMetadataCallbackHandler() + result_1 = llm_1.invoke("Hello", config={"callbacks": [callback]}) + result_2 = llm_2.invoke("Hello", config={"callbacks": [callback]}) + callback.usage_metadata + ``` + ```txt + {'gpt-4o-mini-2024-07-18': {'input_tokens': 8, + 'output_tokens': 10, + 'total_tokens': 18, + 'input_token_details': {'audio': 0, 'cache_read': 0}, + 'output_token_details': {'audio': 0, 'reasoning': 0}}, + 'claude-3-5-haiku-20241022': {'input_tokens': 8, + 'output_tokens': 21, + 'total_tokens': 29, + 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}} + ``` !!! version-added "Added in version 0.3.49" @@ -105,30 +104,35 @@ def get_usage_metadata_callback( The usage metadata callback. Example: - .. code-block:: python + ```python + from langchain.chat_models import init_chat_model + from langchain_core.callbacks import get_usage_metadata_callback - from langchain.chat_models import init_chat_model - from langchain_core.callbacks import get_usage_metadata_callback + llm_1 = init_chat_model(model="openai:gpt-4o-mini") + llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest") - llm_1 = init_chat_model(model="openai:gpt-4o-mini") - llm_2 = init_chat_model(model="anthropic:claude-3-5-haiku-latest") - - with get_usage_metadata_callback() as cb: - llm_1.invoke("Hello") - llm_2.invoke("Hello") - print(cb.usage_metadata) - - .. code-block:: - - {'gpt-4o-mini-2024-07-18': {'input_tokens': 8, - 'output_tokens': 10, - 'total_tokens': 18, - 'input_token_details': {'audio': 0, 'cache_read': 0}, - 'output_token_details': {'audio': 0, 'reasoning': 0}}, - 'claude-3-5-haiku-20241022': {'input_tokens': 8, - 'output_tokens': 21, - 'total_tokens': 29, - 'input_token_details': {'cache_read': 0, 'cache_creation': 0}}} + with get_usage_metadata_callback() as cb: + llm_1.invoke("Hello") + llm_2.invoke("Hello") + print(cb.usage_metadata) + ``` + ```txt + { + "gpt-4o-mini-2024-07-18": { + "input_tokens": 8, + "output_tokens": 10, + "total_tokens": 18, + "input_token_details": {"audio": 0, "cache_read": 0}, + "output_token_details": {"audio": 0, "reasoning": 0}, + }, + "claude-3-5-haiku-20241022": { + "input_tokens": 8, + "output_tokens": 21, + "total_tokens": 29, + "input_token_details": {"cache_read": 0, "cache_creation": 0}, + }, + } + ``` !!! version-added "Added in version 0.3.49" diff --git a/libs/core/langchain_core/chat_history.py b/libs/core/langchain_core/chat_history.py index da3cf58a596..a4f135b8dfc 100644 --- a/libs/core/langchain_core/chat_history.py +++ b/libs/core/langchain_core/chat_history.py @@ -1,18 +1,4 @@ -"""**Chat message history** stores a history of the message interactions in a chat. - -**Class hierarchy:** - -.. code-block:: - - BaseChatMessageHistory --> ChatMessageHistory # Examples: FileChatMessageHistory, PostgresChatMessageHistory - -**Main helpers:** - -.. code-block:: - - AIMessage, HumanMessage, BaseMessage - -""" # noqa: E501 +"""**Chat message history** stores a history of the message interactions in a chat.""" from __future__ import annotations @@ -63,46 +49,45 @@ class BaseChatMessageHistory(ABC): Example: Shows a default implementation. - .. code-block:: python - - import json - import os - from langchain_core.messages import messages_from_dict, message_to_dict + ```python + import json + import os + from langchain_core.messages import messages_from_dict, message_to_dict - class FileChatMessageHistory(BaseChatMessageHistory): - storage_path: str - session_id: str + class FileChatMessageHistory(BaseChatMessageHistory): + storage_path: str + session_id: str - @property - def messages(self) -> list[BaseMessage]: - try: - with open( - os.path.join(self.storage_path, self.session_id), - "r", - encoding="utf-8", - ) as f: - messages_data = json.load(f) - return messages_from_dict(messages_data) - except FileNotFoundError: - return [] + @property + def messages(self) -> list[BaseMessage]: + try: + with open( + os.path.join(self.storage_path, self.session_id), + "r", + encoding="utf-8", + ) as f: + messages_data = json.load(f) + return messages_from_dict(messages_data) + except FileNotFoundError: + return [] - def add_messages(self, messages: Sequence[BaseMessage]) -> None: - all_messages = list(self.messages) # Existing messages - all_messages.extend(messages) # Add new messages + def add_messages(self, messages: Sequence[BaseMessage]) -> None: + all_messages = list(self.messages) # Existing messages + all_messages.extend(messages) # Add new messages - serialized = [message_to_dict(message) for message in all_messages] - file_path = os.path.join(self.storage_path, self.session_id) - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, "w", encoding="utf-8") as f: - json.dump(serialized, f) - - def clear(self) -> None: - file_path = os.path.join(self.storage_path, self.session_id) - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, "w", encoding="utf-8") as f: - json.dump([], f) + serialized = [message_to_dict(message) for message in all_messages] + file_path = os.path.join(self.storage_path, self.session_id) + os.makedirs(os.path.dirname(file_path), exist_ok=True) + with open(file_path, "w", encoding="utf-8") as f: + json.dump(serialized, f) + def clear(self) -> None: + file_path = os.path.join(self.storage_path, self.session_id) + os.makedirs(os.path.dirname(file_path), exist_ok=True) + with open(file_path, "w", encoding="utf-8") as f: + json.dump([], f) + ``` """ messages: list[BaseMessage] diff --git a/libs/core/langchain_core/document_loaders/langsmith.py b/libs/core/langchain_core/document_loaders/langsmith.py index 9cdacb7aeb8..ac69bef81e5 100644 --- a/libs/core/langchain_core/document_loaders/langsmith.py +++ b/libs/core/langchain_core/document_loaders/langsmith.py @@ -22,22 +22,22 @@ class LangSmithLoader(BaseLoader): ??? note "Lazy load" - .. code-block:: python + ```python + from langchain_core.document_loaders import LangSmithLoader - from langchain_core.document_loaders import LangSmithLoader + loader = LangSmithLoader(dataset_id="...", limit=100) + docs = [] + for doc in loader.lazy_load(): + docs.append(doc) + ``` - loader = LangSmithLoader(dataset_id="...", limit=100) - docs = [] - for doc in loader.lazy_load(): - docs.append(doc) - - .. code-block:: python - - # -> [Document("...", metadata={"inputs": {...}, "outputs": {...}, ...}), ...] + ```python + # -> [Document("...", metadata={"inputs": {...}, "outputs": {...}, ...}), ...] + ``` !!! version-added "Added in version 0.2.34" - """ # noqa: E501 + """ def __init__( self, diff --git a/libs/core/langchain_core/documents/__init__.py b/libs/core/langchain_core/documents/__init__.py index 9d5f4064177..2bf3f802197 100644 --- a/libs/core/langchain_core/documents/__init__.py +++ b/libs/core/langchain_core/documents/__init__.py @@ -2,7 +2,6 @@ **Document** module is a collection of classes that handle documents and their transformations. - """ from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/documents/base.py b/libs/core/langchain_core/documents/base.py index 347076f114f..8631392ac36 100644 --- a/libs/core/langchain_core/documents/base.py +++ b/libs/core/langchain_core/documents/base.py @@ -57,52 +57,51 @@ class Blob(BaseMedia): Example: Initialize a blob from in-memory data - .. code-block:: python + ```python + from langchain_core.documents import Blob - from langchain_core.documents import Blob + blob = Blob.from_data("Hello, world!") - blob = Blob.from_data("Hello, world!") + # Read the blob as a string + print(blob.as_string()) - # Read the blob as a string - print(blob.as_string()) + # Read the blob as bytes + print(blob.as_bytes()) - # Read the blob as bytes - print(blob.as_bytes()) - - # Read the blob as a byte stream - with blob.as_bytes_io() as f: - print(f.read()) + # Read the blob as a byte stream + with blob.as_bytes_io() as f: + print(f.read()) + ``` Example: Load from memory and specify mime-type and metadata - .. code-block:: python + ```python + from langchain_core.documents import Blob - from langchain_core.documents import Blob - - blob = Blob.from_data( - data="Hello, world!", - mime_type="text/plain", - metadata={"source": "https://example.com"}, - ) + blob = Blob.from_data( + data="Hello, world!", + mime_type="text/plain", + metadata={"source": "https://example.com"}, + ) + ``` Example: Load the blob from a file - .. code-block:: python + ```python + from langchain_core.documents import Blob - from langchain_core.documents import Blob + blob = Blob.from_path("path/to/file.txt") - blob = Blob.from_path("path/to/file.txt") + # Read the blob as a string + print(blob.as_string()) - # Read the blob as a string - print(blob.as_string()) - - # Read the blob as bytes - print(blob.as_bytes()) - - # Read the blob as a byte stream - with blob.as_bytes_io() as f: - print(f.read()) + # Read the blob as bytes + print(blob.as_bytes()) + # Read the blob as a byte stream + with blob.as_bytes_io() as f: + print(f.read()) + ``` """ data: bytes | str | None = None @@ -278,15 +277,13 @@ class Document(BaseMedia): """Class for storing a piece of text and associated metadata. Example: + ```python + from langchain_core.documents import Document - .. code-block:: python - - from langchain_core.documents import Document - - document = Document( - page_content="Hello, world!", metadata={"source": "https://example.com"} - ) - + document = Document( + page_content="Hello, world!", metadata={"source": "https://example.com"} + ) + ``` """ page_content: str diff --git a/libs/core/langchain_core/documents/transformers.py b/libs/core/langchain_core/documents/transformers.py index f70fa0d491e..4cb37470e6b 100644 --- a/libs/core/langchain_core/documents/transformers.py +++ b/libs/core/langchain_core/documents/transformers.py @@ -20,35 +20,34 @@ class BaseDocumentTransformer(ABC): sequence of transformed Documents. Example: - .. code-block:: python + ```python + class EmbeddingsRedundantFilter(BaseDocumentTransformer, BaseModel): + embeddings: Embeddings + similarity_fn: Callable = cosine_similarity + similarity_threshold: float = 0.95 - class EmbeddingsRedundantFilter(BaseDocumentTransformer, BaseModel): - embeddings: Embeddings - similarity_fn: Callable = cosine_similarity - similarity_threshold: float = 0.95 + class Config: + arbitrary_types_allowed = True - class Config: - arbitrary_types_allowed = True - - def transform_documents( - self, documents: Sequence[Document], **kwargs: Any - ) -> Sequence[Document]: - stateful_documents = get_stateful_documents(documents) - embedded_documents = _get_embeddings_from_stateful_docs( - self.embeddings, stateful_documents - ) - included_idxs = _filter_similar_embeddings( - embedded_documents, - self.similarity_fn, - self.similarity_threshold, - ) - return [stateful_documents[i] for i in sorted(included_idxs)] - - async def atransform_documents( - self, documents: Sequence[Document], **kwargs: Any - ) -> Sequence[Document]: - raise NotImplementedError + def transform_documents( + self, documents: Sequence[Document], **kwargs: Any + ) -> Sequence[Document]: + stateful_documents = get_stateful_documents(documents) + embedded_documents = _get_embeddings_from_stateful_docs( + self.embeddings, stateful_documents + ) + included_idxs = _filter_similar_embeddings( + embedded_documents, + self.similarity_fn, + self.similarity_threshold, + ) + return [stateful_documents[i] for i in sorted(included_idxs)] + async def atransform_documents( + self, documents: Sequence[Document], **kwargs: Any + ) -> Sequence[Document]: + raise NotImplementedError + ``` """ @abstractmethod diff --git a/libs/core/langchain_core/embeddings/fake.py b/libs/core/langchain_core/embeddings/fake.py index c33281c484a..885366d2e62 100644 --- a/libs/core/langchain_core/embeddings/fake.py +++ b/libs/core/langchain_core/embeddings/fake.py @@ -21,37 +21,34 @@ class FakeEmbeddings(Embeddings, BaseModel): Do not use this outside of testing, as it is not a real embedding model. Instantiate: - .. code-block:: python + ```python + from langchain_core.embeddings import FakeEmbeddings - from langchain_core.embeddings import FakeEmbeddings - - embed = FakeEmbeddings(size=100) + embed = FakeEmbeddings(size=100) + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embed.embed_query(input_text) - print(vector[:3]) - - .. code-block:: python - - [-0.700234640213188, -0.581266257710429, -1.1328482266445354] + ```python + input_text = "The meaning of life is 42" + vector = embed.embed_query(input_text) + print(vector[:3]) + ``` + ```python + [-0.700234640213188, -0.581266257710429, -1.1328482266445354] + ``` Embed multiple texts: - .. code-block:: python - - input_texts = ["Document 1...", "Document 2..."] - vectors = embed.embed_documents(input_texts) - print(len(vectors)) - # The first 3 coordinates for the first vector - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.5670477847544458, -0.31403828652395727, -0.5840547508955257] - + ```python + input_texts = ["Document 1...", "Document 2..."] + vectors = embed.embed_documents(input_texts) + print(len(vectors)) + # The first 3 coordinates for the first vector + print(vectors[0][:3]) + ``` + ```python + 2 + [-0.5670477847544458, -0.31403828652395727, -0.5840547508955257] + ``` """ size: int @@ -78,37 +75,34 @@ class DeterministicFakeEmbedding(Embeddings, BaseModel): Do not use this outside of testing, as it is not a real embedding model. Instantiate: - .. code-block:: python + ```python + from langchain_core.embeddings import DeterministicFakeEmbedding - from langchain_core.embeddings import DeterministicFakeEmbedding - - embed = DeterministicFakeEmbedding(size=100) + embed = DeterministicFakeEmbedding(size=100) + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embed.embed_query(input_text) - print(vector[:3]) - - .. code-block:: python - - [-0.700234640213188, -0.581266257710429, -1.1328482266445354] + ```python + input_text = "The meaning of life is 42" + vector = embed.embed_query(input_text) + print(vector[:3]) + ``` + ```python + [-0.700234640213188, -0.581266257710429, -1.1328482266445354] + ``` Embed multiple texts: - .. code-block:: python - - input_texts = ["Document 1...", "Document 2..."] - vectors = embed.embed_documents(input_texts) - print(len(vectors)) - # The first 3 coordinates for the first vector - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.5670477847544458, -0.31403828652395727, -0.5840547508955257] - + ```python + input_texts = ["Document 1...", "Document 2..."] + vectors = embed.embed_documents(input_texts) + print(len(vectors)) + # The first 3 coordinates for the first vector + print(vectors[0][:3]) + ``` + ```python + 2 + [-0.5670477847544458, -0.31403828652395727, -0.5840547508955257] + ``` """ size: int diff --git a/libs/core/langchain_core/language_models/__init__.py b/libs/core/langchain_core/language_models/__init__.py index cb35be4ae3d..6e2770ecf10 100644 --- a/libs/core/langchain_core/language_models/__init__.py +++ b/libs/core/langchain_core/language_models/__init__.py @@ -38,8 +38,6 @@ To implement a custom LLM, inherit from `BaseLLM` or `LLM`. Please see the following guide for more information on how to implement a custom LLM: https://python.langchain.com/docs/how_to/custom_llm/ - - """ from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/language_models/_utils.py b/libs/core/langchain_core/language_models/_utils.py index 51ad689db20..48141b54812 100644 --- a/libs/core/langchain_core/language_models/_utils.py +++ b/libs/core/langchain_core/language_models/_utils.py @@ -92,18 +92,16 @@ def _parse_data_uri(uri: str) -> ParsedDataUri | None: If parsing fails, return None. If either MIME type or data is missing, return None. Example: + ```python + data_uri = "data:image/jpeg;base64,/9j/4AAQSkZJRg..." + parsed = _parse_data_uri(data_uri) - .. code-block:: python - - data_uri = "data:image/jpeg;base64,/9j/4AAQSkZJRg..." - parsed = _parse_data_uri(data_uri) - - assert parsed == { - "source_type": "base64", - "mime_type": "image/jpeg", - "data": "/9j/4AAQSkZJRg...", - } - + assert parsed == { + "source_type": "base64", + "mime_type": "image/jpeg", + "data": "/9j/4AAQSkZJRg...", + } + ``` """ regex = r"^data:(?P[^;]+);base64,(?P.+)$" match = re.match(regex, uri) @@ -150,48 +148,48 @@ def _normalize_messages( `URLContentBlock`: - .. codeblock:: - - { - mime_type: NotRequired[str] - type: Literal['image', 'audio', 'file'], - source_type: Literal['url'], - url: str, - } + ```python + { + mime_type: NotRequired[str] + type: Literal['image', 'audio', 'file'], + source_type: Literal['url'], + url: str, + } + ``` `Base64ContentBlock`: - .. codeblock:: - - { - mime_type: NotRequired[str] - type: Literal['image', 'audio', 'file'], - source_type: Literal['base64'], - data: str, - } + ```python + { + mime_type: NotRequired[str] + type: Literal['image', 'audio', 'file'], + source_type: Literal['base64'], + data: str, + } + ``` `IDContentBlock`: (In practice, this was never used) - .. codeblock:: - - { - type: Literal['image', 'audio', 'file'], - source_type: Literal['id'], - id: str, - } + ```python + { + type: Literal["image", "audio", "file"], + source_type: Literal["id"], + id: str, + } + ``` `PlainTextContentBlock`: - .. codeblock:: - - { - mime_type: NotRequired[str] - type: Literal['file'], - source_type: Literal['text'], - url: str, - } + ```python + { + mime_type: NotRequired[str] + type: Literal['file'], + source_type: Literal['text'], + url: str, + } + ``` If a v1 message is passed in, it will be returned as-is, meaning it is safe to always pass in v1 messages to this function for assurance. diff --git a/libs/core/langchain_core/language_models/chat_models.py b/libs/core/langchain_core/language_models/chat_models.py index 8a1fc06b744..6506111092e 100644 --- a/libs/core/langchain_core/language_models/chat_models.py +++ b/libs/core/langchain_core/language_models/chat_models.py @@ -1568,82 +1568,82 @@ class BaseChatModel(BaseLanguageModel[AIMessage], ABC): - `'parsing_error'`: BaseException | None Example: Pydantic schema (include_raw=False): - .. code-block:: python - - from pydantic import BaseModel + ```python + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatModel(model="model-name", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatModel(model="model-name", temperature=0) + structured_llm = llm.with_structured_output(AnswerWithJustification) - structured_llm.invoke( - "What weighs more a pound of bricks or a pound of feathers" - ) + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: Pydantic schema (include_raw=True): - .. code-block:: python - - from pydantic import BaseModel + ```python + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatModel(model="model-name", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, include_raw=True - ) + llm = ChatModel(model="model-name", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` Example: Dict schema (include_raw=False): - .. code-block:: python - - from pydantic import BaseModel - from langchain_core.utils.function_calling import convert_to_openai_tool + ```python + from pydantic import BaseModel + from langchain_core.utils.function_calling import convert_to_openai_tool - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - dict_schema = convert_to_openai_tool(AnswerWithJustification) - llm = ChatModel(model="model-name", temperature=0) - structured_llm = llm.with_structured_output(dict_schema) + dict_schema = convert_to_openai_tool(AnswerWithJustification) + llm = ChatModel(model="model-name", temperature=0) + structured_llm = llm.with_structured_output(dict_schema) - structured_llm.invoke( - "What weighs more a pound of bricks or a pound of feathers" - ) - # -> { - # 'answer': 'They weigh the same', - # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` !!! warning "Behavior changed in 0.2.26" Added support for TypedDict class. diff --git a/libs/core/langchain_core/language_models/llms.py b/libs/core/langchain_core/language_models/llms.py index 0519a0fe38e..e81d40832a9 100644 --- a/libs/core/langchain_core/language_models/llms.py +++ b/libs/core/langchain_core/language_models/llms.py @@ -1340,11 +1340,9 @@ class BaseLLM(BaseLanguageModel[str], ABC): ValueError: If the file path is not a string or Path object. Example: - - .. code-block:: python - - llm.save(file_path="path/llm.yaml") - + ```python + llm.save(file_path="path/llm.yaml") + ``` """ # Convert file to Path object. save_path = Path(file_path) diff --git a/libs/core/langchain_core/memory.py b/libs/core/langchain_core/memory.py index bd6ac278808..ec50cc01e07 100644 --- a/libs/core/langchain_core/memory.py +++ b/libs/core/langchain_core/memory.py @@ -36,28 +36,25 @@ class BaseMemory(Serializable, ABC): the latest input. Example: - .. code-block:: python + ```python + class SimpleMemory(BaseMemory): + memories: dict[str, Any] = dict() - class SimpleMemory(BaseMemory): - memories: dict[str, Any] = dict() + @property + def memory_variables(self) -> list[str]: + return list(self.memories.keys()) - @property - def memory_variables(self) -> list[str]: - return list(self.memories.keys()) + def load_memory_variables(self, inputs: dict[str, Any]) -> dict[str, str]: + return self.memories - def load_memory_variables( - self, inputs: dict[str, Any] - ) -> dict[str, str]: - return self.memories - - def save_context( - self, inputs: dict[str, Any], outputs: dict[str, str] - ) -> None: - pass - - def clear(self) -> None: - pass + def save_context( + self, inputs: dict[str, Any], outputs: dict[str, str] + ) -> None: + pass + def clear(self) -> None: + pass + ``` """ model_config = ConfigDict( diff --git a/libs/core/langchain_core/messages/__init__.py b/libs/core/langchain_core/messages/__init__.py index 814fba62b9e..183aff7fae7 100644 --- a/libs/core/langchain_core/messages/__init__.py +++ b/libs/core/langchain_core/messages/__init__.py @@ -1,19 +1,4 @@ -"""**Messages** are objects used in prompts and chat conversations. - -**Class hierarchy:** - -.. code-block:: - - BaseMessage --> SystemMessage, AIMessage, HumanMessage, ChatMessage, FunctionMessage, ToolMessage - --> BaseMessageChunk --> SystemMessageChunk, AIMessageChunk, HumanMessageChunk, ChatMessageChunk, FunctionMessageChunk, ToolMessageChunk - -**Main helpers:** - -.. code-block:: - - ChatPromptTemplate - -""" # noqa: E501 +"""**Messages** are objects used in prompts and chat conversations.""" from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/messages/ai.py b/libs/core/langchain_core/messages/ai.py index e5c772d2f43..011c1d2ed46 100644 --- a/libs/core/langchain_core/messages/ai.py +++ b/libs/core/langchain_core/messages/ai.py @@ -40,13 +40,13 @@ class InputTokenDetails(TypedDict, total=False): Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -76,12 +76,12 @@ class OutputTokenDetails(TypedDict, total=False): Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" @@ -104,22 +104,22 @@ class UsageMetadata(TypedDict): This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -681,37 +681,36 @@ def add_usage(left: UsageMetadata | None, right: UsageMetadata | None) -> UsageM """Recursively add two UsageMetadata objects. Example: - .. code-block:: python + ```python + from langchain_core.messages.ai import add_usage - from langchain_core.messages.ai import add_usage + left = UsageMetadata( + input_tokens=5, + output_tokens=0, + total_tokens=5, + input_token_details=InputTokenDetails(cache_read=3), + ) + right = UsageMetadata( + input_tokens=0, + output_tokens=10, + total_tokens=10, + output_token_details=OutputTokenDetails(reasoning=4), + ) - left = UsageMetadata( - input_tokens=5, - output_tokens=0, - total_tokens=5, - input_token_details=InputTokenDetails(cache_read=3), - ) - right = UsageMetadata( - input_tokens=0, - output_tokens=10, - total_tokens=10, - output_token_details=OutputTokenDetails(reasoning=4), - ) - - add_usage(left, right) + add_usage(left, right) + ``` results in - .. code-block:: python - - UsageMetadata( - input_tokens=5, - output_tokens=10, - total_tokens=15, - input_token_details=InputTokenDetails(cache_read=3), - output_token_details=OutputTokenDetails(reasoning=4), - ) - + ```python + UsageMetadata( + input_tokens=5, + output_tokens=10, + total_tokens=15, + input_token_details=InputTokenDetails(cache_read=3), + output_token_details=OutputTokenDetails(reasoning=4), + ) + ``` Args: left: The first `UsageMetadata` object. right: The second `UsageMetadata` object. @@ -745,37 +744,36 @@ def subtract_usage( Token counts cannot be negative so the actual operation is `max(left - right, 0)`. Example: - .. code-block:: python + ```python + from langchain_core.messages.ai import subtract_usage - from langchain_core.messages.ai import subtract_usage + left = UsageMetadata( + input_tokens=5, + output_tokens=10, + total_tokens=15, + input_token_details=InputTokenDetails(cache_read=4), + ) + right = UsageMetadata( + input_tokens=3, + output_tokens=8, + total_tokens=11, + output_token_details=OutputTokenDetails(reasoning=4), + ) - left = UsageMetadata( - input_tokens=5, - output_tokens=10, - total_tokens=15, - input_token_details=InputTokenDetails(cache_read=4), - ) - right = UsageMetadata( - input_tokens=3, - output_tokens=8, - total_tokens=11, - output_token_details=OutputTokenDetails(reasoning=4), - ) - - subtract_usage(left, right) + subtract_usage(left, right) + ``` results in - .. code-block:: python - - UsageMetadata( - input_tokens=2, - output_tokens=2, - total_tokens=4, - input_token_details=InputTokenDetails(cache_read=4), - output_token_details=OutputTokenDetails(reasoning=0), - ) - + ```python + UsageMetadata( + input_tokens=2, + output_tokens=2, + total_tokens=4, + input_token_details=InputTokenDetails(cache_read=4), + output_token_details=OutputTokenDetails(reasoning=0), + ) + ``` Args: left: The first `UsageMetadata` object. right: The second `UsageMetadata` object. diff --git a/libs/core/langchain_core/messages/content.py b/libs/core/langchain_core/messages/content.py index 693afbd0993..9f14baea9b7 100644 --- a/libs/core/langchain_core/messages/content.py +++ b/libs/core/langchain_core/messages/content.py @@ -42,35 +42,35 @@ to be included without breaking the standard structure. **Example with PEP 728 provider-specific fields:** - .. code-block:: python + ```python + # Content block definition + # NOTE: `extra_items=Any` + class TextContentBlock(TypedDict, extra_items=Any): + type: Literal["text"] + id: NotRequired[str] + text: str + annotations: NotRequired[list[Annotation]] + index: NotRequired[int] + ``` - # Content block definition - # NOTE: `extra_items=Any` - class TextContentBlock(TypedDict, extra_items=Any): - type: Literal["text"] - id: NotRequired[str] - text: str - annotations: NotRequired[list[Annotation]] - index: NotRequired[int] + ```python + from langchain_core.messages.content import TextContentBlock - .. code-block:: python + # Create a text content block with provider-specific fields + my_block: TextContentBlock = { + # Add required fields + "type": "text", + "text": "Hello, world!", + # Additional fields not specified in the TypedDict + # These are valid with PEP 728 and are typed as Any + "openai_metadata": {"model": "gpt-4", "temperature": 0.7}, + "anthropic_usage": {"input_tokens": 10, "output_tokens": 20}, + "custom_field": "any value", + } - from langchain_core.messages.content import TextContentBlock - - # Create a text content block with provider-specific fields - my_block: TextContentBlock = { - # Add required fields - "type": "text", - "text": "Hello, world!", - # Additional fields not specified in the TypedDict - # These are valid with PEP 728 and are typed as Any - "openai_metadata": {"model": "gpt-4", "temperature": 0.7}, - "anthropic_usage": {"input_tokens": 10, "output_tokens": 20}, - "custom_field": "any value", - } - - # Mutating an existing block to add provider-specific fields - openai_data = my_block["openai_metadata"] # Type: Any + # Mutating an existing block to add provider-specific fields + openai_data = my_block["openai_metadata"] # Type: Any + ``` PEP 728 is enabled with `# type: ignore[call-arg]` comments to suppress warnings from type checkers that don't yet support it. The functionality works @@ -94,39 +94,38 @@ The module defines several types of content blocks, including: **Example Usage** -.. code-block:: python +```python +# Direct construction: +from langchain_core.messages.content import TextContentBlock, ImageContentBlock - # Direct construction: - from langchain_core.messages.content import TextContentBlock, ImageContentBlock +multimodal_message: AIMessage( + content_blocks=[ + TextContentBlock(type="text", text="What is shown in this image?"), + ImageContentBlock( + type="image", + url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png", + mime_type="image/png", + ), + ] +) - multimodal_message: AIMessage( - content_blocks=[ - TextContentBlock(type="text", text="What is shown in this image?"), - ImageContentBlock( - type="image", - url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png", - mime_type="image/png", - ), - ] - ) +# Using factories: +from langchain_core.messages.content import create_text_block, create_image_block - # Using factories: - from langchain_core.messages.content import create_text_block, create_image_block - - multimodal_message: AIMessage( - content=[ - create_text_block("What is shown in this image?"), - create_image_block( - url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png", - mime_type="image/png", - ), - ] - ) +multimodal_message: AIMessage( + content=[ + create_text_block("What is shown in this image?"), + create_image_block( + url="https://www.langchain.com/images/brand/langchain_logo_text_w_white.png", + mime_type="image/png", + ), + ] +) +``` Factory functions offer benefits such as: - Automatic ID generation (when not provided) - No need to manually specify the `type` field - """ from typing import Any, Literal, get_args, get_type_hints @@ -258,10 +257,9 @@ class ToolCall(TypedDict): """Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named "foo" with arguments {"a": 1} and an identifier of "123". @@ -308,17 +306,15 @@ class ToolCallChunk(TypedDict): values of `index` are equal and not `None`. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] - + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` """ # TODO: Consider making fields NotRequired[str] in the future. diff --git a/libs/core/langchain_core/messages/human.py b/libs/core/langchain_core/messages/human.py index c244aa9fdc6..d4626b6f415 100644 --- a/libs/core/langchain_core/messages/human.py +++ b/libs/core/langchain_core/messages/human.py @@ -12,20 +12,18 @@ class HumanMessage(BaseMessage): `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python - - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` """ type: Literal["human"] = "human" diff --git a/libs/core/langchain_core/messages/system.py b/libs/core/langchain_core/messages/system.py index 003d466960b..789506ce5ce 100644 --- a/libs/core/langchain_core/messages/system.py +++ b/libs/core/langchain_core/messages/system.py @@ -13,19 +13,17 @@ class SystemMessage(BaseMessage): of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python - - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` """ type: Literal["system"] = "system" diff --git a/libs/core/langchain_core/messages/tool.py b/libs/core/langchain_core/messages/tool.py index fd949587347..47128bf5e42 100644 --- a/libs/core/langchain_core/messages/tool.py +++ b/libs/core/langchain_core/messages/tool.py @@ -31,34 +31,33 @@ class ToolMessage(BaseMessage, ToolOutputMixin): Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -219,10 +218,9 @@ class ToolCall(TypedDict): """Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -270,17 +268,15 @@ class ToolCallChunk(TypedDict): values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] - + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` """ name: str | None diff --git a/libs/core/langchain_core/messages/utils.py b/libs/core/langchain_core/messages/utils.py index 61a7d64007f..9b3643c0899 100644 --- a/libs/core/langchain_core/messages/utils.py +++ b/libs/core/langchain_core/messages/utils.py @@ -5,7 +5,6 @@ Some examples of what you can do with these functions include: * Convert messages to strings (serialization) * Convert messages from dicts to Message objects (deserialization) * Filter messages from a list of messages based on name, type or id etc. - """ from __future__ import annotations @@ -108,17 +107,16 @@ def get_buffer_string( ValueError: If an unsupported message type is encountered. Example: - .. code-block:: python - - from langchain_core import AIMessage, HumanMessage - - messages = [ - HumanMessage(content="Hi, how are you?"), - AIMessage(content="Good, how are you?"), - ] - get_buffer_string(messages) - # -> "Human: Hi, how are you?\nAI: Good, how are you?" + ```python + from langchain_core import AIMessage, HumanMessage + messages = [ + HumanMessage(content="Hi, how are you?"), + AIMessage(content="Good, how are you?"), + ] + get_buffer_string(messages) + # -> "Human: Hi, how are you?\nAI: Good, how are you?" + ``` """ string_messages = [] for m in messages: @@ -459,43 +457,42 @@ def filter_messages( ValueError: If two incompatible arguments are provided. Example: - .. code-block:: python + ```python + from langchain_core.messages import ( + filter_messages, + AIMessage, + HumanMessage, + SystemMessage, + ) - from langchain_core.messages import ( - filter_messages, - AIMessage, - HumanMessage, - SystemMessage, - ) + messages = [ + SystemMessage("you're a good assistant."), + HumanMessage("what's your name", id="foo", name="example_user"), + AIMessage("steve-o", id="bar", name="example_assistant"), + HumanMessage( + "what's your favorite color", + id="baz", + ), + AIMessage( + "silicon blue", + id="blah", + ), + ] - messages = [ - SystemMessage("you're a good assistant."), - HumanMessage("what's your name", id="foo", name="example_user"), - AIMessage("steve-o", id="bar", name="example_assistant"), - HumanMessage( - "what's your favorite color", - id="baz", - ), - AIMessage( - "silicon blue", - id="blah", - ), - ] - - filter_messages( - messages, - incl_names=("example_user", "example_assistant"), - incl_types=("system",), - excl_ids=("bar",), - ) - - .. code-block:: python - - [ - SystemMessage("you're a good assistant."), - HumanMessage("what's your name", id="foo", name="example_user"), - ] + filter_messages( + messages, + incl_names=("example_user", "example_assistant"), + incl_types=("system",), + excl_ids=("bar",), + ) + ``` + ```python + [ + SystemMessage("you're a good assistant."), + HumanMessage("what's your name", id="foo", name="example_user"), + ] + ``` """ messages = convert_to_messages(messages) filtered: list[BaseMessage] = [] @@ -583,83 +580,82 @@ def merge_message_runs( of content blocks, the merged content is a list of content blocks. Example: + ```python + from langchain_core.messages import ( + merge_message_runs, + AIMessage, + HumanMessage, + SystemMessage, + ToolCall, + ) - .. code-block:: python + messages = [ + SystemMessage("you're a good assistant."), + HumanMessage( + "what's your favorite color", + id="foo", + ), + HumanMessage( + "wait your favorite food", + id="bar", + ), + AIMessage( + "my favorite colo", + tool_calls=[ + ToolCall( + name="blah_tool", args={"x": 2}, id="123", type="tool_call" + ) + ], + id="baz", + ), + AIMessage( + [{"type": "text", "text": "my favorite dish is lasagna"}], + tool_calls=[ + ToolCall( + name="blah_tool", + args={"x": -10}, + id="456", + type="tool_call", + ) + ], + id="blur", + ), + ] - from langchain_core.messages import ( - merge_message_runs, - AIMessage, - HumanMessage, - SystemMessage, - ToolCall, - ) + merge_message_runs(messages) + ``` - messages = [ - SystemMessage("you're a good assistant."), - HumanMessage( - "what's your favorite color", - id="foo", - ), - HumanMessage( - "wait your favorite food", - id="bar", - ), - AIMessage( + ```python + [ + SystemMessage("you're a good assistant."), + HumanMessage( + "what's your favorite color\\n" + "wait your favorite food", id="foo", + ), + AIMessage( + [ "my favorite colo", - tool_calls=[ - ToolCall( - name="blah_tool", args={"x": 2}, id="123", type="tool_call" - ) - ], - id="baz", - ), - AIMessage( - [{"type": "text", "text": "my favorite dish is lasagna"}], - tool_calls=[ - ToolCall( - name="blah_tool", - args={"x": -10}, - id="456", - type="tool_call", - ) - ], - id="blur", - ), - ] - - merge_message_runs(messages) - - .. code-block:: python - - [ - SystemMessage("you're a good assistant."), - HumanMessage( - "what's your favorite color\\n" - "wait your favorite food", id="foo", - ), - AIMessage( - [ - "my favorite colo", - {"type": "text", "text": "my favorite dish is lasagna"} - ], - tool_calls=[ - ToolCall({ - "name": "blah_tool", - "args": {"x": 2}, - "id": "123", - "type": "tool_call" - }), - ToolCall({ - "name": "blah_tool", - "args": {"x": -10}, - "id": "456", - "type": "tool_call" - }) - ] - id="baz" - ), - ] + {"type": "text", "text": "my favorite dish is lasagna"} + ], + tool_calls=[ + ToolCall({ + "name": "blah_tool", + "args": {"x": 2}, + "id": "123", + "type": "tool_call" + }), + ToolCall({ + "name": "blah_tool", + "args": {"x": -10}, + "id": "456", + "type": "tool_call" + }) + ] + id="baz" + ), + ] + ``` """ if not messages: return [] @@ -797,58 +793,56 @@ def trim_messages( present, and ensuring that the chat history starts with a `HumanMessage` ( or a `SystemMessage` followed by a `HumanMessage`). - .. code-block:: python + ```python + from langchain_core.messages import ( + AIMessage, + HumanMessage, + BaseMessage, + SystemMessage, + trim_messages, + ) - from langchain_core.messages import ( - AIMessage, - HumanMessage, - BaseMessage, - SystemMessage, - trim_messages, - ) - - messages = [ - SystemMessage( - "you're a good assistant, you always respond with a joke." - ), - HumanMessage("i wonder why it's called langchain"), - AIMessage( - 'Well, I guess they thought "WordRope" and "SentenceString" just ' - "didn't have the same ring to it!" - ), - HumanMessage("and who is harrison chasing anyways"), - AIMessage( - "Hmmm let me think.\n\nWhy, he's probably chasing after the last " - "cup of coffee in the office!" - ), - HumanMessage("what do you call a speechless parrot"), - ] + messages = [ + SystemMessage("you're a good assistant, you always respond with a joke."), + HumanMessage("i wonder why it's called langchain"), + AIMessage( + 'Well, I guess they thought "WordRope" and "SentenceString" just ' + "didn't have the same ring to it!" + ), + HumanMessage("and who is harrison chasing anyways"), + AIMessage( + "Hmmm let me think.\n\nWhy, he's probably chasing after the last " + "cup of coffee in the office!" + ), + HumanMessage("what do you call a speechless parrot"), + ] - trim_messages( - messages, - max_tokens=45, - strategy="last", - token_counter=ChatOpenAI(model="gpt-4o"), - # Most chat models expect that chat history starts with either: - # (1) a HumanMessage or - # (2) a SystemMessage followed by a HumanMessage - start_on="human", - # Usually, we want to keep the SystemMessage - # if it's present in the original history. - # The SystemMessage has special instructions for the model. - include_system=True, - allow_partial=False, - ) + trim_messages( + messages, + max_tokens=45, + strategy="last", + token_counter=ChatOpenAI(model="gpt-4o"), + # Most chat models expect that chat history starts with either: + # (1) a HumanMessage or + # (2) a SystemMessage followed by a HumanMessage + start_on="human", + # Usually, we want to keep the SystemMessage + # if it's present in the original history. + # The SystemMessage has special instructions for the model. + include_system=True, + allow_partial=False, + ) + ``` - .. code-block:: python - - [ - SystemMessage( - content="you're a good assistant, you always respond with a joke." - ), - HumanMessage(content="what do you call a speechless parrot"), - ] + ```python + [ + SystemMessage( + content="you're a good assistant, you always respond with a joke." + ), + HumanMessage(content="what do you call a speechless parrot"), + ] + ``` Trim chat history based on the message count, keeping the `SystemMessage` if present, and ensuring that the chat history starts with a `HumanMessage` ( @@ -874,100 +868,95 @@ def trim_messages( allow_partial=False, ) - .. code-block:: python - - [ - SystemMessage( - content="you're a good assistant, you always respond with a joke." - ), - HumanMessage(content="and who is harrison chasing anyways"), - AIMessage( - content="Hmmm let me think.\n\nWhy, he's probably chasing after " - "the last cup of coffee in the office!" - ), - HumanMessage(content="what do you call a speechless parrot"), - ] - - + ```python + [ + SystemMessage( + content="you're a good assistant, you always respond with a joke." + ), + HumanMessage(content="and who is harrison chasing anyways"), + AIMessage( + content="Hmmm let me think.\n\nWhy, he's probably chasing after " + "the last cup of coffee in the office!" + ), + HumanMessage(content="what do you call a speechless parrot"), + ] + ``` Trim chat history using a custom token counter function that counts the number of tokens in each message. - .. code-block:: python - - messages = [ - SystemMessage("This is a 4 token text. The full message is 10 tokens."), - HumanMessage( - "This is a 4 token text. The full message is 10 tokens.", id="first" - ), - AIMessage( - [ - {"type": "text", "text": "This is the FIRST 4 token block."}, - {"type": "text", "text": "This is the SECOND 4 token block."}, - ], - id="second", - ), - HumanMessage( - "This is a 4 token text. The full message is 10 tokens.", id="third" - ), - AIMessage( - "This is a 4 token text. The full message is 10 tokens.", - id="fourth", - ), - ] + ```python + messages = [ + SystemMessage("This is a 4 token text. The full message is 10 tokens."), + HumanMessage( + "This is a 4 token text. The full message is 10 tokens.", id="first" + ), + AIMessage( + [ + {"type": "text", "text": "This is the FIRST 4 token block."}, + {"type": "text", "text": "This is the SECOND 4 token block."}, + ], + id="second", + ), + HumanMessage( + "This is a 4 token text. The full message is 10 tokens.", id="third" + ), + AIMessage( + "This is a 4 token text. The full message is 10 tokens.", + id="fourth", + ), + ] - def dummy_token_counter(messages: list[BaseMessage]) -> int: - # treat each message like it adds 3 default tokens at the beginning - # of the message and at the end of the message. 3 + 4 + 3 = 10 tokens - # per message. + def dummy_token_counter(messages: list[BaseMessage]) -> int: + # treat each message like it adds 3 default tokens at the beginning + # of the message and at the end of the message. 3 + 4 + 3 = 10 tokens + # per message. - default_content_len = 4 - default_msg_prefix_len = 3 - default_msg_suffix_len = 3 + default_content_len = 4 + default_msg_prefix_len = 3 + default_msg_suffix_len = 3 - count = 0 - for msg in messages: - if isinstance(msg.content, str): - count += ( - default_msg_prefix_len - + default_content_len - + default_msg_suffix_len - ) - if isinstance(msg.content, list): - count += ( - default_msg_prefix_len - + len(msg.content) * default_content_len - + default_msg_suffix_len - ) - return count + count = 0 + for msg in messages: + if isinstance(msg.content, str): + count += ( + default_msg_prefix_len + + default_content_len + + default_msg_suffix_len + ) + if isinstance(msg.content, list): + count += ( + default_msg_prefix_len + + len(msg.content) * default_content_len + + default_msg_suffix_len + ) + return count + ``` First 30 tokens, allowing partial messages: - .. code-block:: python - - trim_messages( - messages, - max_tokens=30, - token_counter=dummy_token_counter, - strategy="first", - allow_partial=True, - ) - - .. code-block:: python - - [ - SystemMessage( - "This is a 4 token text. The full message is 10 tokens." - ), - HumanMessage( - "This is a 4 token text. The full message is 10 tokens.", - id="first", - ), - AIMessage( - [{"type": "text", "text": "This is the FIRST 4 token block."}], - id="second", - ), - ] + ```python + trim_messages( + messages, + max_tokens=30, + token_counter=dummy_token_counter, + strategy="first", + allow_partial=True, + ) + ``` + ```python + [ + SystemMessage("This is a 4 token text. The full message is 10 tokens."), + HumanMessage( + "This is a 4 token text. The full message is 10 tokens.", + id="first", + ), + AIMessage( + [{"type": "text", "text": "This is the FIRST 4 token block."}], + id="second", + ), + ] + ``` """ # Validate arguments if start_on and strategy == "first": @@ -1070,50 +1059,49 @@ def convert_to_openai_messages( message dicts is returned. Example: + ```python + from langchain_core.messages import ( + convert_to_openai_messages, + AIMessage, + SystemMessage, + ToolMessage, + ) - .. code-block:: python - - from langchain_core.messages import ( - convert_to_openai_messages, - AIMessage, - SystemMessage, - ToolMessage, - ) - - messages = [ - SystemMessage([{"type": "text", "text": "foo"}]), - { - "role": "user", - "content": [ - {"type": "text", "text": "whats in this"}, - { - "type": "image_url", - "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"}, - }, - ], - }, - AIMessage( - "", - tool_calls=[ - { - "name": "analyze", - "args": {"baz": "buz"}, - "id": "1", - "type": "tool_call", - } - ], - ), - ToolMessage("foobar", tool_call_id="1", name="bar"), - {"role": "assistant", "content": "thats nice"}, - ] - oai_messages = convert_to_openai_messages(messages) - # -> [ - # {'role': 'system', 'content': 'foo'}, - # {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]}, - # {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''}, - # {'role': 'tool', 'name': 'bar', 'content': 'foobar'}, - # {'role': 'assistant', 'content': 'thats nice'} - # ] + messages = [ + SystemMessage([{"type": "text", "text": "foo"}]), + { + "role": "user", + "content": [ + {"type": "text", "text": "whats in this"}, + { + "type": "image_url", + "image_url": {"url": "data:image/png;base64,'/9j/4AAQSk'"}, + }, + ], + }, + AIMessage( + "", + tool_calls=[ + { + "name": "analyze", + "args": {"baz": "buz"}, + "id": "1", + "type": "tool_call", + } + ], + ), + ToolMessage("foobar", tool_call_id="1", name="bar"), + {"role": "assistant", "content": "thats nice"}, + ] + oai_messages = convert_to_openai_messages(messages) + # -> [ + # {'role': 'system', 'content': 'foo'}, + # {'role': 'user', 'content': [{'type': 'text', 'text': 'whats in this'}, {'type': 'image_url', 'image_url': {'url': "data:image/png;base64,'/9j/4AAQSk'"}}]}, + # {'role': 'assistant', 'tool_calls': [{'type': 'function', 'id': '1','function': {'name': 'analyze', 'arguments': '{"baz": "buz"}'}}], 'content': ''}, + # {'role': 'tool', 'name': 'bar', 'content': 'foobar'}, + # {'role': 'assistant', 'content': 'thats nice'} + # ] + ``` !!! version-added "Added in version 0.3.11" diff --git a/libs/core/langchain_core/output_parsers/__init__.py b/libs/core/langchain_core/output_parsers/__init__.py index b53299a9938..81c40b73a43 100644 --- a/libs/core/langchain_core/output_parsers/__init__.py +++ b/libs/core/langchain_core/output_parsers/__init__.py @@ -1,17 +1,4 @@ -"""**OutputParser** classes parse the output of an LLM call. - -**Class hierarchy:** - -.. code-block:: - - BaseLLMOutputParser --> BaseOutputParser --> OutputParser # ListOutputParser, PydanticOutputParser - -**Main helpers:** - -.. code-block:: - - Serializable, Generation, PromptValue -""" # noqa: E501 +"""**OutputParser** classes parse the output of an LLM call.""" from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/output_parsers/base.py b/libs/core/langchain_core/output_parsers/base.py index f1582c3f95d..6786ef7c058 100644 --- a/libs/core/langchain_core/output_parsers/base.py +++ b/libs/core/langchain_core/output_parsers/base.py @@ -134,29 +134,28 @@ class BaseOutputParser( Output parsers help structure language model responses. Example: - .. code-block:: python + ```python + class BooleanOutputParser(BaseOutputParser[bool]): + true_val: str = "YES" + false_val: str = "NO" - class BooleanOutputParser(BaseOutputParser[bool]): - true_val: str = "YES" - false_val: str = "NO" - - def parse(self, text: str) -> bool: - cleaned_text = text.strip().upper() - if cleaned_text not in ( - self.true_val.upper(), - self.false_val.upper(), - ): - raise OutputParserException( - f"BooleanOutputParser expected output value to either be " - f"{self.true_val} or {self.false_val} (case-insensitive). " - f"Received {cleaned_text}." - ) - return cleaned_text == self.true_val.upper() - - @property - def _type(self) -> str: - return "boolean_output_parser" + def parse(self, text: str) -> bool: + cleaned_text = text.strip().upper() + if cleaned_text not in ( + self.true_val.upper(), + self.false_val.upper(), + ): + raise OutputParserException( + f"BooleanOutputParser expected output value to either be " + f"{self.true_val} or {self.false_val} (case-insensitive). " + f"Received {cleaned_text}." + ) + return cleaned_text == self.true_val.upper() + @property + def _type(self) -> str: + return "boolean_output_parser" + ``` """ @property diff --git a/libs/core/langchain_core/output_parsers/openai_functions.py b/libs/core/langchain_core/output_parsers/openai_functions.py index ed1fd22a635..a971884621a 100644 --- a/libs/core/langchain_core/output_parsers/openai_functions.py +++ b/libs/core/langchain_core/output_parsers/openai_functions.py @@ -189,31 +189,34 @@ class PydanticOutputFunctionsParser(OutputFunctionsParser): the provided schema. Example: - ... code-block:: python + ```python + message = AIMessage( + content="This is a test message", + additional_kwargs={ + "function_call": { + "name": "cookie", + "arguments": json.dumps({"name": "value", "age": 10}), + } + }, + ) + chat_generation = ChatGeneration(message=message) - message = AIMessage( - content="This is a test message", - additional_kwargs={ - "function_call": { - "name": "cookie", - "arguments": json.dumps({"name": "value", "age": 10}), - } - }, - ) - chat_generation = ChatGeneration(message=message) - class Cookie(BaseModel): - name: str - age: int + class Cookie(BaseModel): + name: str + age: int - class Dog(BaseModel): - species: str - # Full output - parser = PydanticOutputFunctionsParser( - pydantic_schema={"cookie": Cookie, "dog": Dog} - ) - result = parser.parse_result([chat_generation]) + class Dog(BaseModel): + species: str + + + # Full output + parser = PydanticOutputFunctionsParser( + pydantic_schema={"cookie": Cookie, "dog": Dog} + ) + result = parser.parse_result([chat_generation]) + ``` """ diff --git a/libs/core/langchain_core/prompts/__init__.py b/libs/core/langchain_core/prompts/__init__.py index 1505a9f8783..34a32ba6f55 100644 --- a/libs/core/langchain_core/prompts/__init__.py +++ b/libs/core/langchain_core/prompts/__init__.py @@ -1,28 +1,8 @@ """**Prompt** is the input to the model. -Prompt is often constructed -from multiple components and prompt values. Prompt classes and functions make constructing - and working with prompts easy. - -**Class hierarchy:** - -.. code-block:: - - BasePromptTemplate --> StringPromptTemplate --> PromptTemplate - FewShotPromptTemplate - FewShotPromptWithTemplates - BaseChatPromptTemplate --> AutoGPTPrompt - ChatPromptTemplate --> AgentScratchPadChatPromptTemplate - - - - BaseMessagePromptTemplate --> MessagesPlaceholder - BaseStringMessagePromptTemplate --> ChatMessagePromptTemplate - HumanMessagePromptTemplate - AIMessagePromptTemplate - SystemMessagePromptTemplate - -""" # noqa: E501 +Prompt is often constructed from multiple components and prompt values. Prompt classes +and functions make constructing and working with prompts easy. +""" from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/prompts/base.py b/libs/core/langchain_core/prompts/base.py index 2e45a58b938..43964fe0349 100644 --- a/libs/core/langchain_core/prompts/base.py +++ b/libs/core/langchain_core/prompts/base.py @@ -296,11 +296,9 @@ class BasePromptTemplate( A formatted string. Example: - - .. code-block:: python - - prompt.format(variable1="foo") - + ```python + prompt.format(variable1="foo") + ``` """ async def aformat(self, **kwargs: Any) -> FormatOutputType: @@ -313,11 +311,9 @@ class BasePromptTemplate( A formatted string. Example: - - .. code-block:: python - - await prompt.aformat(variable1="foo") - + ```python + await prompt.aformat(variable1="foo") + ``` """ return self.format(**kwargs) @@ -352,10 +348,9 @@ class BasePromptTemplate( NotImplementedError: If the prompt type is not implemented. Example: - .. code-block:: python - - prompt.save(file_path="path/prompt.yaml") - + ```python + prompt.save(file_path="path/prompt.yaml") + ``` """ if self.partial_variables: msg = "Cannot save prompt with partial variables." @@ -426,16 +421,16 @@ def format_document(doc: Document, prompt: BasePromptTemplate[str]) -> str: string of the document formatted. Example: - .. code-block:: python + ```python + from langchain_core.documents import Document + from langchain_core.prompts import PromptTemplate - from langchain_core.documents import Document - from langchain_core.prompts import PromptTemplate - - doc = Document(page_content="This is a joke", metadata={"page": "1"}) - prompt = PromptTemplate.from_template("Page {page}: {page_content}") - format_document(doc, prompt) - >>> "Page 1: This is a joke" + doc = Document(page_content="This is a joke", metadata={"page": "1"}) + prompt = PromptTemplate.from_template("Page {page}: {page_content}") + format_document(doc, prompt) + >>> "Page 1: This is a joke" + ``` """ return prompt.format(**_get_document_info(doc, prompt)) diff --git a/libs/core/langchain_core/prompts/chat.py b/libs/core/langchain_core/prompts/chat.py index e5f582deabd..4c5d1f7810b 100644 --- a/libs/core/langchain_core/prompts/chat.py +++ b/libs/core/langchain_core/prompts/chat.py @@ -59,71 +59,70 @@ class MessagesPlaceholder(BaseMessagePromptTemplate): Direct usage: - .. code-block:: python + ```python + from langchain_core.prompts import MessagesPlaceholder - from langchain_core.prompts import MessagesPlaceholder + prompt = MessagesPlaceholder("history") + prompt.format_messages() # raises KeyError - prompt = MessagesPlaceholder("history") - prompt.format_messages() # raises KeyError + prompt = MessagesPlaceholder("history", optional=True) + prompt.format_messages() # returns empty list [] - prompt = MessagesPlaceholder("history", optional=True) - prompt.format_messages() # returns empty list [] - - prompt.format_messages( - history=[ - ("system", "You are an AI assistant."), - ("human", "Hello!"), - ] - ) - # -> [ - # SystemMessage(content="You are an AI assistant."), - # HumanMessage(content="Hello!"), - # ] + prompt.format_messages( + history=[ + ("system", "You are an AI assistant."), + ("human", "Hello!"), + ] + ) + # -> [ + # SystemMessage(content="You are an AI assistant."), + # HumanMessage(content="Hello!"), + # ] + ``` Building a prompt with chat history: - .. code-block:: python + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a helpful assistant."), - MessagesPlaceholder("history"), - ("human", "{question}"), - ] - ) - prompt.invoke( - { - "history": [("human", "what's 5 + 2"), ("ai", "5 + 2 is 7")], - "question": "now multiply that by 4", - } - ) - # -> ChatPromptValue(messages=[ - # SystemMessage(content="You are a helpful assistant."), - # HumanMessage(content="what's 5 + 2"), - # AIMessage(content="5 + 2 is 7"), - # HumanMessage(content="now multiply that by 4"), - # ]) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a helpful assistant."), + MessagesPlaceholder("history"), + ("human", "{question}"), + ] + ) + prompt.invoke( + { + "history": [("human", "what's 5 + 2"), ("ai", "5 + 2 is 7")], + "question": "now multiply that by 4", + } + ) + # -> ChatPromptValue(messages=[ + # SystemMessage(content="You are a helpful assistant."), + # HumanMessage(content="what's 5 + 2"), + # AIMessage(content="5 + 2 is 7"), + # HumanMessage(content="now multiply that by 4"), + # ]) + ``` Limiting the number of messages: - .. code-block:: python + ```python + from langchain_core.prompts import MessagesPlaceholder - from langchain_core.prompts import MessagesPlaceholder - - prompt = MessagesPlaceholder("history", n_messages=1) - - prompt.format_messages( - history=[ - ("system", "You are an AI assistant."), - ("human", "Hello!"), - ] - ) - # -> [ - # HumanMessage(content="Hello!"), - # ] + prompt = MessagesPlaceholder("history", n_messages=1) + prompt.format_messages( + history=[ + ("system", "You are an AI assistant."), + ("human", "Hello!"), + ] + ) + # -> [ + # HumanMessage(content="Hello!"), + # ] + ``` """ variable_name: str @@ -784,75 +783,75 @@ class ChatPromptTemplate(BaseChatPromptTemplate): `ChatPromptTemplate.from_messages()` directly to `ChatPromptTemplate()` init. - .. code-block:: python + ```python + from langchain_core.prompts import ChatPromptTemplate - from langchain_core.prompts import ChatPromptTemplate + template = ChatPromptTemplate( + [ + ("system", "You are a helpful AI bot. Your name is {name}."), + ("human", "Hello, how are you doing?"), + ("ai", "I'm doing well, thanks!"), + ("human", "{user_input}"), + ] + ) - template = ChatPromptTemplate( - [ - ("system", "You are a helpful AI bot. Your name is {name}."), - ("human", "Hello, how are you doing?"), - ("ai", "I'm doing well, thanks!"), - ("human", "{user_input}"), - ] - ) - - prompt_value = template.invoke( - { - "name": "Bob", - "user_input": "What is your name?", - } - ) - # Output: - # ChatPromptValue( - # messages=[ - # SystemMessage(content='You are a helpful AI bot. Your name is Bob.'), - # HumanMessage(content='Hello, how are you doing?'), - # AIMessage(content="I'm doing well, thanks!"), - # HumanMessage(content='What is your name?') - # ] - # ) + prompt_value = template.invoke( + { + "name": "Bob", + "user_input": "What is your name?", + } + ) + # Output: + # ChatPromptValue( + # messages=[ + # SystemMessage(content='You are a helpful AI bot. Your name is Bob.'), + # HumanMessage(content='Hello, how are you doing?'), + # AIMessage(content="I'm doing well, thanks!"), + # HumanMessage(content='What is your name?') + # ] + # ) + ``` Messages Placeholder: - .. code-block:: python + ```python + # In addition to Human/AI/Tool/Function messages, + # you can initialize the template with a MessagesPlaceholder + # either using the class directly or with the shorthand tuple syntax: - # In addition to Human/AI/Tool/Function messages, - # you can initialize the template with a MessagesPlaceholder - # either using the class directly or with the shorthand tuple syntax: + template = ChatPromptTemplate( + [ + ("system", "You are a helpful AI bot."), + # Means the template will receive an optional list of messages under + # the "conversation" key + ("placeholder", "{conversation}"), + # Equivalently: + # MessagesPlaceholder(variable_name="conversation", optional=True) + ] + ) - template = ChatPromptTemplate( - [ - ("system", "You are a helpful AI bot."), - # Means the template will receive an optional list of messages under - # the "conversation" key - ("placeholder", "{conversation}"), - # Equivalently: - # MessagesPlaceholder(variable_name="conversation", optional=True) + prompt_value = template.invoke( + { + "conversation": [ + ("human", "Hi!"), + ("ai", "How can I assist you today?"), + ("human", "Can you make me an ice cream sundae?"), + ("ai", "No."), ] - ) + } + ) - prompt_value = template.invoke( - { - "conversation": [ - ("human", "Hi!"), - ("ai", "How can I assist you today?"), - ("human", "Can you make me an ice cream sundae?"), - ("ai", "No."), - ] - } - ) - - # Output: - # ChatPromptValue( - # messages=[ - # SystemMessage(content='You are a helpful AI bot.'), - # HumanMessage(content='Hi!'), - # AIMessage(content='How can I assist you today?'), - # HumanMessage(content='Can you make me an ice cream sundae?'), - # AIMessage(content='No.'), - # ] - # ) + # Output: + # ChatPromptValue( + # messages=[ + # SystemMessage(content='You are a helpful AI bot.'), + # HumanMessage(content='Hi!'), + # AIMessage(content='How can I assist you today?'), + # HumanMessage(content='Can you make me an ice cream sundae?'), + # AIMessage(content='No.'), + # ] + # ) + ``` Single-variable template: @@ -861,29 +860,28 @@ class ChatPromptTemplate(BaseChatPromptTemplate): inject the provided argument into that variable location. - .. code-block:: python + ```python + from langchain_core.prompts import ChatPromptTemplate - from langchain_core.prompts import ChatPromptTemplate + template = ChatPromptTemplate( + [ + ("system", "You are a helpful AI bot. Your name is Carl."), + ("human", "{user_input}"), + ] + ) - template = ChatPromptTemplate( - [ - ("system", "You are a helpful AI bot. Your name is Carl."), - ("human", "{user_input}"), - ] - ) - - prompt_value = template.invoke("Hello, there!") - # Equivalent to - # prompt_value = template.invoke({"user_input": "Hello, there!"}) - - # Output: - # ChatPromptValue( - # messages=[ - # SystemMessage(content='You are a helpful AI bot. Your name is Carl.'), - # HumanMessage(content='Hello, there!'), - # ] - # ) + prompt_value = template.invoke("Hello, there!") + # Equivalent to + # prompt_value = template.invoke({"user_input": "Hello, there!"}) + # Output: + # ChatPromptValue( + # messages=[ + # SystemMessage(content='You are a helpful AI bot. Your name is Carl.'), + # HumanMessage(content='Hello, there!'), + # ] + # ) + ``` """ # noqa: E501 messages: Annotated[list[MessageLike], SkipValidation()] @@ -924,27 +922,26 @@ class ChatPromptTemplate(BaseChatPromptTemplate): Examples: Instantiation from a list of message templates: - .. code-block:: python - - template = ChatPromptTemplate( - [ - ("human", "Hello, how are you?"), - ("ai", "I'm doing well, thanks!"), - ("human", "That's good to hear."), - ] - ) + ```python + template = ChatPromptTemplate( + [ + ("human", "Hello, how are you?"), + ("ai", "I'm doing well, thanks!"), + ("human", "That's good to hear."), + ] + ) + ``` Instantiation from mixed message formats: - .. code-block:: python - - template = ChatPromptTemplate( - [ - SystemMessage(content="hello"), - ("human", "Hello, how are you?"), - ] - ) - + ```python + template = ChatPromptTemplate( + [ + SystemMessage(content="hello"), + ("human", "Hello, how are you?"), + ] + ) + ``` """ messages_ = [ _convert_to_message_template(message, template_format) @@ -1104,27 +1101,26 @@ class ChatPromptTemplate(BaseChatPromptTemplate): Examples: Instantiation from a list of message templates: - .. code-block:: python - - template = ChatPromptTemplate.from_messages( - [ - ("human", "Hello, how are you?"), - ("ai", "I'm doing well, thanks!"), - ("human", "That's good to hear."), - ] - ) + ```python + template = ChatPromptTemplate.from_messages( + [ + ("human", "Hello, how are you?"), + ("ai", "I'm doing well, thanks!"), + ("human", "That's good to hear."), + ] + ) + ``` Instantiation from mixed message formats: - .. code-block:: python - - template = ChatPromptTemplate.from_messages( - [ - SystemMessage(content="hello"), - ("human", "Hello, how are you?"), - ] - ) - + ```python + template = ChatPromptTemplate.from_messages( + [ + SystemMessage(content="hello"), + ("human", "Hello, how are you?"), + ] + ) + ``` Args: messages: sequence of message representations. A message can be represented using the following formats: @@ -1208,23 +1204,21 @@ class ChatPromptTemplate(BaseChatPromptTemplate): Example: + ```python + from langchain_core.prompts import ChatPromptTemplate - .. code-block:: python - - from langchain_core.prompts import ChatPromptTemplate - - template = ChatPromptTemplate.from_messages( - [ - ("system", "You are an AI assistant named {name}."), - ("human", "Hi I'm {user}"), - ("ai", "Hi there, {user}, I'm {name}."), - ("human", "{input}"), - ] - ) - template2 = template.partial(user="Lucy", name="R2D2") - - template2.format_messages(input="hello") + template = ChatPromptTemplate.from_messages( + [ + ("system", "You are an AI assistant named {name}."), + ("human", "Hi I'm {user}"), + ("ai", "Hi there, {user}, I'm {name}."), + ("human", "{input}"), + ] + ) + template2 = template.partial(user="Lucy", name="R2D2") + template2.format_messages(input="hello") + ``` """ prompt_dict = self.__dict__.copy() prompt_dict["input_variables"] = list( diff --git a/libs/core/langchain_core/prompts/few_shot.py b/libs/core/langchain_core/prompts/few_shot.py index 0f5fe857801..86ceb03bad4 100644 --- a/libs/core/langchain_core/prompts/few_shot.py +++ b/libs/core/langchain_core/prompts/few_shot.py @@ -268,97 +268,90 @@ class FewShotChatMessagePromptTemplate( Prompt template with a fixed list of examples (matching the sample conversation above): - .. code-block:: python + ```python + from langchain_core.prompts import ( + FewShotChatMessagePromptTemplate, + ChatPromptTemplate, + ) - from langchain_core.prompts import ( - FewShotChatMessagePromptTemplate, - ChatPromptTemplate, - ) + examples = [ + {"input": "2+2", "output": "4"}, + {"input": "2+3", "output": "5"}, + ] - examples = [ - {"input": "2+2", "output": "4"}, - {"input": "2+3", "output": "5"}, + example_prompt = ChatPromptTemplate.from_messages( + [ + ("human", "What is {input}?"), + ("ai", "{output}"), ] + ) - example_prompt = ChatPromptTemplate.from_messages( - [ - ("human", "What is {input}?"), - ("ai", "{output}"), - ] - ) + few_shot_prompt = FewShotChatMessagePromptTemplate( + examples=examples, + # This is a prompt template used to format each individual example. + example_prompt=example_prompt, + ) - few_shot_prompt = FewShotChatMessagePromptTemplate( - examples=examples, - # This is a prompt template used to format each individual example. - example_prompt=example_prompt, - ) - - final_prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a helpful AI Assistant"), - few_shot_prompt, - ("human", "{input}"), - ] - ) - final_prompt.format(input="What is 4+4?") + final_prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a helpful AI Assistant"), + few_shot_prompt, + ("human", "{input}"), + ] + ) + final_prompt.format(input="What is 4+4?") + ``` Prompt template with dynamically selected examples: - .. code-block:: python + ```python + from langchain_core.prompts import SemanticSimilarityExampleSelector + from langchain_core.embeddings import OpenAIEmbeddings + from langchain_core.vectorstores import Chroma - from langchain_core.prompts import SemanticSimilarityExampleSelector - from langchain_core.embeddings import OpenAIEmbeddings - from langchain_core.vectorstores import Chroma + examples = [ + {"input": "2+2", "output": "4"}, + {"input": "2+3", "output": "5"}, + {"input": "2+4", "output": "6"}, + # ... + ] - examples = [ - {"input": "2+2", "output": "4"}, - {"input": "2+3", "output": "5"}, - {"input": "2+4", "output": "6"}, - # ... - ] + to_vectorize = [" ".join(example.values()) for example in examples] + embeddings = OpenAIEmbeddings() + vectorstore = Chroma.from_texts(to_vectorize, embeddings, metadatas=examples) + example_selector = SemanticSimilarityExampleSelector(vectorstore=vectorstore) - to_vectorize = [" ".join(example.values()) for example in examples] - embeddings = OpenAIEmbeddings() - vectorstore = Chroma.from_texts( - to_vectorize, embeddings, metadatas=examples - ) - example_selector = SemanticSimilarityExampleSelector( - vectorstore=vectorstore - ) + from langchain_core import SystemMessage + from langchain_core.prompts import HumanMessagePromptTemplate + from langchain_core.prompts.few_shot import FewShotChatMessagePromptTemplate - from langchain_core import SystemMessage - from langchain_core.prompts import HumanMessagePromptTemplate - from langchain_core.prompts.few_shot import FewShotChatMessagePromptTemplate + few_shot_prompt = FewShotChatMessagePromptTemplate( + # Which variable(s) will be passed to the example selector. + input_variables=["input"], + example_selector=example_selector, + # Define how each example will be formatted. + # In this case, each example will become 2 messages: + # 1 human, and 1 AI + example_prompt=( + HumanMessagePromptTemplate.from_template("{input}") + + AIMessagePromptTemplate.from_template("{output}") + ), + ) + # Define the overall prompt. + final_prompt = ( + SystemMessagePromptTemplate.from_template("You are a helpful AI Assistant") + + few_shot_prompt + + HumanMessagePromptTemplate.from_template("{input}") + ) + # Show the prompt + print(final_prompt.format_messages(input="What's 3+3?")) # noqa: T201 - few_shot_prompt = FewShotChatMessagePromptTemplate( - # Which variable(s) will be passed to the example selector. - input_variables=["input"], - example_selector=example_selector, - # Define how each example will be formatted. - # In this case, each example will become 2 messages: - # 1 human, and 1 AI - example_prompt=( - HumanMessagePromptTemplate.from_template("{input}") - + AIMessagePromptTemplate.from_template("{output}") - ), - ) - # Define the overall prompt. - final_prompt = ( - SystemMessagePromptTemplate.from_template( - "You are a helpful AI Assistant" - ) - + few_shot_prompt - + HumanMessagePromptTemplate.from_template("{input}") - ) - # Show the prompt - print(final_prompt.format_messages(input="What's 3+3?")) # noqa: T201 - - # Use within an LLM - from langchain_core.chat_models import ChatAnthropic - - chain = final_prompt | ChatAnthropic(model="claude-3-haiku-20240307") - chain.invoke({"input": "What's 3+3?"}) + # Use within an LLM + from langchain_core.chat_models import ChatAnthropic + chain = final_prompt | ChatAnthropic(model="claude-3-haiku-20240307") + chain.invoke({"input": "What's 3+3?"}) + ``` """ input_variables: list[str] = Field(default_factory=list) diff --git a/libs/core/langchain_core/prompts/few_shot_with_templates.py b/libs/core/langchain_core/prompts/few_shot_with_templates.py index bf386d6cbff..7d0997da9d5 100644 --- a/libs/core/langchain_core/prompts/few_shot_with_templates.py +++ b/libs/core/langchain_core/prompts/few_shot_with_templates.py @@ -122,11 +122,9 @@ class FewShotPromptWithTemplates(StringPromptTemplate): A formatted string. Example: - - .. code-block:: python - - prompt.format(variable1="foo") - + ```python + prompt.format(variable1="foo") + ``` """ kwargs = self._merge_partial_and_user_variables(**kwargs) # Get the examples to use. diff --git a/libs/core/langchain_core/prompts/image.py b/libs/core/langchain_core/prompts/image.py index 9f75013ed63..26987576641 100644 --- a/libs/core/langchain_core/prompts/image.py +++ b/libs/core/langchain_core/prompts/image.py @@ -96,11 +96,9 @@ class ImagePromptTemplate(BasePromptTemplate[ImageURL]): ValueError: If `'path'` is provided in the template or kwargs. Example: - - .. code-block:: python - - prompt.format(variable1="foo") - + ```python + prompt.format(variable1="foo") + ``` """ formatted = {} for k, v in self.template.items(): diff --git a/libs/core/langchain_core/prompts/prompt.py b/libs/core/langchain_core/prompts/prompt.py index 7b278add124..59fdf0882d7 100644 --- a/libs/core/langchain_core/prompts/prompt.py +++ b/libs/core/langchain_core/prompts/prompt.py @@ -44,18 +44,16 @@ class PromptTemplate(StringPromptTemplate): from untrusted sources. Example: + ```python + from langchain_core.prompts import PromptTemplate - .. code-block:: python - - from langchain_core.prompts import PromptTemplate - - # Instantiation using from_template (recommended) - prompt = PromptTemplate.from_template("Say {foo}") - prompt.format(foo="bar") - - # Instantiation using initializer - prompt = PromptTemplate(template="Say {foo}") + # Instantiation using from_template (recommended) + prompt = PromptTemplate.from_template("Say {foo}") + prompt.format(foo="bar") + # Instantiation using initializer + prompt = PromptTemplate(template="Say {foo}") + ``` """ @property diff --git a/libs/core/langchain_core/prompts/structured.py b/libs/core/langchain_core/prompts/structured.py index 59a1c2754a1..158c90be170 100644 --- a/libs/core/langchain_core/prompts/structured.py +++ b/libs/core/langchain_core/prompts/structured.py @@ -85,25 +85,24 @@ class StructuredPrompt(ChatPromptTemplate): Examples: Instantiation from a list of message templates: - .. code-block:: python - - from langchain_core.prompts import StructuredPrompt + ```python + from langchain_core.prompts import StructuredPrompt - class OutputSchema(BaseModel): - name: str - value: int + class OutputSchema(BaseModel): + name: str + value: int - template = StructuredPrompt( - [ - ("human", "Hello, how are you?"), - ("ai", "I'm doing well, thanks!"), - ("human", "That's good to hear."), - ], - OutputSchema, - ) - + template = StructuredPrompt( + [ + ("human", "Hello, how are you?"), + ("ai", "I'm doing well, thanks!"), + ("human", "That's good to hear."), + ], + OutputSchema, + ) + ``` Args: messages: sequence of message representations. A message can be represented using the following formats: diff --git a/libs/core/langchain_core/rate_limiters.py b/libs/core/langchain_core/rate_limiters.py index ff6129ef5c3..dd0753fbc41 100644 --- a/libs/core/langchain_core/rate_limiters.py +++ b/libs/core/langchain_core/rate_limiters.py @@ -90,36 +90,34 @@ class InMemoryRateLimiter(BaseRateLimiter): Current limitations: - The rate limiter is not designed to work across different processes. It is - an in-memory rate limiter, but it is thread safe. + an in-memory rate limiter, but it is thread safe. - The rate limiter only supports time-based rate limiting. It does not take - into account the size of the request or any other factors. + into account the size of the request or any other factors. Example: + ```python + import time - .. code-block:: python + from langchain_core.rate_limiters import InMemoryRateLimiter - import time + rate_limiter = InMemoryRateLimiter( + requests_per_second=0.1, # <-- Can only make a request once every 10 seconds!! + check_every_n_seconds=0.1, # Wake up every 100 ms to check whether allowed to make a request, + max_bucket_size=10, # Controls the maximum burst size. + ) - from langchain_core.rate_limiters import InMemoryRateLimiter + from langchain_anthropic import ChatAnthropic - rate_limiter = InMemoryRateLimiter( - requests_per_second=0.1, # <-- Can only make a request once every 10 seconds!! - check_every_n_seconds=0.1, # Wake up every 100 ms to check whether allowed to make a request, - max_bucket_size=10, # Controls the maximum burst size. - ) - - from langchain_anthropic import ChatAnthropic - - model = ChatAnthropic( - model_name="claude-3-opus-20240229", rate_limiter=rate_limiter - ) - - for _ in range(5): - tic = time.time() - model.invoke("hello") - toc = time.time() - print(toc - tic) + model = ChatAnthropic( + model_name="claude-3-opus-20240229", rate_limiter=rate_limiter + ) + for _ in range(5): + tic = time.time() + model.invoke("hello") + toc = time.time() + print(toc - tic) + ``` !!! version-added "Added in version 0.2.24" diff --git a/libs/core/langchain_core/retrievers.py b/libs/core/langchain_core/retrievers.py index 2c1538a1469..74b9eb5cdb6 100644 --- a/libs/core/langchain_core/retrievers.py +++ b/libs/core/langchain_core/retrievers.py @@ -3,20 +3,6 @@ It is more general than a vector store. A retriever does not need to be able to store documents, only to return (or retrieve) it. Vector stores can be used as the backbone of a retriever, but there are other types of retrievers as well. - -**Class hierarchy:** - -.. code-block:: - - BaseRetriever --> Retriever # Examples: ArxivRetriever, MergerRetriever - -**Main helpers:** - -.. code-block:: - - RetrieverInput, RetrieverOutput, RetrieverLike, RetrieverOutputLike, - Document, Serializable, Callbacks, - CallbackManagerForRetrieverRun, AsyncCallbackManagerForRetrieverRun """ from __future__ import annotations @@ -87,48 +73,45 @@ class BaseRetriever(RunnableSerializable[RetrieverInput, RetrieverOutput], ABC): Example: A retriever that returns the first 5 documents from a list of documents - .. code-block:: python + ```python + from langchain_core.documents import Document + from langchain_core.retrievers import BaseRetriever - from langchain_core.documents import Document - from langchain_core.retrievers import BaseRetriever + class SimpleRetriever(BaseRetriever): + docs: list[Document] + k: int = 5 - class SimpleRetriever(BaseRetriever): - docs: list[Document] - k: int = 5 + def _get_relevant_documents(self, query: str) -> list[Document]: + \"\"\"Return the first k documents from the list of documents\"\"\" + return self.docs[:self.k] - def _get_relevant_documents(self, query: str) -> list[Document]: - \"\"\"Return the first k documents from the list of documents\"\"\" - return self.docs[:self.k] - - async def _aget_relevant_documents(self, query: str) -> list[Document]: - \"\"\"(Optional) async native implementation.\"\"\" - return self.docs[:self.k] + async def _aget_relevant_documents(self, query: str) -> list[Document]: + \"\"\"(Optional) async native implementation.\"\"\" + return self.docs[:self.k] + ``` Example: A simple retriever based on a scikit-learn vectorizer - .. code-block:: python - - from sklearn.metrics.pairwise import cosine_similarity + ```python + from sklearn.metrics.pairwise import cosine_similarity - class TFIDFRetriever(BaseRetriever, BaseModel): - vectorizer: Any - docs: list[Document] - tfidf_array: Any - k: int = 4 + class TFIDFRetriever(BaseRetriever, BaseModel): + vectorizer: Any + docs: list[Document] + tfidf_array: Any + k: int = 4 - class Config: - arbitrary_types_allowed = True - - def _get_relevant_documents(self, query: str) -> list[Document]: - # Ip -- (n_docs,x), Op -- (n_docs,n_Feats) - query_vec = self.vectorizer.transform([query]) - # Op -- (n_docs,1) -- Cosine Sim with each doc - results = cosine_similarity(self.tfidf_array, query_vec).reshape( - (-1,) - ) - return [self.docs[i] for i in results.argsort()[-self.k :][::-1]] + class Config: + arbitrary_types_allowed = True + def _get_relevant_documents(self, query: str) -> list[Document]: + # Ip -- (n_docs,x), Op -- (n_docs,n_Feats) + query_vec = self.vectorizer.transform([query]) + # Op -- (n_docs,1) -- Cosine Sim with each doc + results = cosine_similarity(self.tfidf_array, query_vec).reshape((-1,)) + return [self.docs[i] for i in results.argsort()[-self.k :][::-1]] + ``` """ model_config = ConfigDict( @@ -231,11 +214,9 @@ class BaseRetriever(RunnableSerializable[RetrieverInput, RetrieverOutput], ABC): List of relevant documents. Examples: - - .. code-block:: python - - retriever.invoke("query") - + ```python + retriever.invoke("query") + ``` """ config = ensure_config(config) inheritable_metadata = { @@ -294,11 +275,9 @@ class BaseRetriever(RunnableSerializable[RetrieverInput, RetrieverOutput], ABC): List of relevant documents. Examples: - - .. code-block:: python - - await retriever.ainvoke("query") - + ```python + await retriever.ainvoke("query") + ``` """ config = ensure_config(config) inheritable_metadata = { diff --git a/libs/core/langchain_core/runnables/graph_ascii.py b/libs/core/langchain_core/runnables/graph_ascii.py index cc06d7f087f..b7d6edf39b2 100644 --- a/libs/core/langchain_core/runnables/graph_ascii.py +++ b/libs/core/langchain_core/runnables/graph_ascii.py @@ -267,7 +267,8 @@ def draw_ascii(vertices: Mapping[str, str], edges: Sequence[LangEdge]) -> str: print(draw_ascii(vertices, edges)) ``` - .. code-block:: + + ```txt +---+ | 1 | @@ -284,7 +285,7 @@ def draw_ascii(vertices: Mapping[str, str], edges: Sequence[LangEdge]) -> str: +---+ +---+ | 3 | | 4 | +---+ +---+ - + ``` """ # NOTE: coordinates might me negative, so we need to shift # everything to the positive plane before we actually draw it. diff --git a/libs/core/langchain_core/stores.py b/libs/core/langchain_core/stores.py index 745c34bc339..2c570b66580 100644 --- a/libs/core/langchain_core/stores.py +++ b/libs/core/langchain_core/stores.py @@ -47,36 +47,34 @@ class BaseStore(ABC, Generic[K, V]): which will usually be more efficient by saving on round trips to the store. Examples: - - .. code-block:: python - - from langchain.storage import BaseStore + ```python + from langchain.storage import BaseStore - class MyInMemoryStore(BaseStore[str, int]): - def __init__(self) -> None: - self.store: dict[str, int] = {} + class MyInMemoryStore(BaseStore[str, int]): + def __init__(self) -> None: + self.store: dict[str, int] = {} - def mget(self, keys: Sequence[str]) -> list[int | None]: - return [self.store.get(key) for key in keys] + def mget(self, keys: Sequence[str]) -> list[int | None]: + return [self.store.get(key) for key in keys] - def mset(self, key_value_pairs: Sequence[tuple[str, int]]) -> None: - for key, value in key_value_pairs: - self.store[key] = value + def mset(self, key_value_pairs: Sequence[tuple[str, int]]) -> None: + for key, value in key_value_pairs: + self.store[key] = value - def mdelete(self, keys: Sequence[str]) -> None: - for key in keys: - if key in self.store: - del self.store[key] - - def yield_keys(self, prefix: str | None = None) -> Iterator[str]: - if prefix is None: - yield from self.store.keys() - else: - for key in self.store.keys(): - if key.startswith(prefix): - yield key + def mdelete(self, keys: Sequence[str]) -> None: + for key in keys: + if key in self.store: + del self.store[key] + def yield_keys(self, prefix: str | None = None) -> Iterator[str]: + if prefix is None: + yield from self.store.keys() + else: + for key in self.store.keys(): + if key.startswith(prefix): + yield key + ``` """ @abstractmethod @@ -249,21 +247,19 @@ class InMemoryStore(InMemoryBaseStore[Any]): the key-value pairs. Examples: + ```python + from langchain.storage import InMemoryStore - .. code-block:: python - - from langchain.storage import InMemoryStore - - store = InMemoryStore() - store.mset([("key1", "value1"), ("key2", "value2")]) - store.mget(["key1", "key2"]) - # ['value1', 'value2'] - store.mdelete(["key1"]) - list(store.yield_keys()) - # ['key2'] - list(store.yield_keys(prefix="k")) - # ['key2'] - + store = InMemoryStore() + store.mset([("key1", "value1"), ("key2", "value2")]) + store.mget(["key1", "key2"]) + # ['value1', 'value2'] + store.mdelete(["key1"]) + list(store.yield_keys()) + # ['key2'] + list(store.yield_keys(prefix="k")) + # ['key2'] + ``` """ @@ -275,21 +271,19 @@ class InMemoryByteStore(InMemoryBaseStore[bytes]): the key-value pairs. Examples: + ```python + from langchain.storage import InMemoryByteStore - .. code-block:: python - - from langchain.storage import InMemoryByteStore - - store = InMemoryByteStore() - store.mset([("key1", b"value1"), ("key2", b"value2")]) - store.mget(["key1", "key2"]) - # [b'value1', b'value2'] - store.mdelete(["key1"]) - list(store.yield_keys()) - # ['key2'] - list(store.yield_keys(prefix="k")) - # ['key2'] - + store = InMemoryByteStore() + store.mset([("key1", b"value1"), ("key2", b"value2")]) + store.mget(["key1", "key2"]) + # [b'value1', b'value2'] + store.mdelete(["key1"]) + list(store.yield_keys()) + # ['key2'] + list(store.yield_keys(prefix="k")) + # ['key2'] + ``` """ diff --git a/libs/core/langchain_core/tools/__init__.py b/libs/core/langchain_core/tools/__init__.py index 1ead0e8a4f1..3d4642dc4be 100644 --- a/libs/core/langchain_core/tools/__init__.py +++ b/libs/core/langchain_core/tools/__init__.py @@ -2,20 +2,7 @@ Each tool has a **description**. Agent uses the description to choose the right tool for the job. - -**Class hierarchy:** - -.. code-block:: - - RunnableSerializable --> BaseTool --> Tool # Examples: AIPluginTool, BaseGraphQLTool - # Examples: BraveSearch, HumanInputRun - -**Main helpers:** - -.. code-block:: - - CallbackManagerForToolRun, AsyncCallbackManagerForToolRun -""" # noqa: E501 +""" from __future__ import annotations diff --git a/libs/core/langchain_core/tools/base.py b/libs/core/langchain_core/tools/base.py index fe77c878915..adeb49e6150 100644 --- a/libs/core/langchain_core/tools/base.py +++ b/libs/core/langchain_core/tools/base.py @@ -1217,24 +1217,24 @@ class InjectedToolCallId(InjectedToolArg): This annotation is used to mark a tool parameter that should receive the tool call ID at runtime. - .. code-block:: python + ```python + from typing import Annotated + from langchain_core.messages import ToolMessage + from langchain_core.tools import tool, InjectedToolCallId - from typing import Annotated - from langchain_core.messages import ToolMessage - from langchain_core.tools import tool, InjectedToolCallId - - @tool - def foo( - x: int, tool_call_id: Annotated[str, InjectedToolCallId] - ) -> ToolMessage: - \"\"\"Return x.\"\"\" - return ToolMessage( - str(x), - artifact=x, - name="foo", - tool_call_id=tool_call_id - ) + @tool + def foo( + x: int, tool_call_id: Annotated[str, InjectedToolCallId] + ) -> ToolMessage: + \"\"\"Return x.\"\"\" + return ToolMessage( + str(x), + artifact=x, + name="foo", + tool_call_id=tool_call_id + ) + ``` """ diff --git a/libs/core/langchain_core/tools/convert.py b/libs/core/langchain_core/tools/convert.py index 392638c8aa5..0bc4e2e98b6 100644 --- a/libs/core/langchain_core/tools/convert.py +++ b/libs/core/langchain_core/tools/convert.py @@ -137,94 +137,94 @@ def tool( - Function must have a docstring Examples: - .. code-block:: python - - @tool - def search_api(query: str) -> str: - # Searches the API for the query. - return + ```python + @tool + def search_api(query: str) -> str: + # Searches the API for the query. + return - @tool("search", return_direct=True) - def search_api(query: str) -> str: - # Searches the API for the query. - return + @tool("search", return_direct=True) + def search_api(query: str) -> str: + # Searches the API for the query. + return - @tool(response_format="content_and_artifact") - def search_api(query: str) -> tuple[str, dict]: - return "partial json of results", {"full": "object of results"} + @tool(response_format="content_and_artifact") + def search_api(query: str) -> tuple[str, dict]: + return "partial json of results", {"full": "object of results"} + ``` !!! version-added "Added in version 0.2.14" Parse Google-style docstrings: - .. code-block:: python + ```python + @tool(parse_docstring=True) + def foo(bar: str, baz: int) -> str: + \"\"\"The foo. - @tool(parse_docstring=True) - def foo(bar: str, baz: int) -> str: - \"\"\"The foo. + Args: + bar: The bar. + baz: The baz. + \"\"\" + return bar - Args: - bar: The bar. - baz: The baz. - \"\"\" - return bar + foo.args_schema.model_json_schema() + ``` - foo.args_schema.model_json_schema() - - .. code-block:: python - - { - "title": "foo", - "description": "The foo.", - "type": "object", - "properties": { - "bar": { - "title": "Bar", - "description": "The bar.", - "type": "string", - }, - "baz": { - "title": "Baz", - "description": "The baz.", - "type": "integer", - }, + ```python + { + "title": "foo", + "description": "The foo.", + "type": "object", + "properties": { + "bar": { + "title": "Bar", + "description": "The bar.", + "type": "string", }, - "required": ["bar", "baz"], - } + "baz": { + "title": "Baz", + "description": "The baz.", + "type": "integer", + }, + }, + "required": ["bar", "baz"], + } + ``` Note that parsing by default will raise `ValueError` if the docstring is considered invalid. A docstring is considered invalid if it contains arguments not in the function signature, or is unable to be parsed into a summary and "Args:" blocks. Examples below: - .. code-block:: python + ```python + # No args section + def invalid_docstring_1(bar: str, baz: int) -> str: + \"\"\"The foo.\"\"\" + return bar - # No args section - def invalid_docstring_1(bar: str, baz: int) -> str: - \"\"\"The foo.\"\"\" - return bar + # Improper whitespace between summary and args section + def invalid_docstring_2(bar: str, baz: int) -> str: + \"\"\"The foo. + Args: + bar: The bar. + baz: The baz. + \"\"\" + return bar - # Improper whitespace between summary and args section - def invalid_docstring_2(bar: str, baz: int) -> str: - \"\"\"The foo. - Args: - bar: The bar. - baz: The baz. - \"\"\" - return bar + # Documented args absent from function signature + def invalid_docstring_3(bar: str, baz: int) -> str: + \"\"\"The foo. - # Documented args absent from function signature - def invalid_docstring_3(bar: str, baz: int) -> str: - \"\"\"The foo. - - Args: - banana: The bar. - monkey: The baz. - \"\"\" - return bar + Args: + banana: The bar. + monkey: The baz. + \"\"\" + return bar + ``` """ # noqa: D214, D410, D411 # We're intentionally showing bad formatting in examples def _create_tool_factory( diff --git a/libs/core/langchain_core/tools/render.py b/libs/core/langchain_core/tools/render.py index 576dad93530..d0f8b10149e 100644 --- a/libs/core/langchain_core/tools/render.py +++ b/libs/core/langchain_core/tools/render.py @@ -21,10 +21,10 @@ def render_text_description(tools: list[BaseTool]) -> str: Output will be in the format of: - .. code-block:: markdown - - search: This tool is used for search - calculator: This tool is used for math + ```txt + search: This tool is used for search + calculator: This tool is used for math + ``` """ descriptions = [] for tool in tools: @@ -49,11 +49,11 @@ def render_text_description_and_args(tools: list[BaseTool]) -> str: Output will be in the format of: - .. code-block:: markdown - - search: This tool is used for search, args: {"query": {"type": "string"}} - calculator: This tool is used for math, \ -args: {"expression": {"type": "string"}} + ```txt + search: This tool is used for search, args: {"query": {"type": "string"}} + calculator: This tool is used for math, \ + args: {"expression": {"type": "string"}} + ``` """ tool_strings = [] for tool in tools: diff --git a/libs/core/langchain_core/tools/structured.py b/libs/core/langchain_core/tools/structured.py index ae9e001ea7e..cc978446f3b 100644 --- a/libs/core/langchain_core/tools/structured.py +++ b/libs/core/langchain_core/tools/structured.py @@ -176,15 +176,14 @@ class StructuredTool(BaseTool): TypeError: If the `args_schema` is not a `BaseModel` or dict. Examples: + ```python + def add(a: int, b: int) -> int: + \"\"\"Add two numbers\"\"\" + return a + b + tool = StructuredTool.from_function(add) + tool.run(1, 2) # 3 - .. code-block:: python - - def add(a: int, b: int) -> int: - \"\"\"Add two numbers\"\"\" - return a + b - tool = StructuredTool.from_function(add) - tool.run(1, 2) # 3 - + ``` """ if func is not None: source_function = func diff --git a/libs/core/langchain_core/tracers/__init__.py b/libs/core/langchain_core/tracers/__init__.py index 9a83c52e189..b2f41d10f82 100644 --- a/libs/core/langchain_core/tracers/__init__.py +++ b/libs/core/langchain_core/tracers/__init__.py @@ -1,12 +1,4 @@ -"""**Tracers** are classes for tracing runs. - -**Class hierarchy:** - -.. code-block:: - - BaseCallbackHandler --> BaseTracer --> Tracer # Examples: LangChainTracer, RootListenersTracer - --> # Examples: LogStreamCallbackHandler -""" # noqa: E501 +"""**Tracers** are classes for tracing runs.""" from typing import TYPE_CHECKING diff --git a/libs/core/langchain_core/utils/aiter.py b/libs/core/langchain_core/utils/aiter.py index c3613d0fb96..e910cee43c6 100644 --- a/libs/core/langchain_core/utils/aiter.py +++ b/libs/core/langchain_core/utils/aiter.py @@ -163,12 +163,12 @@ class Tee(Generic[T]): A `tee` works lazily and can handle an infinite `iterable`, provided that all iterators advance. - .. code-block:: python - - async def derivative(sensor_data): - previous, current = a.tee(sensor_data, n=2) - await a.anext(previous) # advance one iterator - return a.map(operator.sub, previous, current) + ```python + async def derivative(sensor_data): + previous, current = a.tee(sensor_data, n=2) + await a.anext(previous) # advance one iterator + return a.map(operator.sub, previous, current) + ``` Unlike `itertools.tee`, `.tee` returns a custom type instead of a :py`tuple`. Like a tuple, it can be indexed, iterated and unpacked @@ -273,21 +273,21 @@ class aclosing(AbstractAsyncContextManager): # noqa: N801 Code like this: - .. code-block:: python - - async with aclosing(.fetch()) as agen: - + ```python + async with aclosing(.fetch()) as agen: + + ``` is equivalent to this: - .. code-block:: python - - agen = .fetch() - try: - - finally: - await agen.aclose() + ```python + agen = .fetch() + try: + + finally: + await agen.aclose() + ``` """ def __init__(self, thing: AsyncGenerator[Any, Any] | AsyncIterator[Any]) -> None: diff --git a/libs/core/langchain_core/utils/function_calling.py b/libs/core/langchain_core/utils/function_calling.py index 0ed4575217c..5ba5efa59e7 100644 --- a/libs/core/langchain_core/utils/function_calling.py +++ b/libs/core/langchain_core/utils/function_calling.py @@ -672,43 +672,39 @@ def tool_example_to_messages( A list of messages Examples: - - .. code-block:: python - - from typing import Optional - from pydantic import BaseModel, Field - from langchain_openai import ChatOpenAI + ```python + from typing import Optional + from pydantic import BaseModel, Field + from langchain_openai import ChatOpenAI - class Person(BaseModel): - '''Information about a person.''' + class Person(BaseModel): + '''Information about a person.''' - name: str | None = Field(..., description="The name of the person") - hair_color: str | None = Field( - ..., description="The color of the person's hair if known" - ) - height_in_meters: str | None = Field( - ..., description="Height in METERS" - ) + name: str | None = Field(..., description="The name of the person") + hair_color: str | None = Field( + ..., description="The color of the person's hair if known" + ) + height_in_meters: str | None = Field(..., description="Height in METERS") - examples = [ - ( - "The ocean is vast and blue. It's more than 20,000 feet deep.", - Person(name=None, height_in_meters=None, hair_color=None), - ), - ( - "Fiona traveled far from France to Spain.", - Person(name="Fiona", height_in_meters=None, hair_color=None), - ), - ] + examples = [ + ( + "The ocean is vast and blue. It's more than 20,000 feet deep.", + Person(name=None, height_in_meters=None, hair_color=None), + ), + ( + "Fiona traveled far from France to Spain.", + Person(name="Fiona", height_in_meters=None, hair_color=None), + ), + ] - messages = [] - - for txt, tool_call in examples: - messages.extend(tool_example_to_messages(txt, [tool_call])) + messages = [] + for txt, tool_call in examples: + messages.extend(tool_example_to_messages(txt, [tool_call])) + ``` """ messages: list[BaseMessage] = [HumanMessage(content=input)] openai_tool_calls = [ diff --git a/libs/core/langchain_core/utils/iter.py b/libs/core/langchain_core/utils/iter.py index 4684bf17e8b..2334d9063f6 100644 --- a/libs/core/langchain_core/utils/iter.py +++ b/libs/core/langchain_core/utils/iter.py @@ -99,12 +99,12 @@ class Tee(Generic[T]): A `tee` works lazily and can handle an infinite `iterable`, provided that all iterators advance. - .. code-block:: python - - async def derivative(sensor_data): - previous, current = a.tee(sensor_data, n=2) - await a.anext(previous) # advance one iterator - return a.map(operator.sub, previous, current) + ```python + async def derivative(sensor_data): + previous, current = a.tee(sensor_data, n=2) + await a.anext(previous) # advance one iterator + return a.map(operator.sub, previous, current) + ``` Unlike `itertools.tee`, `.tee` returns a custom type instead of a :py`tuple`. Like a tuple, it can be indexed, iterated and unpacked diff --git a/libs/core/langchain_core/utils/utils.py b/libs/core/langchain_core/utils/utils.py index a7070dd5edd..3d9c2a838bf 100644 --- a/libs/core/langchain_core/utils/utils.py +++ b/libs/core/langchain_core/utils/utils.py @@ -82,11 +82,10 @@ def mock_now(dt_value: datetime.datetime) -> Iterator[type]: The mocked datetime class. Example: - - .. code-block:: python - - with mock_now(datetime.datetime(2011, 2, 3, 10, 11)): - assert datetime.datetime.now() == datetime.datetime(2011, 2, 3, 10, 11) + ```python + with mock_now(datetime.datetime(2011, 2, 3, 10, 11)): + assert datetime.datetime.now() == datetime.datetime(2011, 2, 3, 10, 11) + ``` """ class MockDateTime(datetime.datetime): diff --git a/libs/core/langchain_core/vectorstores/base.py b/libs/core/langchain_core/vectorstores/base.py index e275697e22b..fa15a3018aa 100644 --- a/libs/core/langchain_core/vectorstores/base.py +++ b/libs/core/langchain_core/vectorstores/base.py @@ -3,21 +3,7 @@ One of the most common ways to store and search over unstructured data is to embed it and store the resulting embedding vectors, and then query the store and retrieve the data that are 'most similar' to the embedded query. - -**Class hierarchy:** - -.. code-block:: - - VectorStore --> # Examples: Annoy, FAISS, Milvus - - BaseRetriever --> VectorStoreRetriever --> Retriever # Example: VespaRetriever - -**Main helpers:** - -.. code-block:: - - Embeddings, Document -""" # noqa: E501 +""" from __future__ import annotations @@ -946,36 +932,32 @@ class VectorStore(ABC): Retriever class for VectorStore. Examples: + ```python + # Retrieve more documents with higher diversity + # Useful if your dataset has many similar documents + docsearch.as_retriever( + search_type="mmr", search_kwargs={"k": 6, "lambda_mult": 0.25} + ) - .. code-block:: python + # Fetch more documents for the MMR algorithm to consider + # But only return the top 5 + docsearch.as_retriever(search_type="mmr", search_kwargs={"k": 5, "fetch_k": 50}) - # Retrieve more documents with higher diversity - # Useful if your dataset has many similar documents - docsearch.as_retriever( - search_type="mmr", search_kwargs={"k": 6, "lambda_mult": 0.25} - ) + # Only retrieve documents that have a relevance score + # Above a certain threshold + docsearch.as_retriever( + search_type="similarity_score_threshold", + search_kwargs={"score_threshold": 0.8}, + ) - # Fetch more documents for the MMR algorithm to consider - # But only return the top 5 - docsearch.as_retriever( - search_type="mmr", search_kwargs={"k": 5, "fetch_k": 50} - ) - - # Only retrieve documents that have a relevance score - # Above a certain threshold - docsearch.as_retriever( - search_type="similarity_score_threshold", - search_kwargs={"score_threshold": 0.8}, - ) - - # Only get the single most similar document from the dataset - docsearch.as_retriever(search_kwargs={"k": 1}) - - # Use a filter to only retrieve documents from a specific paper - docsearch.as_retriever( - search_kwargs={"filter": {"paper_title": "GPT-4 Technical Report"}} - ) + # Only get the single most similar document from the dataset + docsearch.as_retriever(search_kwargs={"k": 1}) + # Use a filter to only retrieve documents from a specific paper + docsearch.as_retriever( + search_kwargs={"filter": {"paper_title": "GPT-4 Technical Report"}} + ) + ``` """ tags = kwargs.pop("tags", None) or [*self._get_retriever_tags()] return VectorStoreRetriever(vectorstore=self, tags=tags, **kwargs) diff --git a/libs/core/langchain_core/vectorstores/in_memory.py b/libs/core/langchain_core/vectorstores/in_memory.py index 751ce1610ee..91b5c2c243f 100644 --- a/libs/core/langchain_core/vectorstores/in_memory.py +++ b/libs/core/langchain_core/vectorstores/in_memory.py @@ -40,124 +40,122 @@ class InMemoryVectorStore(VectorStore): Setup: Install `langchain-core`. - .. code-block:: bash - - pip install -U langchain-core + ```bash + pip install -U langchain-core + ``` Key init args — indexing params: embedding_function: Embeddings Embedding function to use. Instantiate: - .. code-block:: python + ```python + from langchain_core.vectorstores import InMemoryVectorStore + from langchain_openai import OpenAIEmbeddings - from langchain_core.vectorstores import InMemoryVectorStore - from langchain_openai import OpenAIEmbeddings - - vector_store = InMemoryVectorStore(OpenAIEmbeddings()) + vector_store = InMemoryVectorStore(OpenAIEmbeddings()) + ``` Add Documents: - .. code-block:: python + ```python + from langchain_core.documents import Document - from langchain_core.documents import Document + document_1 = Document(id="1", page_content="foo", metadata={"baz": "bar"}) + document_2 = Document(id="2", page_content="thud", metadata={"bar": "baz"}) + document_3 = Document(id="3", page_content="i will be deleted :(") - document_1 = Document(id="1", page_content="foo", metadata={"baz": "bar"}) - document_2 = Document(id="2", page_content="thud", metadata={"bar": "baz"}) - document_3 = Document(id="3", page_content="i will be deleted :(") - - documents = [document_1, document_2, document_3] - vector_store.add_documents(documents=documents) + documents = [document_1, document_2, document_3] + vector_store.add_documents(documents=documents) + ``` Inspect documents: - .. code-block:: python - - top_n = 10 - for index, (id, doc) in enumerate(vector_store.store.items()): - if index < top_n: - # docs have keys 'id', 'vector', 'text', 'metadata' - print(f"{id}: {doc['text']}") - else: - break + ```python + top_n = 10 + for index, (id, doc) in enumerate(vector_store.store.items()): + if index < top_n: + # docs have keys 'id', 'vector', 'text', 'metadata' + print(f"{id}: {doc['text']}") + else: + break + ``` Delete Documents: - .. code-block:: python - - vector_store.delete(ids=["3"]) + ```python + vector_store.delete(ids=["3"]) + ``` Search: - .. code-block:: python + ```python + results = vector_store.similarity_search(query="thud", k=1) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search(query="thud", k=1) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: - - * thud [{'bar': 'baz'}] + ```txt + * thud [{'bar': 'baz'}] + ``` Search with filter: - .. code-block:: python - - def _filter_function(doc: Document) -> bool: - return doc.metadata.get("bar") == "baz" + ```python + def _filter_function(doc: Document) -> bool: + return doc.metadata.get("bar") == "baz" - results = vector_store.similarity_search( - query="thud", k=1, filter=_filter_function - ) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: - - * thud [{'bar': 'baz'}] + results = vector_store.similarity_search( + query="thud", k=1, filter=_filter_function + ) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` + ```txt + * thud [{'bar': 'baz'}] + ``` Search with score: - .. code-block:: python + ```python + results = vector_store.similarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: - - * [SIM=0.832268] foo [{'baz': 'bar'}] + ```txt + * [SIM=0.832268] foo [{'baz': 'bar'}] + ``` Async: - .. code-block:: python + ```python + # add documents + # await vector_store.aadd_documents(documents=documents) - # add documents - # await vector_store.aadd_documents(documents=documents) + # delete documents + # await vector_store.adelete(ids=["3"]) - # delete documents - # await vector_store.adelete(ids=["3"]) + # search + # results = vector_store.asimilarity_search(query="thud", k=1) - # search - # results = vector_store.asimilarity_search(query="thud", k=1) + # search with score + results = await vector_store.asimilarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - # search with score - results = await vector_store.asimilarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: - - * [SIM=0.832268] foo [{'baz': 'bar'}] + ```txt + * [SIM=0.832268] foo [{'baz': 'bar'}] + ``` Use as Retriever: - .. code-block:: python - - retriever = vector_store.as_retriever( - search_type="mmr", - search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, - ) - retriever.invoke("thud") - - .. code-block:: - - [Document(id='2', metadata={'bar': 'baz'}, page_content='thud')] + ```python + retriever = vector_store.as_retriever( + search_type="mmr", + search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, + ) + retriever.invoke("thud") + ``` + ```txt + [Document(id='2', metadata={'bar': 'baz'}, page_content='thud')] + ``` """ def __init__(self, embedding: Embeddings) -> None: diff --git a/libs/core/tests/unit_tests/conftest.py b/libs/core/tests/unit_tests/conftest.py index 83f39adc1c1..06962b7f2e1 100644 --- a/libs/core/tests/unit_tests/conftest.py +++ b/libs/core/tests/unit_tests/conftest.py @@ -61,11 +61,10 @@ def pytest_collection_modifyitems( The `requires` marker syntax is: - .. code-block:: python - - @pytest.mark.requires("package1", "package2") - def test_something(): ... - + ```python + @pytest.mark.requires("package1", "package2") + def test_something(): ... + ``` """ # Mapping from the name of a package to whether it is installed or not. # Used to avoid repeated calls to `util.find_spec` diff --git a/libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr b/libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr index 1c83ebe84c3..282713e6f33 100644 --- a/libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr +++ b/libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr @@ -523,19 +523,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -681,13 +680,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -800,12 +799,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -831,18 +830,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -986,10 +984,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -1037,16 +1034,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -1118,34 +1114,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -1319,22 +1314,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -1949,19 +1944,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -2107,13 +2101,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -2226,12 +2220,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -2257,18 +2251,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -2412,10 +2405,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -2463,16 +2455,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -2544,34 +2535,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -2745,22 +2735,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. diff --git a/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr b/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr index c1f761ac7f2..b2d8068bfd8 100644 --- a/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr +++ b/libs/core/tests/unit_tests/runnables/__snapshots__/test_graph.ambr @@ -947,19 +947,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -1105,13 +1104,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -1224,12 +1223,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -1255,18 +1254,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -1410,10 +1408,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -1461,16 +1458,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -1542,34 +1538,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -1743,22 +1738,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. diff --git a/libs/core/tests/unit_tests/runnables/__snapshots__/test_runnable.ambr b/libs/core/tests/unit_tests/runnables/__snapshots__/test_runnable.ambr index f10e6b4f721..599ef032e39 100644 --- a/libs/core/tests/unit_tests/runnables/__snapshots__/test_runnable.ambr +++ b/libs/core/tests/unit_tests/runnables/__snapshots__/test_runnable.ambr @@ -2472,19 +2472,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -2628,13 +2627,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -2746,12 +2745,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -2777,18 +2776,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -2930,10 +2928,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -2980,16 +2977,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -3060,34 +3056,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -3259,22 +3254,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -3941,19 +3936,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -4097,13 +4091,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -4215,12 +4209,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -4265,18 +4259,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -4418,10 +4411,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -4468,16 +4460,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -4548,34 +4539,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -4747,22 +4737,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -5441,19 +5431,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -5597,13 +5586,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -5715,12 +5704,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -5765,18 +5754,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -5918,10 +5906,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -5968,16 +5955,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -6048,34 +6034,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -6247,22 +6232,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -6816,19 +6801,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -6972,13 +6956,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -7090,12 +7074,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -7121,18 +7105,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -7274,10 +7257,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -7324,16 +7306,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -7404,34 +7385,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -7603,22 +7583,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -8327,19 +8307,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -8483,13 +8462,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -8601,12 +8580,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -8651,18 +8630,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -8804,10 +8782,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -8854,16 +8831,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -8934,34 +8910,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -9133,22 +9108,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -9747,19 +9722,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -9903,13 +9877,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -10021,12 +9995,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -10052,18 +10026,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -10205,10 +10178,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -10255,16 +10227,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -10335,34 +10306,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -10534,22 +10504,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -11166,19 +11136,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -11322,13 +11291,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -11440,12 +11409,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -11501,18 +11470,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -11654,10 +11622,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -11704,16 +11671,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -11784,34 +11750,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -11983,22 +11948,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. @@ -12627,19 +12592,18 @@ `HumanMessage`s are messages that are passed in from a human to the model. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Instantiate a chat model and invoke it with the messages - model = ... - print(model.invoke(messages)) + # Instantiate a chat model and invoke it with the messages + model = ... + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -12783,13 +12747,13 @@ Does *not* need to sum to full input token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - } + ```python + { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + } + ``` !!! version-added "Added in version 0.3.9" @@ -12901,12 +12865,12 @@ Does *not* need to sum to full output token count. Does *not* need to have all keys. Example: - .. code-block:: python - - { - "audio": 10, - "reasoning": 200, - } + ```python + { + "audio": 10, + "reasoning": 200, + } + ``` !!! version-added "Added in version 0.3.9" ''', @@ -12951,18 +12915,17 @@ of input messages. Example: + ```python + from langchain_core.messages import HumanMessage, SystemMessage - .. code-block:: python + messages = [ + SystemMessage(content="You are a helpful assistant! Your name is Bob."), + HumanMessage(content="What is your name?"), + ] - from langchain_core.messages import HumanMessage, SystemMessage - - messages = [ - SystemMessage(content="You are a helpful assistant! Your name is Bob."), - HumanMessage(content="What is your name?"), - ] - - # Define a chat model and invoke it with the messages - print(model.invoke(messages)) + # Define a chat model and invoke it with the messages + print(model.invoke(messages)) + ``` ''', 'properties': dict({ 'additional_kwargs': dict({ @@ -13104,10 +13067,9 @@ Represents a request to call a tool. Example: - - .. code-block:: python - - {"name": "foo", "args": {"a": 1}, "id": "123"} + ```python + {"name": "foo", "args": {"a": 1}, "id": "123"} + ``` This represents a request to call the tool named `'foo'` with arguments `{"a": 1}` and an identifier of `'123'`. @@ -13154,16 +13116,15 @@ values of `index` are equal and not None. Example: + ```python + left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] + right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - .. code-block:: python - - left_chunks = [ToolCallChunk(name="foo", args='{"a":', index=0)] - right_chunks = [ToolCallChunk(name=None, args="1}", index=0)] - - ( - AIMessageChunk(content="", tool_call_chunks=left_chunks) - + AIMessageChunk(content="", tool_call_chunks=right_chunks) - ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ( + AIMessageChunk(content="", tool_call_chunks=left_chunks) + + AIMessageChunk(content="", tool_call_chunks=right_chunks) + ).tool_call_chunks == [ToolCallChunk(name="foo", args='{"a":1}', index=0)] + ``` ''', 'properties': dict({ 'args': dict({ @@ -13234,34 +13195,33 @@ Example: A `ToolMessage` representing a result of `42` from a tool call with id - .. code-block:: python - - from langchain_core.messages import ToolMessage - - ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ```python + from langchain_core.messages import ToolMessage + ToolMessage(content="42", tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL") + ``` Example: A `ToolMessage` where only part of the tool output is sent to the model and the full output is passed in to artifact. !!! version-added "Added in version 0.2.17" - .. code-block:: python + ```python + from langchain_core.messages import ToolMessage - from langchain_core.messages import ToolMessage + tool_output = { + "stdout": "From the graph we can see that the correlation between " + "x and y is ...", + "stderr": None, + "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, + } - tool_output = { - "stdout": "From the graph we can see that the correlation between " - "x and y is ...", - "stderr": None, - "artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}, - } - - ToolMessage( - content=tool_output["stdout"], - artifact=tool_output, - tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", - ) + ToolMessage( + content=tool_output["stdout"], + artifact=tool_output, + tool_call_id="call_Jja7J89XsjrOLA5r!MEOW!SL", + ) + ``` The `tool_call_id` field is used to associate the tool call request with the tool call response. This is useful in situations where a chat model is able @@ -13433,22 +13393,22 @@ This is a standard representation of token usage that is consistent across models. Example: - .. code-block:: python - - { - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, - } + ```python + { + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, + }, + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + } + ``` !!! warning "Behavior changed in 0.3.9" Added `input_token_details` and `output_token_details`. diff --git a/libs/core/tests/unit_tests/runnables/test_runnable.py b/libs/core/tests/unit_tests/runnables/test_runnable.py index 63cc478f404..c8afa1eea5d 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable.py @@ -314,15 +314,14 @@ def test_schemas(snapshot: SnapshotAssertion) -> None: "associated metadata.\n" "\n" "Example:\n" + " ```python\n" + " from langchain_core.documents import Document\n" "\n" - " .. code-block:: python\n" - "\n" - " from langchain_core.documents import Document\n" - "\n" - " document = Document(\n" - ' page_content="Hello, world!", ' + " document = Document(\n" + ' page_content="Hello, world!", ' 'metadata={"source": "https://example.com"}\n' - " )", + " )\n" + " ```", "properties": { "id": { "anyOf": [{"type": "string"}, {"type": "null"}], diff --git a/libs/langchain/langchain_classic/agents/__init__.py b/libs/langchain/langchain_classic/agents/__init__.py index 9ad93d58604..83e124000d7 100644 --- a/libs/langchain/langchain_classic/agents/__init__.py +++ b/libs/langchain/langchain_classic/agents/__init__.py @@ -5,28 +5,7 @@ a language model is used as a reasoning engine to determine which actions to take and in which order. Agents select and use **Tools** and **Toolkits** for actions. - -**Class hierarchy:** - -.. code-block:: - - BaseSingleActionAgent --> LLMSingleActionAgent - OpenAIFunctionsAgent - XMLAgent - Agent --> Agent # Examples: ZeroShotAgent, ChatAgent - - - BaseMultiActionAgent --> OpenAIMultiFunctionsAgent - - -**Main helpers:** - -.. code-block:: - - AgentType, AgentExecutor, AgentOutputParser, AgentExecutorIterator, - AgentAction, AgentFinish - -""" # noqa: E501 +""" from pathlib import Path from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/agents/agent.py b/libs/langchain/langchain_classic/agents/agent.py index 00b674b5745..4984573fa08 100644 --- a/libs/langchain/langchain_classic/agents/agent.py +++ b/libs/langchain/langchain_classic/agents/agent.py @@ -189,11 +189,10 @@ class BaseSingleActionAgent(BaseModel): file_path: Path to file to save the agent to. Example: - .. code-block:: python - - # If working with agent executor - agent.agent.save(file_path="path/agent.yaml") - + ```python + # If working with agent executor + agent.agent.save(file_path="path/agent.yaml") + ``` """ # Convert file to Path object. save_path = Path(file_path) if isinstance(file_path, str) else file_path @@ -333,11 +332,10 @@ class BaseMultiActionAgent(BaseModel): ValueError: If file_path is not json or yaml. Example: - .. code-block:: python - - # If working with agent executor - agent.agent.save(file_path="path/agent.yaml") - + ```python + # If working with agent executor + agent.agent.save(file_path="path/agent.yaml") + ``` """ # Convert file to Path object. save_path = Path(file_path) if isinstance(file_path, str) else file_path diff --git a/libs/langchain/langchain_classic/agents/agent_toolkits/vectorstore/base.py b/libs/langchain/langchain_classic/agents/agent_toolkits/vectorstore/base.py index 0011fe15f0f..95587613af6 100644 --- a/libs/langchain/langchain_classic/agents/agent_toolkits/vectorstore/base.py +++ b/libs/langchain/langchain_classic/agents/agent_toolkits/vectorstore/base.py @@ -48,41 +48,40 @@ def create_vectorstore_agent( This class is deprecated. See below for a replacement that uses tool calling methods and LangGraph. Install LangGraph with: - .. code-block:: bash + ```bash + pip install -U langgraph + ``` - pip install -U langgraph + ```python + from langchain_core.tools import create_retriever_tool + from langchain_core.vectorstores import InMemoryVectorStore + from langchain_openai import ChatOpenAI, OpenAIEmbeddings + from langgraph.prebuilt import create_react_agent - .. code-block:: python + llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) - from langchain_core.tools import create_retriever_tool - from langchain_core.vectorstores import InMemoryVectorStore - from langchain_openai import ChatOpenAI, OpenAIEmbeddings - from langgraph.prebuilt import create_react_agent + vector_store = InMemoryVectorStore.from_texts( + [ + "Dogs are great companions, known for their loyalty and friendliness.", + "Cats are independent pets that often enjoy their own space.", + ], + OpenAIEmbeddings(), + ) - llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) + tool = create_retriever_tool( + vector_store.as_retriever(), + "pet_information_retriever", + "Fetches information about pets.", + ) - vector_store = InMemoryVectorStore.from_texts( - [ - "Dogs are great companions, " - "known for their loyalty and friendliness.", - "Cats are independent pets that often enjoy their own space.", - ], - OpenAIEmbeddings(), - ) + agent = create_react_agent(llm, [tool]) - tool = create_retriever_tool( - vector_store.as_retriever(), - "pet_information_retriever", - "Fetches information about pets.", - ) - - agent = create_react_agent(llm, [tool]) - - for step in agent.stream( - {"messages": [("human", "What are dogs known for?")]}, - stream_mode="values", - ): - step["messages"][-1].pretty_print() + for step in agent.stream( + {"messages": [("human", "What are dogs known for?")]}, + stream_mode="values", + ): + step["messages"][-1].pretty_print() + ``` Args: llm: LLM that will be used by the agent @@ -147,56 +146,55 @@ def create_vectorstore_router_agent( This class is deprecated. See below for a replacement that uses tool calling methods and LangGraph. Install LangGraph with: - .. code-block:: bash + ```bash + pip install -U langgraph + ``` - pip install -U langgraph + ```python + from langchain_core.tools import create_retriever_tool + from langchain_core.vectorstores import InMemoryVectorStore + from langchain_openai import ChatOpenAI, OpenAIEmbeddings + from langgraph.prebuilt import create_react_agent - .. code-block:: python + llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) - from langchain_core.tools import create_retriever_tool - from langchain_core.vectorstores import InMemoryVectorStore - from langchain_openai import ChatOpenAI, OpenAIEmbeddings - from langgraph.prebuilt import create_react_agent + pet_vector_store = InMemoryVectorStore.from_texts( + [ + "Dogs are great companions, known for their loyalty and friendliness.", + "Cats are independent pets that often enjoy their own space.", + ], + OpenAIEmbeddings(), + ) - llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) + food_vector_store = InMemoryVectorStore.from_texts( + [ + "Carrots are orange and delicious.", + "Apples are red and delicious.", + ], + OpenAIEmbeddings(), + ) - pet_vector_store = InMemoryVectorStore.from_texts( - [ - "Dogs are great companions, " - "known for their loyalty and friendliness.", - "Cats are independent pets that often enjoy their own space.", - ], - OpenAIEmbeddings(), - ) + tools = [ + create_retriever_tool( + pet_vector_store.as_retriever(), + "pet_information_retriever", + "Fetches information about pets.", + ), + create_retriever_tool( + food_vector_store.as_retriever(), + "food_information_retriever", + "Fetches information about food.", + ), + ] - food_vector_store = InMemoryVectorStore.from_texts( - [ - "Carrots are orange and delicious.", - "Apples are red and delicious.", - ], - OpenAIEmbeddings(), - ) + agent = create_react_agent(llm, tools) - tools = [ - create_retriever_tool( - pet_vector_store.as_retriever(), - "pet_information_retriever", - "Fetches information about pets.", - ), - create_retriever_tool( - food_vector_store.as_retriever(), - "food_information_retriever", - "Fetches information about food.", - ), - ] - - agent = create_react_agent(llm, tools) - - for step in agent.stream( - {"messages": [("human", "Tell me about carrots.")]}, - stream_mode="values", - ): - step["messages"][-1].pretty_print() + for step in agent.stream( + {"messages": [("human", "Tell me about carrots.")]}, + stream_mode="values", + ): + step["messages"][-1].pretty_print() + ``` Args: llm: LLM that will be used by the agent diff --git a/libs/langchain/langchain_classic/agents/json_chat/base.py b/libs/langchain/langchain_classic/agents/json_chat/base.py index 3b01e4a4650..2c8091601e8 100644 --- a/libs/langchain/langchain_classic/agents/json_chat/base.py +++ b/libs/langchain/langchain_classic/agents/json_chat/base.py @@ -49,34 +49,33 @@ def create_json_chat_agent( the required variable 'observation'. Example: + ```python + from langchain_classic import hub + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.agents import AgentExecutor, create_json_chat_agent - .. code-block:: python + prompt = hub.pull("hwchase17/react-chat-json") + model = ChatOpenAI() + tools = ... - from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.agents import AgentExecutor, create_json_chat_agent + agent = create_json_chat_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/react-chat-json") - model = ChatOpenAI() - tools = ... + agent_executor.invoke({"input": "hi"}) - agent = create_json_chat_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + # Using with chat history + from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke({"input": "hi"}) - - # Using with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - "chat_history": [ - HumanMessage(content="hi! my name is bob"), - AIMessage(content="Hello Bob! How can I assist you today?"), - ], - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + "chat_history": [ + HumanMessage(content="hi! my name is bob"), + AIMessage(content="Hello Bob! How can I assist you today?"), + ], + } + ) + ``` Prompt: @@ -84,84 +83,84 @@ def create_json_chat_agent( * `tools`: contains descriptions and arguments for each tool. * `tool_names`: contains all tool names. * `agent_scratchpad`: must be a MessagesPlaceholder. Contains previous - agent actions and tool outputs as messages. + agent actions and tool outputs as messages. Here's an example: - .. code-block:: python + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + system = '''Assistant is a large language model trained by OpenAI. - system = '''Assistant is a large language model trained by OpenAI. + Assistant is designed to be able to assist with a wide range of tasks, from answering + simple questions to providing in-depth explanations and discussions on a wide range of + topics. As a language model, Assistant is able to generate human-like text based on + the input it receives, allowing it to engage in natural-sounding conversations and + provide responses that are coherent and relevant to the topic at hand. - Assistant is designed to be able to assist with a wide range of tasks, from answering - simple questions to providing in-depth explanations and discussions on a wide range of - topics. As a language model, Assistant is able to generate human-like text based on - the input it receives, allowing it to engage in natural-sounding conversations and - provide responses that are coherent and relevant to the topic at hand. + Assistant is constantly learning and improving, and its capabilities are constantly + evolving. It is able to process and understand large amounts of text, and can use this + knowledge to provide accurate and informative responses to a wide range of questions. + Additionally, Assistant is able to generate its own text based on the input it + receives, allowing it to engage in discussions and provide explanations and + descriptions on a wide range of topics. - Assistant is constantly learning and improving, and its capabilities are constantly - evolving. It is able to process and understand large amounts of text, and can use this - knowledge to provide accurate and informative responses to a wide range of questions. - Additionally, Assistant is able to generate its own text based on the input it - receives, allowing it to engage in discussions and provide explanations and - descriptions on a wide range of topics. + Overall, Assistant is a powerful system that can help with a wide range of tasks + and provide valuable insights and information on a wide range of topics. Whether + you need help with a specific question or just want to have a conversation about + a particular topic, Assistant is here to assist.''' - Overall, Assistant is a powerful system that can help with a wide range of tasks - and provide valuable insights and information on a wide range of topics. Whether - you need help with a specific question or just want to have a conversation about - a particular topic, Assistant is here to assist.''' + human = '''TOOLS + ------ + Assistant can ask the user to use tools to look up information that may be helpful in + answering the users original question. The tools the human can use are: - human = '''TOOLS - ------ - Assistant can ask the user to use tools to look up information that may be helpful in - answering the users original question. The tools the human can use are: + {tools} - {tools} + RESPONSE FORMAT INSTRUCTIONS + ---------------------------- - RESPONSE FORMAT INSTRUCTIONS - ---------------------------- + When responding to me, please output a response in one of two formats: - When responding to me, please output a response in one of two formats: + **Option 1:** + Use this if you want the human to use a tool. + Markdown code snippet formatted in the following schema: - **Option 1:** - Use this if you want the human to use a tool. - Markdown code snippet formatted in the following schema: + ```json + {{ + "action": string, \\\\ The action to take. Must be one of {tool_names} + "action_input": string \\\\ The input to the action + }} + ``` - ```json - {{ - "action": string, \\\\ The action to take. Must be one of {tool_names} - "action_input": string \\\\ The input to the action - }} - ``` + **Option #2:** + Use this if you want to respond directly to the human. Markdown code snippet formatted + in the following schema: - **Option #2:** - Use this if you want to respond directly to the human. Markdown code snippet formatted - in the following schema: + ```json + {{ + "action": "Final Answer", + "action_input": string \\\\ You should put what you want to return to use here + }} + ``` - ```json - {{ - "action": "Final Answer", - "action_input": string \\\\ You should put what you want to return to use here - }} - ``` + USER'S INPUT + -------------------- + Here is the user's input (remember to respond with a markdown code snippet of a json + blob with a single action, and NOTHING else): - USER'S INPUT - -------------------- - Here is the user's input (remember to respond with a markdown code snippet of a json - blob with a single action, and NOTHING else): + {input}''' - {input}''' - - prompt = ChatPromptTemplate.from_messages( - [ - ("system", system), - MessagesPlaceholder("chat_history", optional=True), - ("human", human), - MessagesPlaceholder("agent_scratchpad"), - ] - ) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", system), + MessagesPlaceholder("chat_history", optional=True), + ("human", human), + MessagesPlaceholder("agent_scratchpad"), + ] + ) + ``` """ # noqa: E501 missing_vars = {"tools", "tool_names", "agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/agents/openai_assistant/base.py b/libs/langchain/langchain_classic/agents/openai_assistant/base.py index 179b764c6e1..ed428e4ed16 100644 --- a/libs/langchain/langchain_classic/agents/openai_assistant/base.py +++ b/libs/langchain/langchain_classic/agents/openai_assistant/base.py @@ -140,96 +140,94 @@ class OpenAIAssistantRunnable(RunnableSerializable[dict, OutputType]): """Run an OpenAI Assistant. Example using OpenAI tools: - .. code-block:: python + ```python + from langchain_experimental.openai_assistant import OpenAIAssistantRunnable - from langchain_experimental.openai_assistant import OpenAIAssistantRunnable - - interpreter_assistant = OpenAIAssistantRunnable.create_assistant( - name="langchain assistant", - instructions="You are a personal math tutor. " - "Write and run code to answer math questions.", - tools=[{"type": "code_interpreter"}], - model="gpt-4-1106-preview", - ) - output = interpreter_assistant.invoke( - {"content": "What's 10 - 4 raised to the 2.7"} - ) + interpreter_assistant = OpenAIAssistantRunnable.create_assistant( + name="langchain assistant", + instructions="You are a personal math tutor. " + "Write and run code to answer math questions.", + tools=[{"type": "code_interpreter"}], + model="gpt-4-1106-preview", + ) + output = interpreter_assistant.invoke( + {"content": "What's 10 - 4 raised to the 2.7"} + ) + ``` Example using custom tools and AgentExecutor: - .. code-block:: python - - from langchain_experimental.openai_assistant import OpenAIAssistantRunnable - from langchain_classic.agents import AgentExecutor - from langchain_classic.tools import E2BDataAnalysisTool + ```python + from langchain_experimental.openai_assistant import OpenAIAssistantRunnable + from langchain_classic.agents import AgentExecutor + from langchain_classic.tools import E2BDataAnalysisTool - tools = [E2BDataAnalysisTool(api_key="...")] - agent = OpenAIAssistantRunnable.create_assistant( - name="langchain assistant e2b tool", - instructions="You are a personal math tutor. " - "Write and run code to answer math questions.", - tools=tools, - model="gpt-4-1106-preview", - as_agent=True, - ) - - agent_executor = AgentExecutor(agent=agent, tools=tools) - agent_executor.invoke({"content": "What's 10 - 4 raised to the 2.7"}) + tools = [E2BDataAnalysisTool(api_key="...")] + agent = OpenAIAssistantRunnable.create_assistant( + name="langchain assistant e2b tool", + instructions="You are a personal math tutor. " + "Write and run code to answer math questions.", + tools=tools, + model="gpt-4-1106-preview", + as_agent=True, + ) + agent_executor = AgentExecutor(agent=agent, tools=tools) + agent_executor.invoke({"content": "What's 10 - 4 raised to the 2.7"}) + ``` Example using custom tools and custom execution: - .. code-block:: python - - from langchain_experimental.openai_assistant import OpenAIAssistantRunnable - from langchain_classic.agents import AgentExecutor - from langchain_core.agents import AgentFinish - from langchain_classic.tools import E2BDataAnalysisTool + ```python + from langchain_experimental.openai_assistant import OpenAIAssistantRunnable + from langchain_classic.agents import AgentExecutor + from langchain_core.agents import AgentFinish + from langchain_classic.tools import E2BDataAnalysisTool - tools = [E2BDataAnalysisTool(api_key="...")] - agent = OpenAIAssistantRunnable.create_assistant( - name="langchain assistant e2b tool", - instructions="You are a personal math tutor. " - "Write and run code to answer math questions.", - tools=tools, - model="gpt-4-1106-preview", - as_agent=True, - ) + tools = [E2BDataAnalysisTool(api_key="...")] + agent = OpenAIAssistantRunnable.create_assistant( + name="langchain assistant e2b tool", + instructions="You are a personal math tutor. " + "Write and run code to answer math questions.", + tools=tools, + model="gpt-4-1106-preview", + as_agent=True, + ) - def execute_agent(agent, tools, input): - tool_map = {tool.name: tool for tool in tools} - response = agent.invoke(input) - while not isinstance(response, AgentFinish): - tool_outputs = [] - for action in response: - tool_output = tool_map[action.tool].invoke(action.tool_input) - tool_outputs.append( - { - "output": tool_output, - "tool_call_id": action.tool_call_id, - } - ) - response = agent.invoke( + def execute_agent(agent, tools, input): + tool_map = {tool.name: tool for tool in tools} + response = agent.invoke(input) + while not isinstance(response, AgentFinish): + tool_outputs = [] + for action in response: + tool_output = tool_map[action.tool].invoke(action.tool_input) + tool_outputs.append( { - "tool_outputs": tool_outputs, - "run_id": action.run_id, - "thread_id": action.thread_id, + "output": tool_output, + "tool_call_id": action.tool_call_id, } ) + response = agent.invoke( + { + "tool_outputs": tool_outputs, + "run_id": action.run_id, + "thread_id": action.thread_id, + } + ) - return response + return response - response = execute_agent( - agent, tools, {"content": "What's 10 - 4 raised to the 2.7"} - ) - next_response = execute_agent( - agent, - tools, - {"content": "now add 17.241", "thread_id": response.thread_id}, - ) - + response = execute_agent( + agent, tools, {"content": "What's 10 - 4 raised to the 2.7"} + ) + next_response = execute_agent( + agent, + tools, + {"content": "now add 17.241", "thread_id": response.thread_id}, + ) + ``` """ client: Any = Field(default_factory=_get_openai_client) diff --git a/libs/langchain/langchain_classic/agents/openai_functions_agent/base.py b/libs/langchain/langchain_classic/agents/openai_functions_agent/base.py index 035a64caee3..c6907b7eeba 100644 --- a/libs/langchain/langchain_classic/agents/openai_functions_agent/base.py +++ b/libs/langchain/langchain_classic/agents/openai_functions_agent/base.py @@ -310,36 +310,36 @@ def create_openai_functions_agent( Example: Creating an agent with no memory - .. code-block:: python + ```python + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.agents import ( + AgentExecutor, + create_openai_functions_agent, + ) + from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.agents import ( - AgentExecutor, - create_openai_functions_agent, - ) - from langchain_classic import hub + prompt = hub.pull("hwchase17/openai-functions-agent") + model = ChatOpenAI() + tools = ... - prompt = hub.pull("hwchase17/openai-functions-agent") - model = ChatOpenAI() - tools = ... + agent = create_openai_functions_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - agent = create_openai_functions_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + agent_executor.invoke({"input": "hi"}) - agent_executor.invoke({"input": "hi"}) + # Using with chat history + from langchain_core.messages import AIMessage, HumanMessage - # Using with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - "chat_history": [ - HumanMessage(content="hi! my name is bob"), - AIMessage(content="Hello Bob! How can I assist you today?"), - ], - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + "chat_history": [ + HumanMessage(content="hi! my name is bob"), + AIMessage(content="Hello Bob! How can I assist you today?"), + ], + } + ) + ``` Prompt: @@ -349,19 +349,18 @@ def create_openai_functions_agent( Here's an example: - .. code-block:: python - - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a helpful assistant"), - MessagesPlaceholder("chat_history", optional=True), - ("human", "{input}"), - MessagesPlaceholder("agent_scratchpad"), - ] - ) + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a helpful assistant"), + MessagesPlaceholder("chat_history", optional=True), + ("human", "{input}"), + MessagesPlaceholder("agent_scratchpad"), + ] + ) + ``` """ if "agent_scratchpad" not in ( prompt.input_variables + list(prompt.partial_variables) diff --git a/libs/langchain/langchain_classic/agents/openai_tools/base.py b/libs/langchain/langchain_classic/agents/openai_tools/base.py index 2d9c5b02475..891dc97d647 100644 --- a/libs/langchain/langchain_classic/agents/openai_tools/base.py +++ b/libs/langchain/langchain_classic/agents/openai_tools/base.py @@ -38,37 +38,36 @@ def create_openai_tools_agent( ValueError: If the prompt is missing required variables. Example: + ```python + from langchain_classic import hub + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.agents import ( + AgentExecutor, + create_openai_tools_agent, + ) - .. code-block:: python + prompt = hub.pull("hwchase17/openai-tools-agent") + model = ChatOpenAI() + tools = ... - from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.agents import ( - AgentExecutor, - create_openai_tools_agent, - ) + agent = create_openai_tools_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/openai-tools-agent") - model = ChatOpenAI() - tools = ... + agent_executor.invoke({"input": "hi"}) - agent = create_openai_tools_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + # Using with chat history + from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke({"input": "hi"}) - - # Using with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - "chat_history": [ - HumanMessage(content="hi! my name is bob"), - AIMessage(content="Hello Bob! How can I assist you today?"), - ], - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + "chat_history": [ + HumanMessage(content="hi! my name is bob"), + AIMessage(content="Hello Bob! How can I assist you today?"), + ], + } + ) + ``` Prompt: @@ -78,19 +77,18 @@ def create_openai_tools_agent( Here's an example: - .. code-block:: python - - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a helpful assistant"), - MessagesPlaceholder("chat_history", optional=True), - ("human", "{input}"), - MessagesPlaceholder("agent_scratchpad"), - ] - ) + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a helpful assistant"), + MessagesPlaceholder("chat_history", optional=True), + ("human", "{input}"), + MessagesPlaceholder("agent_scratchpad"), + ] + ) + ``` """ missing_vars = {"agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/agents/react/agent.py b/libs/langchain/langchain_classic/agents/react/agent.py index 3c60fca78de..ce56d6ed667 100644 --- a/libs/langchain/langchain_classic/agents/react/agent.py +++ b/libs/langchain/langchain_classic/agents/react/agent.py @@ -57,33 +57,32 @@ def create_react_agent( AgentAction or AgentFinish. Examples: + ```python + from langchain_classic import hub + from langchain_community.llms import OpenAI + from langchain_classic.agents import AgentExecutor, create_react_agent - .. code-block:: python + prompt = hub.pull("hwchase17/react") + model = OpenAI() + tools = ... - from langchain_classic import hub - from langchain_community.llms import OpenAI - from langchain_classic.agents import AgentExecutor, create_react_agent + agent = create_react_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/react") - model = OpenAI() - tools = ... + agent_executor.invoke({"input": "hi"}) - agent = create_react_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + # Use with chat history + from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke({"input": "hi"}) - - # Use with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - # Notice that chat_history is a string - # since this prompt is aimed at LLMs, not chat models - "chat_history": "Human: My name is Bob\nAI: Hello Bob!", - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + # Notice that chat_history is a string + # since this prompt is aimed at LLMs, not chat models + "chat_history": "Human: My name is Bob\nAI: Hello Bob!", + } + ) + ``` Prompt: @@ -91,36 +90,35 @@ def create_react_agent( * `tools`: contains descriptions and arguments for each tool. * `tool_names`: contains all tool names. * `agent_scratchpad`: contains previous agent actions and tool outputs as a - string. + string. Here's an example: - .. code-block:: python + ```python + from langchain_core.prompts import PromptTemplate - from langchain_core.prompts import PromptTemplate + template = '''Answer the following questions as best you can. You have access to the following tools: - template = '''Answer the following questions as best you can. You have access to the following tools: + {tools} - {tools} + Use the following format: - Use the following format: + Question: the input question you must answer + Thought: you should always think about what to do + Action: the action to take, should be one of [{tool_names}] + Action Input: the input to the action + Observation: the result of the action + ... (this Thought/Action/Action Input/Observation can repeat N times) + Thought: I now know the final answer + Final Answer: the final answer to the original input question - Question: the input question you must answer - Thought: you should always think about what to do - Action: the action to take, should be one of [{tool_names}] - Action Input: the input to the action - Observation: the result of the action - ... (this Thought/Action/Action Input/Observation can repeat N times) - Thought: I now know the final answer - Final Answer: the final answer to the original input question + Begin! - Begin! - - Question: {input} - Thought:{agent_scratchpad}''' - - prompt = PromptTemplate.from_template(template) + Question: {input} + Thought:{agent_scratchpad}''' + prompt = PromptTemplate.from_template(template) + ``` """ # noqa: E501 missing_vars = {"tools", "tool_names", "agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/agents/self_ask_with_search/base.py b/libs/langchain/langchain_classic/agents/self_ask_with_search/base.py index c0ca6078a6e..caf513a40ba 100644 --- a/libs/langchain/langchain_classic/agents/self_ask_with_search/base.py +++ b/libs/langchain/langchain_classic/agents/self_ask_with_search/base.py @@ -114,24 +114,23 @@ def create_self_ask_with_search_agent( AgentAction or AgentFinish. Examples: + ```python + from langchain_classic import hub + from langchain_community.chat_models import ChatAnthropic + from langchain_classic.agents import ( + AgentExecutor, + create_self_ask_with_search_agent, + ) - .. code-block:: python + prompt = hub.pull("hwchase17/self-ask-with-search") + model = ChatAnthropic(model="claude-3-haiku-20240307") + tools = [...] # Should just be one tool with name `Intermediate Answer` - from langchain_classic import hub - from langchain_community.chat_models import ChatAnthropic - from langchain_classic.agents import ( - AgentExecutor, - create_self_ask_with_search_agent, - ) + agent = create_self_ask_with_search_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/self-ask-with-search") - model = ChatAnthropic(model="claude-3-haiku-20240307") - tools = [...] # Should just be one tool with name `Intermediate Answer` - - agent = create_self_ask_with_search_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) - - agent_executor.invoke({"input": "hi"}) + agent_executor.invoke({"input": "hi"}) + ``` Prompt: @@ -140,51 +139,50 @@ def create_self_ask_with_search_agent( Here's an example: - .. code-block:: python + ```python + from langchain_core.prompts import PromptTemplate - from langchain_core.prompts import PromptTemplate + template = '''Question: Who lived longer, Muhammad Ali or Alan Turing? + Are follow up questions needed here: Yes. + Follow up: How old was Muhammad Ali when he died? + Intermediate answer: Muhammad Ali was 74 years old when he died. + Follow up: How old was Alan Turing when he died? + Intermediate answer: Alan Turing was 41 years old when he died. + So the final answer is: Muhammad Ali - template = '''Question: Who lived longer, Muhammad Ali or Alan Turing? - Are follow up questions needed here: Yes. - Follow up: How old was Muhammad Ali when he died? - Intermediate answer: Muhammad Ali was 74 years old when he died. - Follow up: How old was Alan Turing when he died? - Intermediate answer: Alan Turing was 41 years old when he died. - So the final answer is: Muhammad Ali + Question: When was the founder of craigslist born? + Are follow up questions needed here: Yes. + Follow up: Who was the founder of craigslist? + Intermediate answer: Craigslist was founded by Craig Newmark. + Follow up: When was Craig Newmark born? + Intermediate answer: Craig Newmark was born on December 6, 1952. + So the final answer is: December 6, 1952 - Question: When was the founder of craigslist born? - Are follow up questions needed here: Yes. - Follow up: Who was the founder of craigslist? - Intermediate answer: Craigslist was founded by Craig Newmark. - Follow up: When was Craig Newmark born? - Intermediate answer: Craig Newmark was born on December 6, 1952. - So the final answer is: December 6, 1952 + Question: Who was the maternal grandfather of George Washington? + Are follow up questions needed here: Yes. + Follow up: Who was the mother of George Washington? + Intermediate answer: The mother of George Washington was Mary Ball Washington. + Follow up: Who was the father of Mary Ball Washington? + Intermediate answer: The father of Mary Ball Washington was Joseph Ball. + So the final answer is: Joseph Ball - Question: Who was the maternal grandfather of George Washington? - Are follow up questions needed here: Yes. - Follow up: Who was the mother of George Washington? - Intermediate answer: The mother of George Washington was Mary Ball Washington. - Follow up: Who was the father of Mary Ball Washington? - Intermediate answer: The father of Mary Ball Washington was Joseph Ball. - So the final answer is: Joseph Ball + Question: Are both the directors of Jaws and Casino Royale from the same country? + Are follow up questions needed here: Yes. + Follow up: Who is the director of Jaws? + Intermediate answer: The director of Jaws is Steven Spielberg. + Follow up: Where is Steven Spielberg from? + Intermediate answer: The United States. + Follow up: Who is the director of Casino Royale? + Intermediate answer: The director of Casino Royale is Martin Campbell. + Follow up: Where is Martin Campbell from? + Intermediate answer: New Zealand. + So the final answer is: No - Question: Are both the directors of Jaws and Casino Royale from the same country? - Are follow up questions needed here: Yes. - Follow up: Who is the director of Jaws? - Intermediate answer: The director of Jaws is Steven Spielberg. - Follow up: Where is Steven Spielberg from? - Intermediate answer: The United States. - Follow up: Who is the director of Casino Royale? - Intermediate answer: The director of Casino Royale is Martin Campbell. - Follow up: Where is Martin Campbell from? - Intermediate answer: New Zealand. - So the final answer is: No - - Question: {input} - Are followup questions needed here:{agent_scratchpad}''' - - prompt = PromptTemplate.from_template(template) + Question: {input} + Are followup questions needed here:{agent_scratchpad}''' + prompt = PromptTemplate.from_template(template) + ``` """ # noqa: E501 missing_vars = {"agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/agents/structured_chat/base.py b/libs/langchain/langchain_classic/agents/structured_chat/base.py index f39116689cf..2a2d9f25aeb 100644 --- a/libs/langchain/langchain_classic/agents/structured_chat/base.py +++ b/libs/langchain/langchain_classic/agents/structured_chat/base.py @@ -193,37 +193,36 @@ def create_structured_chat_agent( AgentAction or AgentFinish. Examples: + ```python + from langchain_classic import hub + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.agents import ( + AgentExecutor, + create_structured_chat_agent, + ) - .. code-block:: python + prompt = hub.pull("hwchase17/structured-chat-agent") + model = ChatOpenAI() + tools = ... - from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.agents import ( - AgentExecutor, - create_structured_chat_agent, - ) + agent = create_structured_chat_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/structured-chat-agent") - model = ChatOpenAI() - tools = ... + agent_executor.invoke({"input": "hi"}) - agent = create_structured_chat_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + # Using with chat history + from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke({"input": "hi"}) - - # Using with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - "chat_history": [ - HumanMessage(content="hi! my name is bob"), - AIMessage(content="Hello Bob! How can I assist you today?"), - ], - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + "chat_history": [ + HumanMessage(content="hi! my name is bob"), + AIMessage(content="Hello Bob! How can I assist you today?"), + ], + } + ) + ``` Prompt: @@ -231,65 +230,65 @@ def create_structured_chat_agent( * `tools`: contains descriptions and arguments for each tool. * `tool_names`: contains all tool names. * `agent_scratchpad`: contains previous agent actions and tool outputs as a - string. + string. Here's an example: - .. code-block:: python + ```python + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + system = '''Respond to the human as helpfully and accurately as possible. You have access to the following tools: - system = '''Respond to the human as helpfully and accurately as possible. You have access to the following tools: + {tools} - {tools} + Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input). - Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input). + Valid "action" values: "Final Answer" or {tool_names} - Valid "action" values: "Final Answer" or {tool_names} + Provide only ONE action per $JSON_BLOB, as shown: - Provide only ONE action per $JSON_BLOB, as shown: + ```txt + {{ + "action": $TOOL_NAME, + "action_input": $INPUT + }} + ``` - ``` - {{ - "action": $TOOL_NAME, - "action_input": $INPUT - }} - ``` + Follow this format: - Follow this format: + Question: input question to answer + Thought: consider previous and subsequent steps + Action: + ``` + $JSON_BLOB + ``` + Observation: action result + ... (repeat Thought/Action/Observation N times) + Thought: I know what to respond + Action: + ```txt + {{ + "action": "Final Answer", + "action_input": "Final response to human" + }} - Question: input question to answer - Thought: consider previous and subsequent steps - Action: - ``` - $JSON_BLOB - ``` - Observation: action result - ... (repeat Thought/Action/Observation N times) - Thought: I know what to respond - Action: - ``` - {{ - "action": "Final Answer", - "action_input": "Final response to human" - }} + Begin! Reminder to ALWAYS respond with a valid json blob of a single action. Use tools if necessary. Respond directly if appropriate. Format is Action:```$JSON_BLOB```then Observation''' - Begin! Reminder to ALWAYS respond with a valid json blob of a single action. Use tools if necessary. Respond directly if appropriate. Format is Action:```$JSON_BLOB```then Observation''' + human = '''{input} - human = '''{input} + {agent_scratchpad} - {agent_scratchpad} + (reminder to respond in a JSON blob no matter what)''' - (reminder to respond in a JSON blob no matter what)''' - - prompt = ChatPromptTemplate.from_messages( - [ - ("system", system), - MessagesPlaceholder("chat_history", optional=True), - ("human", human), - ] - ) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", system), + MessagesPlaceholder("chat_history", optional=True), + ("human", human), + ] + ) + ``` """ # noqa: E501 missing_vars = {"tools", "tool_names", "agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/agents/tool_calling_agent/base.py b/libs/langchain/langchain_classic/agents/tool_calling_agent/base.py index 8abfc475f5d..1c8b23ae669 100644 --- a/libs/langchain/langchain_classic/agents/tool_calling_agent/base.py +++ b/libs/langchain/langchain_classic/agents/tool_calling_agent/base.py @@ -38,50 +38,49 @@ def create_tool_calling_agent( AgentAction or AgentFinish. Example: + ```python + from langchain_classic.agents import ( + AgentExecutor, + create_tool_calling_agent, + tool, + ) + from langchain_anthropic import ChatAnthropic + from langchain_core.prompts import ChatPromptTemplate - .. code-block:: python + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a helpful assistant"), + ("placeholder", "{chat_history}"), + ("human", "{input}"), + ("placeholder", "{agent_scratchpad}"), + ] + ) + model = ChatAnthropic(model="claude-3-opus-20240229") - from langchain_classic.agents import ( - AgentExecutor, - create_tool_calling_agent, - tool, - ) - from langchain_anthropic import ChatAnthropic - from langchain_core.prompts import ChatPromptTemplate + @tool + def magic_function(input: int) -> int: + \"\"\"Applies a magic function to an input.\"\"\" + return input + 2 - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a helpful assistant"), - ("placeholder", "{chat_history}"), - ("human", "{input}"), - ("placeholder", "{agent_scratchpad}"), - ] - ) - model = ChatAnthropic(model="claude-3-opus-20240229") + tools = [magic_function] - @tool - def magic_function(input: int) -> int: - \"\"\"Applies a magic function to an input.\"\"\" - return input + 2 + agent = create_tool_calling_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) - tools = [magic_function] + agent_executor.invoke({"input": "what is the value of magic_function(3)?"}) - agent = create_tool_calling_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) - - agent_executor.invoke({"input": "what is the value of magic_function(3)?"}) - - # Using with chat history - from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke( - { - "input": "what's my name?", - "chat_history": [ - HumanMessage(content="hi! my name is bob"), - AIMessage(content="Hello Bob! How can I assist you today?"), - ], - } - ) + # Using with chat history + from langchain_core.messages import AIMessage, HumanMessage + agent_executor.invoke( + { + "input": "what's my name?", + "chat_history": [ + HumanMessage(content="hi! my name is bob"), + AIMessage(content="Hello Bob! How can I assist you today?"), + ], + } + ) + ``` Prompt: diff --git a/libs/langchain/langchain_classic/agents/xml/base.py b/libs/langchain/langchain_classic/agents/xml/base.py index fcc3d83e4e9..d06ff88c1ed 100644 --- a/libs/langchain/langchain_classic/agents/xml/base.py +++ b/libs/langchain/langchain_classic/agents/xml/base.py @@ -28,15 +28,14 @@ class XMLAgent(BaseSingleActionAgent): llm_chain: The LLMChain to call to predict the next action Examples: + ```python + from langchain_classic.agents import XMLAgent + from langchain - .. code-block:: python - - from langchain_classic.agents import XMLAgent - from langchain - - tools = ... - model = + tools = ... + model = + ``` """ tools: list[BaseTool] @@ -145,33 +144,32 @@ def create_xml_agent( AgentAction or AgentFinish. Example: + ```python + from langchain_classic import hub + from langchain_community.chat_models import ChatAnthropic + from langchain_classic.agents import AgentExecutor, create_xml_agent - .. code-block:: python + prompt = hub.pull("hwchase17/xml-agent-convo") + model = ChatAnthropic(model="claude-3-haiku-20240307") + tools = ... - from langchain_classic import hub - from langchain_community.chat_models import ChatAnthropic - from langchain_classic.agents import AgentExecutor, create_xml_agent + agent = create_xml_agent(model, tools, prompt) + agent_executor = AgentExecutor(agent=agent, tools=tools) - prompt = hub.pull("hwchase17/xml-agent-convo") - model = ChatAnthropic(model="claude-3-haiku-20240307") - tools = ... + agent_executor.invoke({"input": "hi"}) - agent = create_xml_agent(model, tools, prompt) - agent_executor = AgentExecutor(agent=agent, tools=tools) + # Use with chat history + from langchain_core.messages import AIMessage, HumanMessage - agent_executor.invoke({"input": "hi"}) - - # Use with chat history - from langchain_core.messages import AIMessage, HumanMessage - - agent_executor.invoke( - { - "input": "what's my name?", - # Notice that chat_history is a string - # since this prompt is aimed at LLMs, not chat models - "chat_history": "Human: My name is Bob\nAI: Hello Bob!", - } - ) + agent_executor.invoke( + { + "input": "what's my name?", + # Notice that chat_history is a string + # since this prompt is aimed at LLMs, not chat models + "chat_history": "Human: My name is Bob\nAI: Hello Bob!", + } + ) + ``` Prompt: @@ -182,35 +180,34 @@ def create_xml_agent( Here's an example: - .. code-block:: python + ```python + from langchain_core.prompts import PromptTemplate - from langchain_core.prompts import PromptTemplate + template = '''You are a helpful assistant. Help the user answer any questions. - template = '''You are a helpful assistant. Help the user answer any questions. + You have access to the following tools: - You have access to the following tools: + {tools} - {tools} + In order to use a tool, you can use and tags. You will then get back a response in the form + For example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond: - In order to use a tool, you can use and tags. You will then get back a response in the form - For example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond: + searchweather in SF + 64 degrees - searchweather in SF - 64 degrees + When you are done, respond with a final answer between . For example: - When you are done, respond with a final answer between . For example: + The weather in SF is 64 degrees - The weather in SF is 64 degrees + Begin! - Begin! - - Previous Conversation: - {chat_history} - - Question: {input} - {agent_scratchpad}''' - prompt = PromptTemplate.from_template(template) + Previous Conversation: + {chat_history} + Question: {input} + {agent_scratchpad}''' + prompt = PromptTemplate.from_template(template) + ``` """ # noqa: E501 missing_vars = {"tools", "agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables), diff --git a/libs/langchain/langchain_classic/callbacks/__init__.py b/libs/langchain/langchain_classic/callbacks/__init__.py index 49c304d2958..3ce6f211733 100644 --- a/libs/langchain/langchain_classic/callbacks/__init__.py +++ b/libs/langchain/langchain_classic/callbacks/__init__.py @@ -1,11 +1,4 @@ -"""**Callback handlers** allow listening to events in LangChain. - -**Class hierarchy:** - -.. code-block:: - - BaseCallbackHandler --> CallbackHandler # Example: AimCallbackHandler -""" +"""**Callback handlers** allow listening to events in LangChain.""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/chains/__init__.py b/libs/langchain/langchain_classic/chains/__init__.py index 8b460c86078..8c9cf40439d 100644 --- a/libs/langchain/langchain_classic/chains/__init__.py +++ b/libs/langchain/langchain_classic/chains/__init__.py @@ -7,14 +7,8 @@ The Chain interface makes it easy to create apps that are: - **Stateful:** add Memory to any Chain to give it state, - **Observable:** pass Callbacks to a Chain to execute additional functionality, - like logging, outside the main sequence of component calls, + like logging, outside the main sequence of component calls, - **Composable:** combine Chains with other components, including other Chains. - -**Class hierarchy:** - -.. code-block:: - - Chain --> Chain # Examples: LLMChain, MapReduceChain, RouterChain """ from typing import Any diff --git a/libs/langchain/langchain_classic/chains/api/base.py b/libs/langchain/langchain_classic/chains/api/base.py index 41f186d513e..955e52fd5ee 100644 --- a/libs/langchain/langchain_classic/chains/api/base.py +++ b/libs/langchain/langchain_classic/chains/api/base.py @@ -25,10 +25,10 @@ def _extract_scheme_and_domain(url: str) -> tuple[str, str]: """Extract the scheme + domain from a given URL. Args: - url (str): The input URL. + url: The input URL. Returns: - return a 2-tuple of scheme and domain + A 2-tuple of scheme and domain """ parsed_uri = urlparse(url) return parsed_uri.scheme, parsed_uri.netloc @@ -38,11 +38,11 @@ def _check_in_allowed_domain(url: str, limit_to_domains: Sequence[str]) -> bool: """Check if a URL is in the allowed domains. Args: - url (str): The input URL. - limit_to_domains (Sequence[str]): The allowed domains. + url: The input URL. + limit_to_domains: The allowed domains. Returns: - bool: True if the URL is in the allowed domains, False otherwise. + True if the URL is in the allowed domains, False otherwise. """ scheme, domain = _extract_scheme_and_domain(url) @@ -94,107 +94,106 @@ try: Install LangGraph with: - .. code-block:: bash + ```bash + pip install -U langgraph + ``` - pip install -U langgraph + ```python + from typing import Annotated, Sequence + from typing_extensions import TypedDict - .. code-block:: python + from langchain_classic.chains.api.prompt import API_URL_PROMPT + from langchain_community.agent_toolkits.openapi.toolkit import RequestsToolkit + from langchain_community.utilities.requests import TextRequestsWrapper + from langchain_core.messages import BaseMessage + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + from langchain_core.runnables import RunnableConfig + from langgraph.graph import END, StateGraph + from langgraph.graph.message import add_messages + from langgraph.prebuilt.tool_node import ToolNode - from typing import Annotated, Sequence - from typing_extensions import TypedDict + # NOTE: There are inherent risks in giving models discretion + # to execute real-world actions. We must "opt-in" to these + # risks by setting allow_dangerous_request=True to use these tools. + # This can be dangerous for calling unwanted requests. Please make + # sure your custom OpenAPI spec (yaml) is safe and that permissions + # associated with the tools are narrowly-scoped. + ALLOW_DANGEROUS_REQUESTS = True - from langchain_classic.chains.api.prompt import API_URL_PROMPT - from langchain_community.agent_toolkits.openapi.toolkit import RequestsToolkit - from langchain_community.utilities.requests import TextRequestsWrapper - from langchain_core.messages import BaseMessage - from langchain_core.prompts import ChatPromptTemplate - from langchain_openai import ChatOpenAI - from langchain_core.runnables import RunnableConfig - from langgraph.graph import END, StateGraph - from langgraph.graph.message import add_messages - from langgraph.prebuilt.tool_node import ToolNode + # Subset of spec for https://jsonplaceholder.typicode.com + api_spec = \"\"\" + openapi: 3.0.0 + info: + title: JSONPlaceholder API + version: 1.0.0 + servers: + - url: https://jsonplaceholder.typicode.com + paths: + /posts: + get: + summary: Get posts + parameters: &id001 + - name: _limit + in: query + required: false + schema: + type: integer + example: 2 + description: Limit the number of results + \"\"\" - # NOTE: There are inherent risks in giving models discretion - # to execute real-world actions. We must "opt-in" to these - # risks by setting allow_dangerous_request=True to use these tools. - # This can be dangerous for calling unwanted requests. Please make - # sure your custom OpenAPI spec (yaml) is safe and that permissions - # associated with the tools are narrowly-scoped. - ALLOW_DANGEROUS_REQUESTS = True + llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) + toolkit = RequestsToolkit( + requests_wrapper=TextRequestsWrapper(headers={}), # no auth required + allow_dangerous_requests=ALLOW_DANGEROUS_REQUESTS, + ) + tools = toolkit.get_tools() - # Subset of spec for https://jsonplaceholder.typicode.com - api_spec = \"\"\" - openapi: 3.0.0 - info: - title: JSONPlaceholder API - version: 1.0.0 - servers: - - url: https://jsonplaceholder.typicode.com - paths: - /posts: - get: - summary: Get posts - parameters: &id001 - - name: _limit - in: query - required: false - schema: - type: integer - example: 2 - description: Limit the number of results - \"\"\" + api_request_chain = ( + API_URL_PROMPT.partial(api_docs=api_spec) + | llm.bind_tools(tools, tool_choice="any") + ) - llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) - toolkit = RequestsToolkit( - requests_wrapper=TextRequestsWrapper(headers={}), # no auth required - allow_dangerous_requests=ALLOW_DANGEROUS_REQUESTS, + class ChainState(TypedDict): + \"\"\"LangGraph state.\"\"\" + + messages: Annotated[Sequence[BaseMessage], add_messages] + + + async def acall_request_chain(state: ChainState, config: RunnableConfig): + last_message = state["messages"][-1] + response = await api_request_chain.ainvoke( + {"question": last_message.content}, config ) - tools = toolkit.get_tools() + return {"messages": [response]} - api_request_chain = ( - API_URL_PROMPT.partial(api_docs=api_spec) - | llm.bind_tools(tools, tool_choice="any") - ) + async def acall_model(state: ChainState, config: RunnableConfig): + response = await llm.ainvoke(state["messages"], config) + return {"messages": [response]} - class ChainState(TypedDict): - \"\"\"LangGraph state.\"\"\" + graph_builder = StateGraph(ChainState) + graph_builder.add_node("call_tool", acall_request_chain) + graph_builder.add_node("execute_tool", ToolNode(tools)) + graph_builder.add_node("call_model", acall_model) + graph_builder.set_entry_point("call_tool") + graph_builder.add_edge("call_tool", "execute_tool") + graph_builder.add_edge("execute_tool", "call_model") + graph_builder.add_edge("call_model", END) + chain = graph_builder.compile() + ``` - messages: Annotated[Sequence[BaseMessage], add_messages] + ```python + example_query = "Fetch the top two posts. What are their titles?" - - async def acall_request_chain(state: ChainState, config: RunnableConfig): - last_message = state["messages"][-1] - response = await api_request_chain.ainvoke( - {"question": last_message.content}, config - ) - return {"messages": [response]} - - async def acall_model(state: ChainState, config: RunnableConfig): - response = await llm.ainvoke(state["messages"], config) - return {"messages": [response]} - - graph_builder = StateGraph(ChainState) - graph_builder.add_node("call_tool", acall_request_chain) - graph_builder.add_node("execute_tool", ToolNode(tools)) - graph_builder.add_node("call_model", acall_model) - graph_builder.set_entry_point("call_tool") - graph_builder.add_edge("call_tool", "execute_tool") - graph_builder.add_edge("execute_tool", "call_model") - graph_builder.add_edge("call_model", END) - chain = graph_builder.compile() - - .. code-block:: python - - example_query = "Fetch the top two posts. What are their titles?" - - events = chain.astream( - {"messages": [("user", example_query)]}, - stream_mode="values", - ) - async for event in events: - event["messages"][-1].pretty_print() - - """ # noqa: E501 + events = chain.astream( + {"messages": [("user", example_query)]}, + stream_mode="values", + ) + async for event in events: + event["messages"][-1].pretty_print() + ``` + """ api_request_chain: LLMChain api_answer_chain: LLMChain @@ -209,11 +208,11 @@ try: `limit_to_domains=["https://www.example.com"]`. * The default value is an empty tuple, which means that no domains are - allowed by default. By design this will raise an error on instantiation. + allowed by default. By design this will raise an error on instantiation. * Use a None if you want to allow all domains by default -- this is not - recommended for security reasons, as it would allow malicious users to - make requests to arbitrary URLS including internal APIs accessible from - the server. + recommended for security reasons, as it would allow malicious users to + make requests to arbitrary URLS including internal APIs accessible from + the server. """ @property diff --git a/libs/langchain/langchain_classic/chains/base.py b/libs/langchain/langchain_classic/chains/base.py index a40e482bebc..2d1c0b1e028 100644 --- a/libs/langchain/langchain_classic/chains/base.py +++ b/libs/langchain/langchain_classic/chains/base.py @@ -609,19 +609,18 @@ class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC): The chain output. Example: - .. code-block:: python - - # Suppose we have a single-input chain that takes a 'question' string: - chain.run("What's the temperature in Boise, Idaho?") - # -> "The temperature in Boise is..." - - # Suppose we have a multi-input chain that takes a 'question' string - # and 'context' string: - question = "What's the temperature in Boise, Idaho?" - context = "Weather report for Boise, Idaho on 07/03/23..." - chain.run(question=question, context=context) - # -> "The temperature in Boise is..." + ```python + # Suppose we have a single-input chain that takes a 'question' string: + chain.run("What's the temperature in Boise, Idaho?") + # -> "The temperature in Boise is..." + # Suppose we have a multi-input chain that takes a 'question' string + # and 'context' string: + question = "What's the temperature in Boise, Idaho?" + context = "Weather report for Boise, Idaho on 07/03/23..." + chain.run(question=question, context=context) + # -> "The temperature in Boise is..." + ``` """ # Run at start to make sure this is possible/defined _output_key = self._run_output_key @@ -685,19 +684,18 @@ class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC): The chain output. Example: - .. code-block:: python - - # Suppose we have a single-input chain that takes a 'question' string: - await chain.arun("What's the temperature in Boise, Idaho?") - # -> "The temperature in Boise is..." - - # Suppose we have a multi-input chain that takes a 'question' string - # and 'context' string: - question = "What's the temperature in Boise, Idaho?" - context = "Weather report for Boise, Idaho on 07/03/23..." - await chain.arun(question=question, context=context) - # -> "The temperature in Boise is..." + ```python + # Suppose we have a single-input chain that takes a 'question' string: + await chain.arun("What's the temperature in Boise, Idaho?") + # -> "The temperature in Boise is..." + # Suppose we have a multi-input chain that takes a 'question' string + # and 'context' string: + question = "What's the temperature in Boise, Idaho?" + context = "Weather report for Boise, Idaho on 07/03/23..." + await chain.arun(question=question, context=context) + # -> "The temperature in Boise is..." + ``` """ if len(self.output_keys) != 1: msg = ( @@ -748,11 +746,10 @@ class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC): A dictionary representation of the chain. Example: - .. code-block:: python - - chain.model_dump(exclude_unset=True) - # -> {"_type": "foo", "verbose": False, ...} - + ```python + chain.model_dump(exclude_unset=True) + # -> {"_type": "foo", "verbose": False, ...} + ``` """ _dict = super().model_dump(**kwargs) with contextlib.suppress(NotImplementedError): @@ -769,10 +766,9 @@ class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC): file_path: Path to file to save the chain to. Example: - .. code-block:: python - - chain.save(file_path="path/chain.yaml") - + ```python + chain.save(file_path="path/chain.yaml") + ``` """ if self.memory is not None: msg = "Saving of memory is not yet supported." diff --git a/libs/langchain/langchain_classic/chains/combine_documents/base.py b/libs/langchain/langchain_classic/chains/combine_documents/base.py index 51f50ec87d0..ee672ea1001 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/base.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/base.py @@ -192,47 +192,46 @@ class AnalyzeDocumentChain(Chain): If the underlying combine documents chain takes one `input_documents` argument (e.g., chains generated by `load_summarize_chain`): - .. code-block:: python + ```python + split_text = lambda x: text_splitter.create_documents([x]) - split_text = lambda x: text_splitter.create_documents([x]) - - summarize_document_chain = split_text | chain + summarize_document_chain = split_text | chain + ``` If the underlying chain takes additional arguments (e.g., `load_qa_chain`, which takes an additional `question` argument), we can use the following: - .. code-block:: python + ```python + from operator import itemgetter + from langchain_core.runnables import RunnableLambda, RunnableParallel - from operator import itemgetter - from langchain_core.runnables import RunnableLambda, RunnableParallel - - split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) - summarize_document_chain = RunnableParallel( - question=itemgetter("question"), - input_documents=itemgetter("input_document") | split_text, - ) | chain.pick("output_text") + split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) + summarize_document_chain = RunnableParallel( + question=itemgetter("question"), + input_documents=itemgetter("input_document") | split_text, + ) | chain.pick("output_text") + ``` To additionally return the input parameters, as `AnalyzeDocumentChain` does, we can wrap this construction with `RunnablePassthrough`: - .. code-block:: python + ```python + from operator import itemgetter + from langchain_core.runnables import ( + RunnableLambda, + RunnableParallel, + RunnablePassthrough, + ) - from operator import itemgetter - from langchain_core.runnables import ( - RunnableLambda, - RunnableParallel, - RunnablePassthrough, + split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) + summarize_document_chain = RunnablePassthrough.assign( + output_text=RunnableParallel( + question=itemgetter("question"), + input_documents=itemgetter("input_document") | split_text, ) - - split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) - summarize_document_chain = RunnablePassthrough.assign( - output_text=RunnableParallel( - question=itemgetter("question"), - input_documents=itemgetter("input_document") | split_text, - ) - | chain.pick("output_text") - ) - + | chain.pick("output_text") + ) + ``` """ input_key: str = "input_document" #: :meta private: diff --git a/libs/langchain/langchain_classic/chains/combine_documents/map_reduce.py b/libs/langchain/langchain_classic/chains/combine_documents/map_reduce.py index f2a6ae14b44..99fdf7bc1be 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/map_reduce.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/map_reduce.py @@ -36,65 +36,64 @@ class MapReduceDocumentsChain(BaseCombineDocumentsChain): likely be a ReduceDocumentsChain. Example: - .. code-block:: python - - from langchain_classic.chains import ( - StuffDocumentsChain, - LLMChain, - ReduceDocumentsChain, - MapReduceDocumentsChain, - ) - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - - # This controls how each document will be formatted. Specifically, - # it will be passed to `format_document` - see that function for more - # details. - document_prompt = PromptTemplate( - input_variables=["page_content"], template="{page_content}" - ) - document_variable_name = "context" - llm = OpenAI() - # The prompt here should take as an input variable the - # `document_variable_name` - prompt = PromptTemplate.from_template("Summarize this content: {context}") - llm_chain = LLMChain(llm=llm, prompt=prompt) - # We now define how to combine these summaries - reduce_prompt = PromptTemplate.from_template( - "Combine these summaries: {context}" - ) - reduce_llm_chain = LLMChain(llm=llm, prompt=reduce_prompt) - combine_documents_chain = StuffDocumentsChain( - llm_chain=reduce_llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - ) - reduce_documents_chain = ReduceDocumentsChain( - combine_documents_chain=combine_documents_chain, - ) - chain = MapReduceDocumentsChain( - llm_chain=llm_chain, - reduce_documents_chain=reduce_documents_chain, - ) - # If we wanted to, we could also pass in collapse_documents_chain - # which is specifically aimed at collapsing documents BEFORE - # the final call. - prompt = PromptTemplate.from_template("Collapse this content: {context}") - llm_chain = LLMChain(llm=llm, prompt=prompt) - collapse_documents_chain = StuffDocumentsChain( - llm_chain=llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - ) - reduce_documents_chain = ReduceDocumentsChain( - combine_documents_chain=combine_documents_chain, - collapse_documents_chain=collapse_documents_chain, - ) - chain = MapReduceDocumentsChain( - llm_chain=llm_chain, - reduce_documents_chain=reduce_documents_chain, - ) + ```python + from langchain_classic.chains import ( + StuffDocumentsChain, + LLMChain, + ReduceDocumentsChain, + MapReduceDocumentsChain, + ) + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI + # This controls how each document will be formatted. Specifically, + # it will be passed to `format_document` - see that function for more + # details. + document_prompt = PromptTemplate( + input_variables=["page_content"], template="{page_content}" + ) + document_variable_name = "context" + llm = OpenAI() + # The prompt here should take as an input variable the + # `document_variable_name` + prompt = PromptTemplate.from_template("Summarize this content: {context}") + llm_chain = LLMChain(llm=llm, prompt=prompt) + # We now define how to combine these summaries + reduce_prompt = PromptTemplate.from_template( + "Combine these summaries: {context}" + ) + reduce_llm_chain = LLMChain(llm=llm, prompt=reduce_prompt) + combine_documents_chain = StuffDocumentsChain( + llm_chain=reduce_llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + ) + reduce_documents_chain = ReduceDocumentsChain( + combine_documents_chain=combine_documents_chain, + ) + chain = MapReduceDocumentsChain( + llm_chain=llm_chain, + reduce_documents_chain=reduce_documents_chain, + ) + # If we wanted to, we could also pass in collapse_documents_chain + # which is specifically aimed at collapsing documents BEFORE + # the final call. + prompt = PromptTemplate.from_template("Collapse this content: {context}") + llm_chain = LLMChain(llm=llm, prompt=prompt) + collapse_documents_chain = StuffDocumentsChain( + llm_chain=llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + ) + reduce_documents_chain = ReduceDocumentsChain( + combine_documents_chain=combine_documents_chain, + collapse_documents_chain=collapse_documents_chain, + ) + chain = MapReduceDocumentsChain( + llm_chain=llm_chain, + reduce_documents_chain=reduce_documents_chain, + ) + ``` """ llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/combine_documents/map_rerank.py b/libs/langchain/langchain_classic/chains/combine_documents/map_rerank.py index fb6993ac544..a712ab6f06b 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/map_rerank.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/map_rerank.py @@ -35,41 +35,40 @@ class MapRerankDocumentsChain(BaseCombineDocumentsChain): and a score (`rank_key`). The answer with the highest score is then returned. Example: - .. code-block:: python - - from langchain_classic.chains import MapRerankDocumentsChain, LLMChain - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - from langchain_classic.output_parsers.regex import RegexParser - - document_variable_name = "context" - llm = OpenAI() - # The prompt here should take as an input variable the - # `document_variable_name` - # The actual prompt will need to be a lot more complex, this is just - # an example. - prompt_template = ( - "Use the following context to tell me the chemical formula " - "for water. Output both your answer and a score of how confident " - "you are. Context: {context}" - ) - output_parser = RegexParser( - regex=r"(.*?)\nScore: (.*)", - output_keys=["answer", "score"], - ) - prompt = PromptTemplate( - template=prompt_template, - input_variables=["context"], - output_parser=output_parser, - ) - llm_chain = LLMChain(llm=llm, prompt=prompt) - chain = MapRerankDocumentsChain( - llm_chain=llm_chain, - document_variable_name=document_variable_name, - rank_key="score", - answer_key="answer", - ) + ```python + from langchain_classic.chains import MapRerankDocumentsChain, LLMChain + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI + from langchain_classic.output_parsers.regex import RegexParser + document_variable_name = "context" + llm = OpenAI() + # The prompt here should take as an input variable the + # `document_variable_name` + # The actual prompt will need to be a lot more complex, this is just + # an example. + prompt_template = ( + "Use the following context to tell me the chemical formula " + "for water. Output both your answer and a score of how confident " + "you are. Context: {context}" + ) + output_parser = RegexParser( + regex=r"(.*?)\nScore: (.*)", + output_keys=["answer", "score"], + ) + prompt = PromptTemplate( + template=prompt_template, + input_variables=["context"], + output_parser=output_parser, + ) + llm_chain = LLMChain(llm=llm, prompt=prompt) + chain = MapRerankDocumentsChain( + llm_chain=llm_chain, + document_variable_name=document_variable_name, + rank_key="score", + answer_key="answer", + ) + ``` """ llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/combine_documents/reduce.py b/libs/langchain/langchain_classic/chains/combine_documents/reduce.py index ba977eb5042..0f86c389ff0 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/reduce.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/reduce.py @@ -155,51 +155,50 @@ class ReduceDocumentsChain(BaseCombineDocumentsChain): as are allowed. Example: - .. code-block:: python - - from langchain_classic.chains import ( - StuffDocumentsChain, - LLMChain, - ReduceDocumentsChain, - ) - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - - # This controls how each document will be formatted. Specifically, - # it will be passed to `format_document` - see that function for more - # details. - document_prompt = PromptTemplate( - input_variables=["page_content"], template="{page_content}" - ) - document_variable_name = "context" - llm = OpenAI() - # The prompt here should take as an input variable the - # `document_variable_name` - prompt = PromptTemplate.from_template("Summarize this content: {context}") - llm_chain = LLMChain(llm=llm, prompt=prompt) - combine_documents_chain = StuffDocumentsChain( - llm_chain=llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - ) - chain = ReduceDocumentsChain( - combine_documents_chain=combine_documents_chain, - ) - # If we wanted to, we could also pass in collapse_documents_chain - # which is specifically aimed at collapsing documents BEFORE - # the final call. - prompt = PromptTemplate.from_template("Collapse this content: {context}") - llm_chain = LLMChain(llm=llm, prompt=prompt) - collapse_documents_chain = StuffDocumentsChain( - llm_chain=llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - ) - chain = ReduceDocumentsChain( - combine_documents_chain=combine_documents_chain, - collapse_documents_chain=collapse_documents_chain, - ) + ```python + from langchain_classic.chains import ( + StuffDocumentsChain, + LLMChain, + ReduceDocumentsChain, + ) + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI + # This controls how each document will be formatted. Specifically, + # it will be passed to `format_document` - see that function for more + # details. + document_prompt = PromptTemplate( + input_variables=["page_content"], template="{page_content}" + ) + document_variable_name = "context" + llm = OpenAI() + # The prompt here should take as an input variable the + # `document_variable_name` + prompt = PromptTemplate.from_template("Summarize this content: {context}") + llm_chain = LLMChain(llm=llm, prompt=prompt) + combine_documents_chain = StuffDocumentsChain( + llm_chain=llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + ) + chain = ReduceDocumentsChain( + combine_documents_chain=combine_documents_chain, + ) + # If we wanted to, we could also pass in collapse_documents_chain + # which is specifically aimed at collapsing documents BEFORE + # the final call. + prompt = PromptTemplate.from_template("Collapse this content: {context}") + llm_chain = LLMChain(llm=llm, prompt=prompt) + collapse_documents_chain = StuffDocumentsChain( + llm_chain=llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + ) + chain = ReduceDocumentsChain( + combine_documents_chain=combine_documents_chain, + collapse_documents_chain=collapse_documents_chain, + ) + ``` """ combine_documents_chain: BaseCombineDocumentsChain diff --git a/libs/langchain/langchain_classic/chains/combine_documents/refine.py b/libs/langchain/langchain_classic/chains/combine_documents/refine.py index 66657ae1148..a6d694c63d1 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/refine.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/refine.py @@ -43,40 +43,39 @@ class RefineDocumentsChain(BaseCombineDocumentsChain): as well as the previous response with the variable name `initial_response_name`. Example: - .. code-block:: python - - from langchain_classic.chains import RefineDocumentsChain, LLMChain - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - - # This controls how each document will be formatted. Specifically, - # it will be passed to `format_document` - see that function for more - # details. - document_prompt = PromptTemplate( - input_variables=["page_content"], template="{page_content}" - ) - document_variable_name = "context" - llm = OpenAI() - # The prompt here should take as an input variable the - # `document_variable_name` - prompt = PromptTemplate.from_template("Summarize this content: {context}") - initial_llm_chain = LLMChain(llm=llm, prompt=prompt) - initial_response_name = "prev_response" - # The prompt here should take as an input variable the - # `document_variable_name` as well as `initial_response_name` - prompt_refine = PromptTemplate.from_template( - "Here's your first summary: {prev_response}. " - "Now add to it based on the following context: {context}" - ) - refine_llm_chain = LLMChain(llm=llm, prompt=prompt_refine) - chain = RefineDocumentsChain( - initial_llm_chain=initial_llm_chain, - refine_llm_chain=refine_llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - initial_response_name=initial_response_name, - ) + ```python + from langchain_classic.chains import RefineDocumentsChain, LLMChain + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI + # This controls how each document will be formatted. Specifically, + # it will be passed to `format_document` - see that function for more + # details. + document_prompt = PromptTemplate( + input_variables=["page_content"], template="{page_content}" + ) + document_variable_name = "context" + llm = OpenAI() + # The prompt here should take as an input variable the + # `document_variable_name` + prompt = PromptTemplate.from_template("Summarize this content: {context}") + initial_llm_chain = LLMChain(llm=llm, prompt=prompt) + initial_response_name = "prev_response" + # The prompt here should take as an input variable the + # `document_variable_name` as well as `initial_response_name` + prompt_refine = PromptTemplate.from_template( + "Here's your first summary: {prev_response}. " + "Now add to it based on the following context: {context}" + ) + refine_llm_chain = LLMChain(llm=llm, prompt=prompt_refine) + chain = RefineDocumentsChain( + initial_llm_chain=initial_llm_chain, + refine_llm_chain=refine_llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + initial_response_name=initial_response_name, + ) + ``` """ initial_llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/combine_documents/stuff.py b/libs/langchain/langchain_classic/chains/combine_documents/stuff.py index 8900c8b0ff1..5e05d01114e 100644 --- a/libs/langchain/langchain_classic/chains/combine_documents/stuff.py +++ b/libs/langchain/langchain_classic/chains/combine_documents/stuff.py @@ -55,32 +55,31 @@ def create_stuff_documents_chain( The Runnable return type depends on output_parser used. Example: - .. code-block:: python + ```python + # pip install -U langchain langchain-community - # pip install -U langchain langchain-community + from langchain_community.chat_models import ChatOpenAI + from langchain_core.documents import Document + from langchain_core.prompts import ChatPromptTemplate + from langchain_classic.chains.combine_documents import ( + create_stuff_documents_chain, + ) - from langchain_community.chat_models import ChatOpenAI - from langchain_core.documents import Document - from langchain_core.prompts import ChatPromptTemplate - from langchain_classic.chains.combine_documents import ( - create_stuff_documents_chain, - ) + prompt = ChatPromptTemplate.from_messages( + [("system", "What are everyone's favorite colors:\n\n{context}")] + ) + llm = ChatOpenAI(model="gpt-3.5-turbo") + chain = create_stuff_documents_chain(llm, prompt) - prompt = ChatPromptTemplate.from_messages( - [("system", "What are everyone's favorite colors:\n\n{context}")] - ) - llm = ChatOpenAI(model="gpt-3.5-turbo") - chain = create_stuff_documents_chain(llm, prompt) - - docs = [ - Document(page_content="Jesse loves red but not yellow"), - Document( - page_content="Jamal loves green but not as much as he loves orange" - ), - ] - - chain.invoke({"context": docs}) + docs = [ + Document(page_content="Jesse loves red but not yellow"), + Document( + page_content="Jamal loves green but not as much as he loves orange" + ), + ] + chain.invoke({"context": docs}) + ``` """ _validate_prompt(prompt, document_variable_name) _document_prompt = document_prompt or DEFAULT_DOCUMENT_PROMPT @@ -121,30 +120,29 @@ class StuffDocumentsChain(BaseCombineDocumentsChain): Those inputs are then passed to the `llm_chain`. Example: - .. code-block:: python - - from langchain_classic.chains import StuffDocumentsChain, LLMChain - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - - # This controls how each document will be formatted. Specifically, - # it will be passed to `format_document` - see that function for more - # details. - document_prompt = PromptTemplate( - input_variables=["page_content"], template="{page_content}" - ) - document_variable_name = "context" - llm = OpenAI() - # The prompt here should take as an input variable the - # `document_variable_name` - prompt = PromptTemplate.from_template("Summarize this content: {context}") - llm_chain = LLMChain(llm=llm, prompt=prompt) - chain = StuffDocumentsChain( - llm_chain=llm_chain, - document_prompt=document_prompt, - document_variable_name=document_variable_name, - ) + ```python + from langchain_classic.chains import StuffDocumentsChain, LLMChain + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI + # This controls how each document will be formatted. Specifically, + # it will be passed to `format_document` - see that function for more + # details. + document_prompt = PromptTemplate( + input_variables=["page_content"], template="{page_content}" + ) + document_variable_name = "context" + llm = OpenAI() + # The prompt here should take as an input variable the + # `document_variable_name` + prompt = PromptTemplate.from_template("Summarize this content: {context}") + llm_chain = LLMChain(llm=llm, prompt=prompt) + chain = StuffDocumentsChain( + llm_chain=llm_chain, + document_prompt=document_prompt, + document_variable_name=document_variable_name, + ) + ``` """ llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/constitutional_ai/base.py b/libs/langchain/langchain_classic/chains/constitutional_ai/base.py index 3884fa20f24..72bbcbb9323 100644 --- a/libs/langchain/langchain_classic/chains/constitutional_ai/base.py +++ b/libs/langchain/langchain_classic/chains/constitutional_ai/base.py @@ -40,158 +40,158 @@ class ConstitutionalChain(Chain): Install LangGraph with: - .. code-block:: bash + ```bash + pip install -U langgraph + ``` - pip install -U langgraph + ```python + from typing import List, Optional, Tuple - .. code-block:: python + from langchain_classic.chains.constitutional_ai.prompts import ( + CRITIQUE_PROMPT, + REVISION_PROMPT, + ) + from langchain_classic.chains.constitutional_ai.models import ConstitutionalPrinciple + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI + from langgraph.graph import END, START, StateGraph + from typing_extensions import Annotated, TypedDict - from typing import List, Optional, Tuple + llm = ChatOpenAI(model="gpt-4o-mini") - from langchain_classic.chains.constitutional_ai.prompts import ( - CRITIQUE_PROMPT, - REVISION_PROMPT, - ) - from langchain_classic.chains.constitutional_ai.models import ConstitutionalPrinciple - from langchain_core.output_parsers import StrOutputParser - from langchain_core.prompts import ChatPromptTemplate - from langchain_openai import ChatOpenAI - from langgraph.graph import END, START, StateGraph - from typing_extensions import Annotated, TypedDict + class Critique(TypedDict): + """Generate a critique, if needed.""" + critique_needed: Annotated[bool, ..., "Whether or not a critique is needed."] + critique: Annotated[str, ..., "If needed, the critique."] - llm = ChatOpenAI(model="gpt-4o-mini") + critique_prompt = ChatPromptTemplate.from_template( + "Critique this response according to the critique request. " + "If no critique is needed, specify that.\n\n" + "Query: {query}\n\n" + "Response: {response}\n\n" + "Critique request: {critique_request}" + ) - class Critique(TypedDict): - """Generate a critique, if needed.""" - critique_needed: Annotated[bool, ..., "Whether or not a critique is needed."] - critique: Annotated[str, ..., "If needed, the critique."] + revision_prompt = ChatPromptTemplate.from_template( + "Revise this response according to the critique and reivsion request.\n\n" + "Query: {query}\n\n" + "Response: {response}\n\n" + "Critique request: {critique_request}\n\n" + "Critique: {critique}\n\n" + "If the critique does not identify anything worth changing, ignore the " + "revision request and return 'No revisions needed'. If the critique " + "does identify something worth changing, revise the response based on " + "the revision request.\n\n" + "Revision Request: {revision_request}" + ) - critique_prompt = ChatPromptTemplate.from_template( - "Critique this response according to the critique request. " - "If no critique is needed, specify that.\n\n" - "Query: {query}\n\n" - "Response: {response}\n\n" - "Critique request: {critique_request}" - ) - - revision_prompt = ChatPromptTemplate.from_template( - "Revise this response according to the critique and reivsion request.\n\n" - "Query: {query}\n\n" - "Response: {response}\n\n" - "Critique request: {critique_request}\n\n" - "Critique: {critique}\n\n" - "If the critique does not identify anything worth changing, ignore the " - "revision request and return 'No revisions needed'. If the critique " - "does identify something worth changing, revise the response based on " - "the revision request.\n\n" - "Revision Request: {revision_request}" - ) - - chain = llm | StrOutputParser() - critique_chain = critique_prompt | llm.with_structured_output(Critique) - revision_chain = revision_prompt | llm | StrOutputParser() + chain = llm | StrOutputParser() + critique_chain = critique_prompt | llm.with_structured_output(Critique) + revision_chain = revision_prompt | llm | StrOutputParser() - class State(TypedDict): - query: str - constitutional_principles: List[ConstitutionalPrinciple] - initial_response: str - critiques_and_revisions: List[Tuple[str, str]] - response: str + class State(TypedDict): + query: str + constitutional_principles: List[ConstitutionalPrinciple] + initial_response: str + critiques_and_revisions: List[Tuple[str, str]] + response: str - async def generate_response(state: State): - """Generate initial response.""" - response = await chain.ainvoke(state["query"]) - return {"response": response, "initial_response": response} + async def generate_response(state: State): + """Generate initial response.""" + response = await chain.ainvoke(state["query"]) + return {"response": response, "initial_response": response} - async def critique_and_revise(state: State): - """Critique and revise response according to principles.""" - critiques_and_revisions = [] - response = state["initial_response"] - for principle in state["constitutional_principles"]: - critique = await critique_chain.ainvoke( + async def critique_and_revise(state: State): + """Critique and revise response according to principles.""" + critiques_and_revisions = [] + response = state["initial_response"] + for principle in state["constitutional_principles"]: + critique = await critique_chain.ainvoke( + { + "query": state["query"], + "response": response, + "critique_request": principle.critique_request, + } + ) + if critique["critique_needed"]: + revision = await revision_chain.ainvoke( { "query": state["query"], "response": response, "critique_request": principle.critique_request, + "critique": critique["critique"], + "revision_request": principle.revision_request, } ) - if critique["critique_needed"]: - revision = await revision_chain.ainvoke( - { - "query": state["query"], - "response": response, - "critique_request": principle.critique_request, - "critique": critique["critique"], - "revision_request": principle.revision_request, - } - ) - response = revision - critiques_and_revisions.append((critique["critique"], revision)) - else: - critiques_and_revisions.append((critique["critique"], "")) - return { - "critiques_and_revisions": critiques_and_revisions, - "response": response, - } + response = revision + critiques_and_revisions.append((critique["critique"], revision)) + else: + critiques_and_revisions.append((critique["critique"], "")) + return { + "critiques_and_revisions": critiques_and_revisions, + "response": response, + } - graph = StateGraph(State) - graph.add_node("generate_response", generate_response) - graph.add_node("critique_and_revise", critique_and_revise) + graph = StateGraph(State) + graph.add_node("generate_response", generate_response) + graph.add_node("critique_and_revise", critique_and_revise) - graph.add_edge(START, "generate_response") - graph.add_edge("generate_response", "critique_and_revise") - graph.add_edge("critique_and_revise", END) - app = graph.compile() + graph.add_edge(START, "generate_response") + graph.add_edge("generate_response", "critique_and_revise") + graph.add_edge("critique_and_revise", END) + app = graph.compile() + ``` - .. code-block:: python + ```python + constitutional_principles=[ + ConstitutionalPrinciple( + critique_request="Tell if this answer is good.", + revision_request="Give a better answer.", + ) + ] + query = "What is the meaning of life? Answer in 10 words or fewer." + + async for step in app.astream( + {"query": query, "constitutional_principles": constitutional_principles}, + stream_mode="values", + ): + subset = ["initial_response", "critiques_and_revisions", "response"] + print({k: v for k, v in step.items() if k in subset}) + ``` + + Example: + ```python + from langchain_community.llms import OpenAI + from langchain_classic.chains import LLMChain, ConstitutionalChain + from langchain_classic.chains.constitutional_ai.models \ + import ConstitutionalPrinciple + + llm = OpenAI() + + qa_prompt = PromptTemplate( + template="Q: {question} A:", + input_variables=["question"], + ) + qa_chain = LLMChain(llm=llm, prompt=qa_prompt) + + constitutional_chain = ConstitutionalChain.from_llm( + llm=llm, + chain=qa_chain, constitutional_principles=[ ConstitutionalPrinciple( critique_request="Tell if this answer is good.", revision_request="Give a better answer.", ) - ] + ], + ) - query = "What is the meaning of life? Answer in 10 words or fewer." - - async for step in app.astream( - {"query": query, "constitutional_principles": constitutional_principles}, - stream_mode="values", - ): - subset = ["initial_response", "critiques_and_revisions", "response"] - print({k: v for k, v in step.items() if k in subset}) - - Example: - .. code-block:: python - - from langchain_community.llms import OpenAI - from langchain_classic.chains import LLMChain, ConstitutionalChain - from langchain_classic.chains.constitutional_ai.models \ - import ConstitutionalPrinciple - - llm = OpenAI() - - qa_prompt = PromptTemplate( - template="Q: {question} A:", - input_variables=["question"], - ) - qa_chain = LLMChain(llm=llm, prompt=qa_prompt) - - constitutional_chain = ConstitutionalChain.from_llm( - llm=llm, - chain=qa_chain, - constitutional_principles=[ - ConstitutionalPrinciple( - critique_request="Tell if this answer is good.", - revision_request="Give a better answer.", - ) - ], - ) - - constitutional_chain.run(question="What is the meaning of life?") + constitutional_chain.run(question="What is the meaning of life?") + ``` ''' # noqa: E501 chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/conversation/base.py b/libs/langchain/langchain_classic/chains/conversation/base.py index 492c9036485..07c1c98ee08 100644 --- a/libs/langchain/langchain_classic/chains/conversation/base.py +++ b/libs/langchain/langchain_classic/chains/conversation/base.py @@ -32,76 +32,75 @@ class ConversationChain(LLMChain): Below is a minimal implementation, analogous to using `ConversationChain` with the default `ConversationBufferMemory`: - .. code-block:: python - - from langchain_core.chat_history import InMemoryChatMessageHistory - from langchain_core.runnables.history import RunnableWithMessageHistory - from langchain_openai import ChatOpenAI + ```python + from langchain_core.chat_history import InMemoryChatMessageHistory + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_openai import ChatOpenAI - store = {} # memory is maintained outside the chain + store = {} # memory is maintained outside the chain - def get_session_history(session_id: str) -> InMemoryChatMessageHistory: - if session_id not in store: - store[session_id] = InMemoryChatMessageHistory() - return store[session_id] + def get_session_history(session_id: str) -> InMemoryChatMessageHistory: + if session_id not in store: + store[session_id] = InMemoryChatMessageHistory() + return store[session_id] - llm = ChatOpenAI(model="gpt-3.5-turbo-0125") + llm = ChatOpenAI(model="gpt-3.5-turbo-0125") - chain = RunnableWithMessageHistory(llm, get_session_history) - chain.invoke( - "Hi I'm Bob.", - config={"configurable": {"session_id": "1"}}, - ) # session_id determines thread + chain = RunnableWithMessageHistory(llm, get_session_history) + chain.invoke( + "Hi I'm Bob.", + config={"configurable": {"session_id": "1"}}, + ) # session_id determines thread + ``` Memory objects can also be incorporated into the `get_session_history` callable: - .. code-block:: python - - from langchain_classic.memory import ConversationBufferWindowMemory - from langchain_core.chat_history import InMemoryChatMessageHistory - from langchain_core.runnables.history import RunnableWithMessageHistory - from langchain_openai import ChatOpenAI + ```python + from langchain_classic.memory import ConversationBufferWindowMemory + from langchain_core.chat_history import InMemoryChatMessageHistory + from langchain_core.runnables.history import RunnableWithMessageHistory + from langchain_openai import ChatOpenAI - store = {} # memory is maintained outside the chain + store = {} # memory is maintained outside the chain - def get_session_history(session_id: str) -> InMemoryChatMessageHistory: - if session_id not in store: - store[session_id] = InMemoryChatMessageHistory() - return store[session_id] - - memory = ConversationBufferWindowMemory( - chat_memory=store[session_id], - k=3, - return_messages=True, - ) - assert len(memory.memory_variables) == 1 - key = memory.memory_variables[0] - messages = memory.load_memory_variables({})[key] - store[session_id] = InMemoryChatMessageHistory(messages=messages) + def get_session_history(session_id: str) -> InMemoryChatMessageHistory: + if session_id not in store: + store[session_id] = InMemoryChatMessageHistory() return store[session_id] + memory = ConversationBufferWindowMemory( + chat_memory=store[session_id], + k=3, + return_messages=True, + ) + assert len(memory.memory_variables) == 1 + key = memory.memory_variables[0] + messages = memory.load_memory_variables({})[key] + store[session_id] = InMemoryChatMessageHistory(messages=messages) + return store[session_id] - llm = ChatOpenAI(model="gpt-3.5-turbo-0125") - chain = RunnableWithMessageHistory(llm, get_session_history) - chain.invoke( - "Hi I'm Bob.", - config={"configurable": {"session_id": "1"}}, - ) # session_id determines thread + llm = ChatOpenAI(model="gpt-3.5-turbo-0125") + + chain = RunnableWithMessageHistory(llm, get_session_history) + chain.invoke( + "Hi I'm Bob.", + config={"configurable": {"session_id": "1"}}, + ) # session_id determines thread + ``` Example: - .. code-block:: python - - from langchain_classic.chains import ConversationChain - from langchain_community.llms import OpenAI - - conversation = ConversationChain(llm=OpenAI()) + ```python + from langchain_classic.chains import ConversationChain + from langchain_community.llms import OpenAI + conversation = ConversationChain(llm=OpenAI()) + ``` """ memory: BaseMemory = Field(default_factory=ConversationBufferMemory) diff --git a/libs/langchain/langchain_classic/chains/conversational_retrieval/base.py b/libs/langchain/langchain_classic/chains/conversational_retrieval/base.py index a70e18b140b..d1424619ec4 100644 --- a/libs/langchain/langchain_classic/chains/conversational_retrieval/base.py +++ b/libs/langchain/langchain_classic/chains/conversational_retrieval/base.py @@ -270,69 +270,67 @@ class ConversationalRetrievalChain(BaseConversationalRetrievalChain): `create_retrieval_chain`. Additional walkthroughs can be found at https://python.langchain.com/docs/use_cases/question_answering/chat_history - .. code-block:: python + ```python + from langchain_classic.chains import ( + create_history_aware_retriever, + create_retrieval_chain, + ) + from langchain_classic.chains.combine_documents import ( + create_stuff_documents_chain, + ) + from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder + from langchain_openai import ChatOpenAI - from langchain_classic.chains import ( - create_history_aware_retriever, - create_retrieval_chain, - ) - from langchain_classic.chains.combine_documents import ( - create_stuff_documents_chain, - ) - from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder - from langchain_openai import ChatOpenAI + retriever = ... # Your retriever - retriever = ... # Your retriever + llm = ChatOpenAI() - llm = ChatOpenAI() + # Contextualize question + contextualize_q_system_prompt = ( + "Given a chat history and the latest user question " + "which might reference context in the chat history, " + "formulate a standalone question which can be understood " + "without the chat history. Do NOT answer the question, just " + "reformulate it if needed and otherwise return it as is." + ) + contextualize_q_prompt = ChatPromptTemplate.from_messages( + [ + ("system", contextualize_q_system_prompt), + MessagesPlaceholder("chat_history"), + ("human", "{input}"), + ] + ) + history_aware_retriever = create_history_aware_retriever( + llm, retriever, contextualize_q_prompt + ) - # Contextualize question - contextualize_q_system_prompt = ( - "Given a chat history and the latest user question " - "which might reference context in the chat history, " - "formulate a standalone question which can be understood " - "without the chat history. Do NOT answer the question, just " - "reformulate it if needed and otherwise return it as is." - ) - contextualize_q_prompt = ChatPromptTemplate.from_messages( - [ - ("system", contextualize_q_system_prompt), - MessagesPlaceholder("chat_history"), - ("human", "{input}"), - ] - ) - history_aware_retriever = create_history_aware_retriever( - llm, retriever, contextualize_q_prompt - ) + # Answer question + qa_system_prompt = ( + "You are an assistant for question-answering tasks. Use " + "the following pieces of retrieved context to answer the " + "question. If you don't know the answer, just say that you " + "don't know. Use three sentences maximum and keep the answer " + "concise." + "\n\n" + "{context}" + ) + qa_prompt = ChatPromptTemplate.from_messages( + [ + ("system", qa_system_prompt), + MessagesPlaceholder("chat_history"), + ("human", "{input}"), + ] + ) + # Below we use create_stuff_documents_chain to feed all retrieved context + # into the LLM. Note that we can also use StuffDocumentsChain and other + # instances of BaseCombineDocumentsChain. + question_answer_chain = create_stuff_documents_chain(llm, qa_prompt) + rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain) - # Answer question - qa_system_prompt = ( - "You are an assistant for question-answering tasks. Use " - "the following pieces of retrieved context to answer the " - "question. If you don't know the answer, just say that you " - "don't know. Use three sentences maximum and keep the answer " - "concise." - "\n\n" - "{context}" - ) - qa_prompt = ChatPromptTemplate.from_messages( - [ - ("system", qa_system_prompt), - MessagesPlaceholder("chat_history"), - ("human", "{input}"), - ] - ) - # Below we use create_stuff_documents_chain to feed all retrieved context - # into the LLM. Note that we can also use StuffDocumentsChain and other - # instances of BaseCombineDocumentsChain. - question_answer_chain = create_stuff_documents_chain(llm, qa_prompt) - rag_chain = create_retrieval_chain( - history_aware_retriever, question_answer_chain - ) - - # Usage: - chat_history = [] # Collect chat history here (a sequence of messages) - rag_chain.invoke({"input": query, "chat_history": chat_history}) + # Usage: + chat_history = [] # Collect chat history here (a sequence of messages) + rag_chain.invoke({"input": query, "chat_history": chat_history}) + ``` This chain takes in chat history (a list of messages) and new questions, and then returns an answer to that question. @@ -352,36 +350,35 @@ class ConversationalRetrievalChain(BaseConversationalRetrievalChain): response. Example: - .. code-block:: python + ```python + from langchain_classic.chains import ( + StuffDocumentsChain, + LLMChain, + ConversationalRetrievalChain, + ) + from langchain_core.prompts import PromptTemplate + from langchain_community.llms import OpenAI - from langchain_classic.chains import ( - StuffDocumentsChain, - LLMChain, - ConversationalRetrievalChain, - ) - from langchain_core.prompts import PromptTemplate - from langchain_community.llms import OpenAI - - combine_docs_chain = StuffDocumentsChain(...) - vectorstore = ... - retriever = vectorstore.as_retriever() - - # This controls how the standalone question is generated. - # Should take `chat_history` and `question` as input variables. - template = ( - "Combine the chat history and follow up question into " - "a standalone question. Chat History: {chat_history}" - "Follow up question: {question}" - ) - prompt = PromptTemplate.from_template(template) - llm = OpenAI() - question_generator_chain = LLMChain(llm=llm, prompt=prompt) - chain = ConversationalRetrievalChain( - combine_docs_chain=combine_docs_chain, - retriever=retriever, - question_generator=question_generator_chain, - ) + combine_docs_chain = StuffDocumentsChain(...) + vectorstore = ... + retriever = vectorstore.as_retriever() + # This controls how the standalone question is generated. + # Should take `chat_history` and `question` as input variables. + template = ( + "Combine the chat history and follow up question into " + "a standalone question. Chat History: {chat_history}" + "Follow up question: {question}" + ) + prompt = PromptTemplate.from_template(template) + llm = OpenAI() + question_generator_chain = LLMChain(llm=llm, prompt=prompt) + chain = ConversationalRetrievalChain( + combine_docs_chain=combine_docs_chain, + retriever=retriever, + question_generator=question_generator_chain, + ) + ``` """ retriever: BaseRetriever diff --git a/libs/langchain/langchain_classic/chains/elasticsearch_database/base.py b/libs/langchain/langchain_classic/chains/elasticsearch_database/base.py index 70b681e1eee..589cccbb832 100644 --- a/libs/langchain/langchain_classic/chains/elasticsearch_database/base.py +++ b/libs/langchain/langchain_classic/chains/elasticsearch_database/base.py @@ -29,15 +29,14 @@ class ElasticsearchDatabaseChain(Chain): """Chain for interacting with Elasticsearch Database. Example: - .. code-block:: python - - from langchain_classic.chains import ElasticsearchDatabaseChain - from langchain_community.llms import OpenAI - from elasticsearch import Elasticsearch - - database = Elasticsearch("http://localhost:9200") - db_chain = ElasticsearchDatabaseChain.from_llm(OpenAI(), database) + ```python + from langchain_classic.chains import ElasticsearchDatabaseChain + from langchain_community.llms import OpenAI + from elasticsearch import Elasticsearch + database = Elasticsearch("http://localhost:9200") + db_chain = ElasticsearchDatabaseChain.from_llm(OpenAI(), database) + ``` """ query_chain: Runnable diff --git a/libs/langchain/langchain_classic/chains/history_aware_retriever.py b/libs/langchain/langchain_classic/chains/history_aware_retriever.py index 8fe40de304e..ed16c85560b 100644 --- a/libs/langchain/langchain_classic/chains/history_aware_retriever.py +++ b/libs/langchain/langchain_classic/chains/history_aware_retriever.py @@ -30,23 +30,23 @@ def create_history_aware_retriever( The Runnable output is a list of Documents Example: - .. code-block:: python + ```python + # pip install -U langchain langchain-community - # pip install -U langchain langchain-community + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.chains import create_history_aware_retriever + from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.chains import create_history_aware_retriever - from langchain_classic import hub + rephrase_prompt = hub.pull("langchain-ai/chat-langchain-rephrase") + llm = ChatOpenAI() + retriever = ... + chat_retriever_chain = create_history_aware_retriever( + llm, retriever, rephrase_prompt + ) - rephrase_prompt = hub.pull("langchain-ai/chat-langchain-rephrase") - llm = ChatOpenAI() - retriever = ... - chat_retriever_chain = create_history_aware_retriever( - llm, retriever, rephrase_prompt - ) - - chain.invoke({"input": "...", "chat_history": }) + chain.invoke({"input": "...", "chat_history": }) + ``` """ if "input" not in prompt.input_variables: msg = ( diff --git a/libs/langchain/langchain_classic/chains/llm.py b/libs/langchain/langchain_classic/chains/llm.py index 0076c298287..534cd97794c 100644 --- a/libs/langchain/langchain_classic/chains/llm.py +++ b/libs/langchain/langchain_classic/chains/llm.py @@ -48,34 +48,29 @@ class LLMChain(Chain): This class is deprecated. See below for an example implementation using LangChain runnables: - .. code-block:: python + ```python + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import PromptTemplate + from langchain_openai import OpenAI - from langchain_core.output_parsers import StrOutputParser - from langchain_core.prompts import PromptTemplate - from langchain_openai import OpenAI + prompt_template = "Tell me a {adjective} joke" + prompt = PromptTemplate(input_variables=["adjective"], template=prompt_template) + llm = OpenAI() + chain = prompt | llm | StrOutputParser() - prompt_template = "Tell me a {adjective} joke" - prompt = PromptTemplate( - input_variables=["adjective"], template=prompt_template - ) - llm = OpenAI() - chain = prompt | llm | StrOutputParser() - - chain.invoke("your adjective here") + chain.invoke("your adjective here") + ``` Example: - .. code-block:: python - - from langchain_classic.chains import LLMChain - from langchain_community.llms import OpenAI - from langchain_core.prompts import PromptTemplate - - prompt_template = "Tell me a {adjective} joke" - prompt = PromptTemplate( - input_variables=["adjective"], template=prompt_template - ) - llm = LLMChain(llm=OpenAI(), prompt=prompt) + ```python + from langchain_classic.chains import LLMChain + from langchain_community.llms import OpenAI + from langchain_core.prompts import PromptTemplate + prompt_template = "Tell me a {adjective} joke" + prompt = PromptTemplate(input_variables=["adjective"], template=prompt_template) + llm = LLMChain(llm=OpenAI(), prompt=prompt) + ``` """ @classmethod @@ -319,10 +314,9 @@ class LLMChain(Chain): Completion from LLM. Example: - .. code-block:: python - - completion = llm.predict(adjective="funny") - + ```python + completion = llm.predict(adjective="funny") + ``` """ return self(kwargs, callbacks=callbacks)[self.output_key] @@ -337,10 +331,9 @@ class LLMChain(Chain): Completion from LLM. Example: - .. code-block:: python - - completion = llm.predict(adjective="funny") - + ```python + completion = llm.predict(adjective="funny") + ``` """ return (await self.acall(kwargs, callbacks=callbacks))[self.output_key] diff --git a/libs/langchain/langchain_classic/chains/llm_checker/base.py b/libs/langchain/langchain_classic/chains/llm_checker/base.py index 39e1b92d747..5bd53322e9c 100644 --- a/libs/langchain/langchain_classic/chains/llm_checker/base.py +++ b/libs/langchain/langchain_classic/chains/llm_checker/base.py @@ -76,14 +76,13 @@ class LLMCheckerChain(Chain): """Chain for question-answering with self-verification. Example: - .. code-block:: python - - from langchain_community.llms import OpenAI - from langchain_classic.chains import LLMCheckerChain - - llm = OpenAI(temperature=0.7) - checker_chain = LLMCheckerChain.from_llm(llm) + ```python + from langchain_community.llms import OpenAI + from langchain_classic.chains import LLMCheckerChain + llm = OpenAI(temperature=0.7) + checker_chain = LLMCheckerChain.from_llm(llm) + ``` """ question_to_checked_assertions_chain: SequentialChain diff --git a/libs/langchain/langchain_classic/chains/llm_math/base.py b/libs/langchain/langchain_classic/chains/llm_math/base.py index 7d262b69d50..86eea13e1b7 100644 --- a/libs/langchain/langchain_classic/chains/llm_math/base.py +++ b/libs/langchain/langchain_classic/chains/llm_math/base.py @@ -41,35 +41,35 @@ class LLMMathChain(Chain): - Support for both token-by-token and step-by-step streaming; - Support for checkpointing and memory of chat history; - Easier to modify or extend - (e.g., with additional tools, structured responses, etc.) + (e.g., with additional tools, structured responses, etc.) Install LangGraph with: - .. code-block:: bash + ```bash + pip install -U langgraph + ``` - pip install -U langgraph + ```python + import math + from typing import Annotated, Sequence - .. code-block:: python + from langchain_core.messages import BaseMessage + from langchain_core.runnables import RunnableConfig + from langchain_core.tools import tool + from langchain_openai import ChatOpenAI + from langgraph.graph import END, StateGraph + from langgraph.graph.message import add_messages + from langgraph.prebuilt.tool_node import ToolNode + import numexpr + from typing_extensions import TypedDict - import math - from typing import Annotated, Sequence + @tool + def calculator(expression: str) -> str: + \"\"\"Calculate expression using Python's numexpr library. - from langchain_core.messages import BaseMessage - from langchain_core.runnables import RunnableConfig - from langchain_core.tools import tool - from langchain_openai import ChatOpenAI - from langgraph.graph import END, StateGraph - from langgraph.graph.message import add_messages - from langgraph.prebuilt.tool_node import ToolNode - import numexpr - from typing_extensions import TypedDict - - @tool - def calculator(expression: str) -> str: - \"\"\"Calculate expression using Python's numexpr library. - - Expression should be a single line mathematical expression - that solves the problem. + Expression should be a single line mathematical expression + that solves the problem. + ``` Examples: "37593 * 67" for "37593 times 67" @@ -112,45 +112,44 @@ class LLMMathChain(Chain): graph_builder.add_edge("call_model", END) chain = graph_builder.compile() - .. code-block:: python + ```python + example_query = "What is 551368 divided by 82" - example_query = "What is 551368 divided by 82" + events = chain.astream( + {"messages": [("user", example_query)]}, + stream_mode="values", + ) + async for event in events: + event["messages"][-1].pretty_print() + ``` - events = chain.astream( - {"messages": [("user", example_query)]}, - stream_mode="values", - ) - async for event in events: - event["messages"][-1].pretty_print() + ```txt + ================================ Human Message ================================= - .. code-block:: + What is 551368 divided by 82 + ================================== Ai Message ================================== + Tool Calls: + calculator (call_MEiGXuJjJ7wGU4aOT86QuGJS) + Call ID: call_MEiGXuJjJ7wGU4aOT86QuGJS + Args: + expression: 551368 / 82 + ================================= Tool Message ================================= + Name: calculator - ================================ Human Message ================================= + 6724.0 + ================================== Ai Message ================================== - What is 551368 divided by 82 - ================================== Ai Message ================================== - Tool Calls: - calculator (call_MEiGXuJjJ7wGU4aOT86QuGJS) - Call ID: call_MEiGXuJjJ7wGU4aOT86QuGJS - Args: - expression: 551368 / 82 - ================================= Tool Message ================================= - Name: calculator - - 6724.0 - ================================== Ai Message ================================== - - 551368 divided by 82 equals 6724. + 551368 divided by 82 equals 6724. + ``` Example: - .. code-block:: python + ```python + from langchain_classic.chains import LLMMathChain + from langchain_community.llms import OpenAI - from langchain_classic.chains import LLMMathChain - from langchain_community.llms import OpenAI - - llm_math = LLMMathChain.from_llm(OpenAI()) - - """ # noqa: E501 + llm_math = LLMMathChain.from_llm(OpenAI()) + ``` + """ llm_chain: LLMChain llm: BaseLanguageModel | None = None diff --git a/libs/langchain/langchain_classic/chains/llm_summarization_checker/base.py b/libs/langchain/langchain_classic/chains/llm_summarization_checker/base.py index fabf4603bc4..28e745772a7 100644 --- a/libs/langchain/langchain_classic/chains/llm_summarization_checker/base.py +++ b/libs/langchain/langchain_classic/chains/llm_summarization_checker/base.py @@ -79,14 +79,13 @@ class LLMSummarizationCheckerChain(Chain): """Chain for question-answering with self-verification. Example: - .. code-block:: python - - from langchain_community.llms import OpenAI - from langchain_classic.chains import LLMSummarizationCheckerChain - - llm = OpenAI(temperature=0.0) - checker_chain = LLMSummarizationCheckerChain.from_llm(llm) + ```python + from langchain_community.llms import OpenAI + from langchain_classic.chains import LLMSummarizationCheckerChain + llm = OpenAI(temperature=0.0) + checker_chain = LLMSummarizationCheckerChain.from_llm(llm) + ``` """ sequential_chain: SequentialChain diff --git a/libs/langchain/langchain_classic/chains/moderation.py b/libs/langchain/langchain_classic/chains/moderation.py index 8aaf1b8f4af..1a7e9aa5002 100644 --- a/libs/langchain/langchain_classic/chains/moderation.py +++ b/libs/langchain/langchain_classic/chains/moderation.py @@ -23,12 +23,11 @@ class OpenAIModerationChain(Chain): in, even if not explicitly saved on this class. Example: - .. code-block:: python - - from langchain_classic.chains import OpenAIModerationChain - - moderation = OpenAIModerationChain() + ```python + from langchain_classic.chains import OpenAIModerationChain + moderation = OpenAIModerationChain() + ``` """ client: Any = None #: :meta private: diff --git a/libs/langchain/langchain_classic/chains/natbot/base.py b/libs/langchain/langchain_classic/chains/natbot/base.py index 45b63fbcc2a..6c482bccd86 100644 --- a/libs/langchain/langchain_classic/chains/natbot/base.py +++ b/libs/langchain/langchain_classic/chains/natbot/base.py @@ -43,12 +43,11 @@ class NatBotChain(Chain): See https://python.langchain.com/docs/security for more information. Example: - .. code-block:: python - - from langchain_classic.chains import NatBotChain - - natbot = NatBotChain.from_default("Buy me a new hat.") + ```python + from langchain_classic.chains import NatBotChain + natbot = NatBotChain.from_default("Buy me a new hat.") + ``` """ llm_chain: Runnable @@ -149,11 +148,10 @@ class NatBotChain(Chain): Next browser command to run. Example: - .. code-block:: python - - browser_content = "...." - llm_command = natbot.run("www.google.com", browser_content) - + ```python + browser_content = "...." + llm_command = natbot.run("www.google.com", browser_content) + ``` """ _inputs = { self.input_url_key: url, diff --git a/libs/langchain/langchain_classic/chains/openai_functions/base.py b/libs/langchain/langchain_classic/chains/openai_functions/base.py index bda045a7931..417ae1e2a64 100644 --- a/libs/langchain/langchain_classic/chains/openai_functions/base.py +++ b/libs/langchain/langchain_classic/chains/openai_functions/base.py @@ -81,45 +81,45 @@ def create_openai_fn_chain( An LLMChain that will pass in the given functions to the model when run. Example: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from langchain_classic.chains.openai_functions import create_openai_fn_chain + from langchain_community.chat_models import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate - from langchain_classic.chains.openai_functions import create_openai_fn_chain - from langchain_community.chat_models import ChatOpenAI - from langchain_core.prompts import ChatPromptTemplate - - from pydantic import BaseModel, Field + from pydantic import BaseModel, Field - class RecordPerson(BaseModel): - \"\"\"Record some identifying information about a person.\"\"\" + class RecordPerson(BaseModel): + \"\"\"Record some identifying information about a person.\"\"\" - name: str = Field(..., description="The person's name") - age: int = Field(..., description="The person's age") - fav_food: str | None = Field(None, description="The person's favorite food") + name: str = Field(..., description="The person's name") + age: int = Field(..., description="The person's age") + fav_food: str | None = Field(None, description="The person's favorite food") - class RecordDog(BaseModel): - \"\"\"Record some identifying information about a dog.\"\"\" + class RecordDog(BaseModel): + \"\"\"Record some identifying information about a dog.\"\"\" - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - llm = ChatOpenAI(model="gpt-4", temperature=0) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a world class algorithm for recording entities."), - ("human", "Make calls to the relevant function to record the entities in the following input: {input}"), - ("human", "Tip: Make sure to answer in the correct format"), - ] - ) - chain = create_openai_fn_chain([RecordPerson, RecordDog], llm, prompt) - chain.run("Harry was a chubby brown beagle who loved chicken") - # -> RecordDog(name="Harry", color="brown", fav_food="chicken") + llm = ChatOpenAI(model="gpt-4", temperature=0) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a world class algorithm for recording entities."), + ("human", "Make calls to the relevant function to record the entities in the following input: {input}"), + ("human", "Tip: Make sure to answer in the correct format"), + ] + ) + chain = create_openai_fn_chain([RecordPerson, RecordDog], llm, prompt) + chain.run("Harry was a chubby brown beagle who loved chicken") + # -> RecordDog(name="Harry", color="brown", fav_food="chicken") + ``` """ # noqa: E501 if not functions: msg = "Need to pass in at least one function. Received zero." @@ -175,35 +175,35 @@ def create_structured_output_chain( An LLMChain that will pass the given function to the model. Example: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from langchain_classic.chains.openai_functions import create_structured_output_chain + from langchain_community.chat_models import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate - from langchain_classic.chains.openai_functions import create_structured_output_chain - from langchain_community.chat_models import ChatOpenAI - from langchain_core.prompts import ChatPromptTemplate + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field + class Dog(BaseModel): + \"\"\"Identifying information about a dog.\"\"\" - class Dog(BaseModel): - \"\"\"Identifying information about a dog.\"\"\" + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") - - llm = ChatOpenAI(model="gpt-3.5-turbo-0613", temperature=0) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a world class algorithm for extracting information in structured formats."), - ("human", "Use the given format to extract information from the following input: {input}"), - ("human", "Tip: Make sure to answer in the correct format"), - ] - ) - chain = create_structured_output_chain(Dog, llm, prompt) - chain.run("Harry was a chubby brown beagle who loved chicken") - # -> Dog(name="Harry", color="brown", fav_food="chicken") + llm = ChatOpenAI(model="gpt-3.5-turbo-0613", temperature=0) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are a world class algorithm for extracting information in structured formats."), + ("human", "Use the given format to extract information from the following input: {input}"), + ("human", "Tip: Make sure to answer in the correct format"), + ] + ) + chain = create_structured_output_chain(Dog, llm, prompt) + chain.run("Harry was a chubby brown beagle who loved chicken") + # -> Dog(name="Harry", color="brown", fav_food="chicken") + ``` """ # noqa: E501 if isinstance(output_schema, dict): function: Any = { diff --git a/libs/langchain/langchain_classic/chains/openai_functions/citation_fuzzy_match.py b/libs/langchain/langchain_classic/chains/openai_functions/citation_fuzzy_match.py index 3b0ee91e55d..0a8cc3bed02 100644 --- a/libs/langchain/langchain_classic/chains/openai_functions/citation_fuzzy_match.py +++ b/libs/langchain/langchain_classic/chains/openai_functions/citation_fuzzy_match.py @@ -79,18 +79,18 @@ def create_citation_fuzzy_match_runnable(llm: BaseChatModel) -> Runnable: Example usage: - .. code-block:: python + ```python + from langchain_classic.chains import create_citation_fuzzy_match_runnable + from langchain_openai import ChatOpenAI - from langchain_classic.chains import create_citation_fuzzy_match_runnable - from langchain_openai import ChatOpenAI + llm = ChatOpenAI(model="gpt-4o-mini") - llm = ChatOpenAI(model="gpt-4o-mini") + context = "Alice has blue eyes. Bob has brown eyes. Charlie has green eyes." + question = "What color are Bob's eyes?" - context = "Alice has blue eyes. Bob has brown eyes. Charlie has green eyes." - question = "What color are Bob's eyes?" - - chain = create_citation_fuzzy_match_runnable(llm) - chain.invoke({"question": question, "context": context}) + chain = create_citation_fuzzy_match_runnable(llm) + chain.invoke({"question": question, "context": context}) + ``` Args: llm: Language model to use for the chain. Must implement bind_tools. diff --git a/libs/langchain/langchain_classic/chains/openai_functions/openapi.py b/libs/langchain/langchain_classic/chains/openai_functions/openapi.py index 7c1e065dbb8..2cfbfc31522 100644 --- a/libs/langchain/langchain_classic/chains/openai_functions/openapi.py +++ b/libs/langchain/langchain_classic/chains/openai_functions/openapi.py @@ -277,76 +277,76 @@ def get_openapi_chain( - Uses LLM tool calling features to encourage properly-formatted API requests; - Includes async support. - .. code-block:: python + ```python + from typing import Any - from typing import Any + from langchain_classic.chains.openai_functions.openapi import openapi_spec_to_openai_fn + from langchain_community.utilities.openapi import OpenAPISpec + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI - from langchain_classic.chains.openai_functions.openapi import openapi_spec_to_openai_fn - from langchain_community.utilities.openapi import OpenAPISpec - from langchain_core.prompts import ChatPromptTemplate - from langchain_openai import ChatOpenAI - - # Define API spec. Can be JSON or YAML - api_spec = \"\"\" + # Define API spec. Can be JSON or YAML + api_spec = \"\"\" + { + "openapi": "3.1.0", + "info": { + "title": "JSONPlaceholder API", + "version": "1.0.0" + }, + "servers": [ { - "openapi": "3.1.0", - "info": { - "title": "JSONPlaceholder API", - "version": "1.0.0" - }, - "servers": [ + "url": "https://jsonplaceholder.typicode.com" + } + ], + "paths": { + "/posts": { + "get": { + "summary": "Get posts", + "parameters": [ { - "url": "https://jsonplaceholder.typicode.com" - } - ], - "paths": { - "/posts": { - "get": { - "summary": "Get posts", - "parameters": [ - { - "name": "_limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "example": 2 - }, - "description": "Limit the number of results" - } - ] - } + "name": "_limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "example": 2 + }, + "description": "Limit the number of results" } + ] } } - \"\"\" + } + } + \"\"\" - parsed_spec = OpenAPISpec.from_text(api_spec) - openai_fns, call_api_fn = openapi_spec_to_openai_fn(parsed_spec) - tools = [ - {"type": "function", "function": fn} - for fn in openai_fns - ] + parsed_spec = OpenAPISpec.from_text(api_spec) + openai_fns, call_api_fn = openapi_spec_to_openai_fn(parsed_spec) + tools = [ + {"type": "function", "function": fn} + for fn in openai_fns + ] - prompt = ChatPromptTemplate.from_template( - "Use the provided APIs to respond to this user query:\\n\\n{query}" - ) - llm = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools) + prompt = ChatPromptTemplate.from_template( + "Use the provided APIs to respond to this user query:\\n\\n{query}" + ) + llm = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools) - def _execute_tool(message) -> Any: - if tool_calls := message.tool_calls: - tool_call = message.tool_calls[0] - response = call_api_fn(name=tool_call["name"], fn_args=tool_call["args"]) - response.raise_for_status() - return response.json() - else: - return message.content + def _execute_tool(message) -> Any: + if tool_calls := message.tool_calls: + tool_call = message.tool_calls[0] + response = call_api_fn(name=tool_call["name"], fn_args=tool_call["args"]) + response.raise_for_status() + return response.json() + else: + return message.content - chain = prompt | llm | _execute_tool + chain = prompt | llm | _execute_tool + ``` - .. code-block:: python - - response = chain.invoke({"query": "Get me top two posts."}) + ```python + response = chain.invoke({"query": "Get me top two posts."}) + ``` Args: spec: OpenAPISpec or url/file/text string corresponding to one. diff --git a/libs/langchain/langchain_classic/chains/openai_functions/tagging.py b/libs/langchain/langchain_classic/chains/openai_functions/tagging.py index 3b3dbf6788f..81021bc842d 100644 --- a/libs/langchain/langchain_classic/chains/openai_functions/tagging.py +++ b/libs/langchain/langchain_classic/chains/openai_functions/tagging.py @@ -62,27 +62,28 @@ def create_tagging_chain( This function is deprecated. Please use `with_structured_output` instead. See example usage below: - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict + from langchain_anthropic import ChatAnthropic - from typing_extensions import Annotated, TypedDict - from langchain_anthropic import ChatAnthropic + class Joke(TypedDict): + \"\"\"Tagged joke.\"\"\" - class Joke(TypedDict): - \"\"\"Tagged joke.\"\"\" + setup: Annotated[str, ..., "The setup of the joke"] + punchline: Annotated[str, ..., "The punchline of the joke"] - setup: Annotated[str, ..., "The setup of the joke"] - punchline: Annotated[str, ..., "The punchline of the joke"] + # Or any other chat model that supports tools. + # Please reference to to the documentation of structured_output + # to see an up to date list of which models support + # with_structured_output. + model = ChatAnthropic(model="claude-3-haiku-20240307", temperature=0) + structured_llm = model.with_structured_output(Joke) + structured_llm.invoke( + "Why did the cat cross the road? To get to the other " + "side... and then lay down in the middle of it!" + ) + ``` - # Or any other chat model that supports tools. - # Please reference to to the documentation of structured_output - # to see an up to date list of which models support - # with_structured_output. - model = ChatAnthropic(model="claude-3-haiku-20240307", temperature=0) - structured_llm = model.with_structured_output(Joke) - structured_llm.invoke( - "Why did the cat cross the road? To get to the other " - "side... and then lay down in the middle of it!" - ) Read more here: https://python.langchain.com/docs/how_to/structured_output/ Args: @@ -137,25 +138,28 @@ def create_tagging_chain_pydantic( This function is deprecated. Please use `with_structured_output` instead. See example usage below: - .. code-block:: python + ```python + from pydantic import BaseModel, Field + from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel, Field - from langchain_anthropic import ChatAnthropic - class Joke(BaseModel): - setup: str = Field(description="The setup of the joke") - punchline: str = Field(description="The punchline to the joke") + class Joke(BaseModel): + setup: str = Field(description="The setup of the joke") + punchline: str = Field(description="The punchline to the joke") + + + # Or any other chat model that supports tools. + # Please reference to to the documentation of structured_output + # to see an up to date list of which models support + # with_structured_output. + model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0) + structured_llm = model.with_structured_output(Joke) + structured_llm.invoke( + "Why did the cat cross the road? To get to the other " + "side... and then lay down in the middle of it!" + ) + ``` - # Or any other chat model that supports tools. - # Please reference to to the documentation of structured_output - # to see an up to date list of which models support - # with_structured_output. - model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0) - structured_llm = model.with_structured_output(Joke) - structured_llm.invoke( - "Why did the cat cross the road? To get to the other " - "side... and then lay down in the middle of it!" - ) Read more here: https://python.langchain.com/docs/how_to/structured_output/ Args: diff --git a/libs/langchain/langchain_classic/chains/qa_generation/base.py b/libs/langchain/langchain_classic/chains/qa_generation/base.py index 365ab964526..df74669d045 100644 --- a/libs/langchain/langchain_classic/chains/qa_generation/base.py +++ b/libs/langchain/langchain_classic/chains/qa_generation/base.py @@ -36,34 +36,33 @@ class QAGenerationChain(Chain): - Use of JsonOutputParser supports JSONPatch operations in streaming mode, as well as robustness to markdown. - .. code-block:: python + ```python + from langchain_classic.chains.qa_generation.prompt import ( + CHAT_PROMPT as prompt, + ) - from langchain_classic.chains.qa_generation.prompt import ( - CHAT_PROMPT as prompt, - ) + # Note: import PROMPT if using a legacy non-chat model. + from langchain_core.output_parsers import JsonOutputParser + from langchain_core.runnables import ( + RunnableLambda, + RunnableParallel, + RunnablePassthrough, + ) + from langchain_core.runnables.base import RunnableEach + from langchain_openai import ChatOpenAI + from langchain_text_splitters import RecursiveCharacterTextSplitter - # Note: import PROMPT if using a legacy non-chat model. - from langchain_core.output_parsers import JsonOutputParser - from langchain_core.runnables import ( - RunnableLambda, - RunnableParallel, - RunnablePassthrough, - ) - from langchain_core.runnables.base import RunnableEach - from langchain_openai import ChatOpenAI - from langchain_text_splitters import RecursiveCharacterTextSplitter - - llm = ChatOpenAI() - text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=500) - split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) - - chain = RunnableParallel( - text=RunnablePassthrough(), - questions=( - split_text | RunnableEach(bound=prompt | llm | JsonOutputParser()) - ), - ) + llm = ChatOpenAI() + text_splitter = RecursiveCharacterTextSplitter(chunk_overlap=500) + split_text = RunnableLambda(lambda x: text_splitter.create_documents([x])) + chain = RunnableParallel( + text=RunnablePassthrough(), + questions=( + split_text | RunnableEach(bound=prompt | llm | JsonOutputParser()) + ), + ) + ``` """ llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/retrieval.py b/libs/langchain/langchain_classic/chains/retrieval.py index eb1860fefc1..8eb4ff47768 100644 --- a/libs/langchain/langchain_classic/chains/retrieval.py +++ b/libs/langchain/langchain_classic/chains/retrieval.py @@ -35,27 +35,24 @@ def create_retrieval_chain( least a `context` and `answer` key. Example: - .. code-block:: python + ```python + # pip install -U langchain langchain-community - # pip install -U langchain langchain-community + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.chains.combine_documents import ( + create_stuff_documents_chain, + ) + from langchain_classic.chains import create_retrieval_chain + from langchain_classic import hub - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.chains.combine_documents import ( - create_stuff_documents_chain, - ) - from langchain_classic.chains import create_retrieval_chain - from langchain_classic import hub - - retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat") - llm = ChatOpenAI() - retriever = ... - combine_docs_chain = create_stuff_documents_chain( - llm, retrieval_qa_chat_prompt - ) - retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain) - - retrieval_chain.invoke({"input": "..."}) + retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat") + llm = ChatOpenAI() + retriever = ... + combine_docs_chain = create_stuff_documents_chain(llm, retrieval_qa_chat_prompt) + retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain) + retrieval_chain.invoke({"input": "..."}) + ``` """ if not isinstance(retriever, BaseRetriever): retrieval_docs: Runnable[dict, RetrieverOutput] = retriever diff --git a/libs/langchain/langchain_classic/chains/retrieval_qa/base.py b/libs/langchain/langchain_classic/chains/retrieval_qa/base.py index 4277e89f731..39fdf46ff60 100644 --- a/libs/langchain/langchain_classic/chains/retrieval_qa/base.py +++ b/libs/langchain/langchain_classic/chains/retrieval_qa/base.py @@ -143,11 +143,10 @@ class BaseRetrievalQA(Chain): the retrieved documents as well under the key 'source_documents'. Example: - .. code-block:: python - - res = indexqa({'query': 'This is my query'}) - answer, docs = res['result'], res['source_documents'] - + ```python + res = indexqa({"query": "This is my query"}) + answer, docs = res["result"], res["source_documents"] + ``` """ _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager() question = inputs[self.input_key] @@ -188,11 +187,10 @@ class BaseRetrievalQA(Chain): the retrieved documents as well under the key 'source_documents'. Example: - .. code-block:: python - - res = indexqa({'query': 'This is my query'}) - answer, docs = res['result'], res['source_documents'] - + ```python + res = indexqa({"query": "This is my query"}) + answer, docs = res["result"], res["source_documents"] + ``` """ _run_manager = run_manager or AsyncCallbackManagerForChainRun.get_noop_manager() question = inputs[self.input_key] @@ -229,47 +227,46 @@ class RetrievalQA(BaseRetrievalQA): This class is deprecated. See below for an example implementation using `create_retrieval_chain`: - .. code-block:: python - - from langchain_classic.chains import create_retrieval_chain - from langchain_classic.chains.combine_documents import ( - create_stuff_documents_chain, - ) - from langchain_core.prompts import ChatPromptTemplate - from langchain_openai import ChatOpenAI + ```python + from langchain_classic.chains import create_retrieval_chain + from langchain_classic.chains.combine_documents import ( + create_stuff_documents_chain, + ) + from langchain_core.prompts import ChatPromptTemplate + from langchain_openai import ChatOpenAI - retriever = ... # Your retriever - llm = ChatOpenAI() + retriever = ... # Your retriever + llm = ChatOpenAI() - system_prompt = ( - "Use the given context to answer the question. " - "If you don't know the answer, say you don't know. " - "Use three sentence maximum and keep the answer concise. " - "Context: {context}" - ) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", system_prompt), - ("human", "{input}"), - ] - ) - question_answer_chain = create_stuff_documents_chain(llm, prompt) - chain = create_retrieval_chain(retriever, question_answer_chain) + system_prompt = ( + "Use the given context to answer the question. " + "If you don't know the answer, say you don't know. " + "Use three sentence maximum and keep the answer concise. " + "Context: {context}" + ) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", system_prompt), + ("human", "{input}"), + ] + ) + question_answer_chain = create_stuff_documents_chain(llm, prompt) + chain = create_retrieval_chain(retriever, question_answer_chain) - chain.invoke({"input": query}) + chain.invoke({"input": query}) + ``` Example: - .. code-block:: python - - from langchain_community.llms import OpenAI - from langchain_classic.chains import RetrievalQA - from langchain_community.vectorstores import FAISS - from langchain_core.vectorstores import VectorStoreRetriever - - retriever = VectorStoreRetriever(vectorstore=FAISS(...)) - retrievalQA = RetrievalQA.from_llm(llm=OpenAI(), retriever=retriever) + ```python + from langchain_community.llms import OpenAI + from langchain_classic.chains import RetrievalQA + from langchain_community.vectorstores import FAISS + from langchain_core.vectorstores import VectorStoreRetriever + retriever = VectorStoreRetriever(vectorstore=FAISS(...)) + retrievalQA = RetrievalQA.from_llm(llm=OpenAI(), retriever=retriever) + ``` """ retriever: BaseRetriever = Field(exclude=True) diff --git a/libs/langchain/langchain_classic/chains/router/llm_router.py b/libs/langchain/langchain_classic/chains/router/llm_router.py index 64e4823b8eb..6fa390a2f12 100644 --- a/libs/langchain/langchain_classic/chains/router/llm_router.py +++ b/libs/langchain/langchain_classic/chains/router/llm_router.py @@ -38,66 +38,66 @@ class LLMRouterChain(RouterChain): Below is an example implementation: - .. code-block:: python + ```python + from operator import itemgetter + from typing import Literal + from typing_extensions import TypedDict - from operator import itemgetter - from typing import Literal - from typing_extensions import TypedDict + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.runnables import RunnableLambda, RunnablePassthrough + from langchain_openai import ChatOpenAI - from langchain_core.output_parsers import StrOutputParser - from langchain_core.prompts import ChatPromptTemplate - from langchain_core.runnables import RunnableLambda, RunnablePassthrough - from langchain_openai import ChatOpenAI + llm = ChatOpenAI(model="gpt-4o-mini") - llm = ChatOpenAI(model="gpt-4o-mini") + prompt_1 = ChatPromptTemplate.from_messages( + [ + ("system", "You are an expert on animals."), + ("human", "{query}"), + ] + ) + prompt_2 = ChatPromptTemplate.from_messages( + [ + ("system", "You are an expert on vegetables."), + ("human", "{query}"), + ] + ) - prompt_1 = ChatPromptTemplate.from_messages( - [ - ("system", "You are an expert on animals."), - ("human", "{query}"), - ] - ) - prompt_2 = ChatPromptTemplate.from_messages( - [ - ("system", "You are an expert on vegetables."), - ("human", "{query}"), - ] - ) + chain_1 = prompt_1 | llm | StrOutputParser() + chain_2 = prompt_2 | llm | StrOutputParser() - chain_1 = prompt_1 | llm | StrOutputParser() - chain_2 = prompt_2 | llm | StrOutputParser() - - route_system = "Route the user's query to either the animal " - "or vegetable expert." - route_prompt = ChatPromptTemplate.from_messages( - [ - ("system", route_system), - ("human", "{query}"), - ] - ) + route_system = "Route the user's query to either the animal " + "or vegetable expert." + route_prompt = ChatPromptTemplate.from_messages( + [ + ("system", route_system), + ("human", "{query}"), + ] + ) - class RouteQuery(TypedDict): - \"\"\"Route query to destination.\"\"\" - destination: Literal["animal", "vegetable"] + class RouteQuery(TypedDict): + \"\"\"Route query to destination.\"\"\" + destination: Literal["animal", "vegetable"] - route_chain = ( - route_prompt - | llm.with_structured_output(RouteQuery) - | itemgetter("destination") - ) + route_chain = ( + route_prompt + | llm.with_structured_output(RouteQuery) + | itemgetter("destination") + ) - chain = { - "destination": route_chain, # "animal" or "vegetable" - "query": lambda x: x["query"], # pass through input query - } | RunnableLambda( - # if animal, chain_1. otherwise, chain_2. - lambda x: chain_1 if x["destination"] == "animal" else chain_2, - ) + chain = { + "destination": route_chain, # "animal" or "vegetable" + "query": lambda x: x["query"], # pass through input query + } | RunnableLambda( + # if animal, chain_1. otherwise, chain_2. + lambda x: chain_1 if x["destination"] == "animal" else chain_2, + ) - chain.invoke({"query": "what color are carrots"}) + chain.invoke({"query": "what color are carrots"}) + ``` """ llm_chain: LLMChain diff --git a/libs/langchain/langchain_classic/chains/router/multi_prompt.py b/libs/langchain/langchain_classic/chains/router/multi_prompt.py index 413f1cd58a6..2c13ec476d4 100644 --- a/libs/langchain/langchain_classic/chains/router/multi_prompt.py +++ b/libs/langchain/langchain_classic/chains/router/multi_prompt.py @@ -38,115 +38,115 @@ class MultiPromptChain(MultiRouteChain): Below is an example implementation: - .. code-block:: python + ```python + from operator import itemgetter + from typing import Literal - from operator import itemgetter - from typing import Literal + from langchain_core.output_parsers import StrOutputParser + from langchain_core.prompts import ChatPromptTemplate + from langchain_core.runnables import RunnableConfig + from langchain_openai import ChatOpenAI + from langgraph.graph import END, START, StateGraph + from typing_extensions import TypedDict - from langchain_core.output_parsers import StrOutputParser - from langchain_core.prompts import ChatPromptTemplate - from langchain_core.runnables import RunnableConfig - from langchain_openai import ChatOpenAI - from langgraph.graph import END, START, StateGraph - from typing_extensions import TypedDict + llm = ChatOpenAI(model="gpt-4o-mini") - llm = ChatOpenAI(model="gpt-4o-mini") + # Define the prompts we will route to + prompt_1 = ChatPromptTemplate.from_messages( + [ + ("system", "You are an expert on animals."), + ("human", "{input}"), + ] + ) + prompt_2 = ChatPromptTemplate.from_messages( + [ + ("system", "You are an expert on vegetables."), + ("human", "{input}"), + ] + ) - # Define the prompts we will route to - prompt_1 = ChatPromptTemplate.from_messages( - [ - ("system", "You are an expert on animals."), - ("human", "{input}"), - ] - ) - prompt_2 = ChatPromptTemplate.from_messages( - [ - ("system", "You are an expert on vegetables."), - ("human", "{input}"), - ] - ) - - # Construct the chains we will route to. These format the input query - # into the respective prompt, run it through a chat model, and cast - # the result to a string. - chain_1 = prompt_1 | llm | StrOutputParser() - chain_2 = prompt_2 | llm | StrOutputParser() + # Construct the chains we will route to. These format the input query + # into the respective prompt, run it through a chat model, and cast + # the result to a string. + chain_1 = prompt_1 | llm | StrOutputParser() + chain_2 = prompt_2 | llm | StrOutputParser() - # Next: define the chain that selects which branch to route to. - # Here we will take advantage of tool-calling features to force - # the output to select one of two desired branches. - route_system = "Route the user's query to either the animal " - "or vegetable expert." - route_prompt = ChatPromptTemplate.from_messages( - [ - ("system", route_system), - ("human", "{input}"), - ] - ) + # Next: define the chain that selects which branch to route to. + # Here we will take advantage of tool-calling features to force + # the output to select one of two desired branches. + route_system = "Route the user's query to either the animal " + "or vegetable expert." + route_prompt = ChatPromptTemplate.from_messages( + [ + ("system", route_system), + ("human", "{input}"), + ] + ) - # Define schema for output: - class RouteQuery(TypedDict): - \"\"\"Route query to destination expert.\"\"\" + # Define schema for output: + class RouteQuery(TypedDict): + \"\"\"Route query to destination expert.\"\"\" - destination: Literal["animal", "vegetable"] + destination: Literal["animal", "vegetable"] - route_chain = route_prompt | llm.with_structured_output(RouteQuery) + route_chain = route_prompt | llm.with_structured_output(RouteQuery) - # For LangGraph, we will define the state of the graph to hold the query, - # destination, and final answer. - class State(TypedDict): - query: str - destination: RouteQuery - answer: str + # For LangGraph, we will define the state of the graph to hold the query, + # destination, and final answer. + class State(TypedDict): + query: str + destination: RouteQuery + answer: str - # We define functions for each node, including routing the query: - async def route_query(state: State, config: RunnableConfig): - destination = await route_chain.ainvoke(state["query"], config) - return {"destination": destination} + # We define functions for each node, including routing the query: + async def route_query(state: State, config: RunnableConfig): + destination = await route_chain.ainvoke(state["query"], config) + return {"destination": destination} - # And one node for each prompt - async def prompt_1(state: State, config: RunnableConfig): - return {"answer": await chain_1.ainvoke(state["query"], config)} + # And one node for each prompt + async def prompt_1(state: State, config: RunnableConfig): + return {"answer": await chain_1.ainvoke(state["query"], config)} - async def prompt_2(state: State, config: RunnableConfig): - return {"answer": await chain_2.ainvoke(state["query"], config)} + async def prompt_2(state: State, config: RunnableConfig): + return {"answer": await chain_2.ainvoke(state["query"], config)} - # We then define logic that selects the prompt based on the classification - def select_node(state: State) -> Literal["prompt_1", "prompt_2"]: - if state["destination"] == "animal": - return "prompt_1" - else: - return "prompt_2" + # We then define logic that selects the prompt based on the classification + def select_node(state: State) -> Literal["prompt_1", "prompt_2"]: + if state["destination"] == "animal": + return "prompt_1" + else: + return "prompt_2" - # Finally, assemble the multi-prompt chain. This is a sequence of two steps: - # 1) Select "animal" or "vegetable" via the route_chain, and collect the - # answer alongside the input query. - # 2) Route the input query to chain_1 or chain_2, based on the - # selection. - graph = StateGraph(State) - graph.add_node("route_query", route_query) - graph.add_node("prompt_1", prompt_1) - graph.add_node("prompt_2", prompt_2) + # Finally, assemble the multi-prompt chain. This is a sequence of two steps: + # 1) Select "animal" or "vegetable" via the route_chain, and collect the + # answer alongside the input query. + # 2) Route the input query to chain_1 or chain_2, based on the + # selection. + graph = StateGraph(State) + graph.add_node("route_query", route_query) + graph.add_node("prompt_1", prompt_1) + graph.add_node("prompt_2", prompt_2) - graph.add_edge(START, "route_query") - graph.add_conditional_edges("route_query", select_node) - graph.add_edge("prompt_1", END) - graph.add_edge("prompt_2", END) - app = graph.compile() + graph.add_edge(START, "route_query") + graph.add_conditional_edges("route_query", select_node) + graph.add_edge("prompt_1", END) + graph.add_edge("prompt_2", END) + app = graph.compile() - result = await app.ainvoke({"query": "what color are carrots"}) - print(result["destination"]) - print(result["answer"]) + result = await app.ainvoke({"query": "what color are carrots"}) + print(result["destination"]) + print(result["answer"]) + ``` """ @property diff --git a/libs/langchain/langchain_classic/chains/sql_database/query.py b/libs/langchain/langchain_classic/chains/sql_database/query.py index 18b0f07e352..9b10b704b62 100644 --- a/libs/langchain/langchain_classic/chains/sql_database/query.py +++ b/libs/langchain/langchain_classic/chains/sql_database/query.py @@ -69,18 +69,17 @@ def create_sql_query_chain( that question. Example: + ```python + # pip install -U langchain langchain-community langchain-openai + from langchain_openai import ChatOpenAI + from langchain_classic.chains import create_sql_query_chain + from langchain_community.utilities import SQLDatabase - .. code-block:: python - - # pip install -U langchain langchain-community langchain-openai - from langchain_openai import ChatOpenAI - from langchain_classic.chains import create_sql_query_chain - from langchain_community.utilities import SQLDatabase - - db = SQLDatabase.from_uri("sqlite:///Chinook.db") - llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) - chain = create_sql_query_chain(llm, db) - response = chain.invoke({"question": "How many employees are there"}) + db = SQLDatabase.from_uri("sqlite:///Chinook.db") + llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) + chain = create_sql_query_chain(llm, db) + response = chain.invoke({"question": "How many employees are there"}) + ``` Prompt: If no prompt is provided, a default prompt is selected based on the SQLDatabase @@ -88,34 +87,33 @@ def create_sql_query_chain( * input: The user question plus suffix "\\nSQLQuery: " is passed here. * top_k: The number of results per select statement (the `k` argument to - this function) is passed in here. + this function) is passed in here. * table_info: Table definitions and sample rows are passed in here. If the - user specifies "table_names_to_use" when invoking chain, only those - will be included. Otherwise, all tables are included. + user specifies "table_names_to_use" when invoking chain, only those + will be included. Otherwise, all tables are included. * dialect (optional): If dialect input variable is in prompt, the db - dialect will be passed in here. + dialect will be passed in here. Here's an example prompt: - .. code-block:: python + ```python + from langchain_core.prompts import PromptTemplate - from langchain_core.prompts import PromptTemplate + template = '''Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. + Use the following format: - template = '''Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. - Use the following format: + Question: "Question here" + SQLQuery: "SQL Query to run" + SQLResult: "Result of the SQLQuery" + Answer: "Final answer here" - Question: "Question here" - SQLQuery: "SQL Query to run" - SQLResult: "Result of the SQLQuery" - Answer: "Final answer here" + Only use the following tables: - Only use the following tables: - - {table_info}. - - Question: {input}''' - prompt = PromptTemplate.from_template(template) + {table_info}. + Question: {input}''' + prompt = PromptTemplate.from_template(template) + ``` """ # noqa: E501 if prompt is not None: prompt_to_use = prompt diff --git a/libs/langchain/langchain_classic/chains/structured_output/base.py b/libs/langchain/langchain_classic/chains/structured_output/base.py index 34ab4232084..9d99cb33f9c 100644 --- a/libs/langchain/langchain_classic/chains/structured_output/base.py +++ b/libs/langchain/langchain_classic/chains/structured_output/base.py @@ -103,37 +103,37 @@ def create_openai_fn_runnable( A runnable sequence that will pass in the given functions to the model when run. Example: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_classic.chains.structured_output import create_openai_fn_runnable - from langchain_openai import ChatOpenAI - from pydantic import BaseModel, Field + from langchain_classic.chains.structured_output import create_openai_fn_runnable + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field - class RecordPerson(BaseModel): - '''Record some identifying information about a person.''' + class RecordPerson(BaseModel): + '''Record some identifying information about a person.''' - name: str = Field(..., description="The person's name") - age: int = Field(..., description="The person's age") - fav_food: str | None = Field(None, description="The person's favorite food") + name: str = Field(..., description="The person's name") + age: int = Field(..., description="The person's age") + fav_food: str | None = Field(None, description="The person's favorite food") - class RecordDog(BaseModel): - '''Record some identifying information about a dog.''' + class RecordDog(BaseModel): + '''Record some identifying information about a dog.''' - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - llm = ChatOpenAI(model="gpt-4", temperature=0) - structured_llm = create_openai_fn_runnable([RecordPerson, RecordDog], llm) - structured_llm.invoke("Harry was a chubby brown beagle who loved chicken) - # -> RecordDog(name="Harry", color="brown", fav_food="chicken") + llm = ChatOpenAI(model="gpt-4", temperature=0) + structured_llm = create_openai_fn_runnable([RecordPerson, RecordDog], llm) + structured_llm.invoke("Harry was a chubby brown beagle who loved chicken) + # -> RecordDog(name="Harry", color="brown", fav_food="chicken") - """ # noqa: E501 + ``` + """ if not functions: msg = "Need to pass in at least one function. Received zero." raise ValueError(msg) @@ -235,163 +235,164 @@ def create_structured_output_runnable( output_schema. OpenAI tools example with Pydantic schema (mode='openai-tools'): - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_classic.chains import create_structured_output_runnable - from langchain_openai import ChatOpenAI - from pydantic import BaseModel, Field + from langchain_classic.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field - class RecordDog(BaseModel): - '''Record some identifying information about a dog.''' + class RecordDog(BaseModel): + '''Record some identifying information about a dog.''' - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are an extraction algorithm. Please extract every possible instance"), - ('human', '{input}') - ] - ) - structured_llm = create_structured_output_runnable( - RecordDog, - llm, - mode="openai-tools", - enforce_function_usage=True, - return_single=True - ) - structured_llm.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) - # -> RecordDog(name="Harry", color="brown", fav_food="chicken") + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + prompt = ChatPromptTemplate.from_messages( + [ + ("system", "You are an extraction algorithm. Please extract every possible instance"), + ('human', '{input}') + ] + ) + structured_llm = create_structured_output_runnable( + RecordDog, + llm, + mode="openai-tools", + enforce_function_usage=True, + return_single=True + ) + structured_llm.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) + # -> RecordDog(name="Harry", color="brown", fav_food="chicken") + ``` OpenAI tools example with dict schema (mode="openai-tools"): - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_classic.chains import create_structured_output_runnable - from langchain_openai import ChatOpenAI + from langchain_classic.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI - dog_schema = { - "type": "function", - "function": { - "name": "record_dog", - "description": "Record some identifying information about a dog.", - "parameters": { - "type": "object", - "properties": { - "name": { - "description": "The dog's name", - "type": "string" - }, - "color": { - "description": "The dog's color", - "type": "string" - }, - "fav_food": { - "description": "The dog's favorite food", - "type": "string" - } - }, - "required": ["name", "color"] + dog_schema = { + "type": "function", + "function": { + "name": "record_dog", + "description": "Record some identifying information about a dog.", + "parameters": { + "type": "object", + "properties": { + "name": { + "description": "The dog's name", + "type": "string" + }, + "color": { + "description": "The dog's color", + "type": "string" + }, + "fav_food": { + "description": "The dog's favorite food", + "type": "string" } - } + }, + "required": ["name", "color"] } + } + } - llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) - structured_llm = create_structured_output_runnable( - dog_schema, - llm, - mode="openai-tools", - enforce_function_usage=True, - return_single=True - ) - structured_llm.invoke("Harry was a chubby brown beagle who loved chicken") - # -> {'name': 'Harry', 'color': 'brown', 'fav_food': 'chicken'} + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable( + dog_schema, + llm, + mode="openai-tools", + enforce_function_usage=True, + return_single=True + ) + structured_llm.invoke("Harry was a chubby brown beagle who loved chicken") + # -> {'name': 'Harry', 'color': 'brown', 'fav_food': 'chicken'} + ``` OpenAI functions example (mode="openai-functions"): - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from langchain_classic.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI + from pydantic import BaseModel, Field - from langchain_classic.chains import create_structured_output_runnable - from langchain_openai import ChatOpenAI - from pydantic import BaseModel, Field + class Dog(BaseModel): + '''Identifying information about a dog.''' - class Dog(BaseModel): - '''Identifying information about a dog.''' + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") - - llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) - structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") - structured_llm.invoke("Harry was a chubby brown beagle who loved chicken") - # -> Dog(name="Harry", color="brown", fav_food="chicken") + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") + structured_llm.invoke("Harry was a chubby brown beagle who loved chicken") + # -> Dog(name="Harry", color="brown", fav_food="chicken") + ``` OpenAI functions with prompt example: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from langchain_classic.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + from pydantic import BaseModel, Field - from langchain_classic.chains import create_structured_output_runnable - from langchain_openai import ChatOpenAI - from langchain_core.prompts import ChatPromptTemplate - from pydantic import BaseModel, Field + class Dog(BaseModel): + '''Identifying information about a dog.''' - class Dog(BaseModel): - '''Identifying information about a dog.''' + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") + system = '''Extract information about any dogs mentioned in the user input.''' + prompt = ChatPromptTemplate.from_messages( + [("system", system), ("human", "{input}"),] + ) + chain = prompt | structured_llm + chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) + # -> Dog(name="Harry", color="brown", fav_food="chicken") + ``` - llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) - structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") - system = '''Extract information about any dogs mentioned in the user input.''' - prompt = ChatPromptTemplate.from_messages( - [("system", system), ("human", "{input}"),] - ) - chain = prompt | structured_llm - chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) - # -> Dog(name="Harry", color="brown", fav_food="chicken") OpenAI json response format example (mode="openai-json"): - .. code-block:: python + ```python + from typing import Optional - from typing import Optional + from langchain_classic.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI + from langchain_core.prompts import ChatPromptTemplate + from pydantic import BaseModel, Field - from langchain_classic.chains import create_structured_output_runnable - from langchain_openai import ChatOpenAI - from langchain_core.prompts import ChatPromptTemplate - from pydantic import BaseModel, Field + class Dog(BaseModel): + '''Identifying information about a dog.''' - class Dog(BaseModel): - '''Identifying information about a dog.''' + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: str | None = Field(None, description="The dog's favorite food") - name: str = Field(..., description="The dog's name") - color: str = Field(..., description="The dog's color") - fav_food: str | None = Field(None, description="The dog's favorite food") + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-json") + system = '''You are a world class assistant for extracting information in structured JSON formats. \ - llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) - structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-json") - system = '''You are a world class assistant for extracting information in structured JSON formats. \ + Extract a valid JSON blob from the user input that matches the following JSON Schema: - Extract a valid JSON blob from the user input that matches the following JSON Schema: - - {output_schema}''' - prompt = ChatPromptTemplate.from_messages( - [("system", system), ("human", "{input}"),] - ) - chain = prompt | structured_llm - chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) + {output_schema}''' + prompt = ChatPromptTemplate.from_messages( + [("system", system), ("human", "{input}"),] + ) + chain = prompt | structured_llm + chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) + ``` """ # noqa: E501 # for backwards compatibility force_function_usage = kwargs.get( diff --git a/libs/langchain/langchain_classic/chains/transform.py b/libs/langchain/langchain_classic/chains/transform.py index 7926b33e634..709ec348dde 100644 --- a/libs/langchain/langchain_classic/chains/transform.py +++ b/libs/langchain/langchain_classic/chains/transform.py @@ -21,12 +21,12 @@ class TransformChain(Chain): """Chain that transforms the chain output. Example: - .. code-block:: python - - from langchain_classic.chains import TransformChain - transform_chain = TransformChain(input_variables=["text"], - output_variables["entities"], transform=func()) + ```python + from langchain_classic.chains import TransformChain + transform_chain = TransformChain(input_variables=["text"], + output_variables["entities"], transform=func()) + ``` """ input_variables: list[str] diff --git a/libs/langchain/langchain_classic/chat_loaders/__init__.py b/libs/langchain/langchain_classic/chat_loaders/__init__.py index 7547ddcecc8..941d59a4214 100644 --- a/libs/langchain/langchain_classic/chat_loaders/__init__.py +++ b/libs/langchain/langchain_classic/chat_loaders/__init__.py @@ -3,17 +3,4 @@ Load chat messages from various communications platforms such as Facebook Messenger, Telegram, and WhatsApp. The loaded chat messages can be used for fine-tuning models. - -**Class hierarchy:** - -.. code-block:: - - BaseChatLoader --> ChatLoader # Examples: WhatsAppChatLoader, IMessageChatLoader - -**Main helpers:** - -.. code-block:: - - ChatSession - -""" # noqa: E501 +""" diff --git a/libs/langchain/langchain_classic/chat_models/__init__.py b/libs/langchain/langchain_classic/chat_models/__init__.py index 914a035f6df..640d77a0fa4 100644 --- a/libs/langchain/langchain_classic/chat_models/__init__.py +++ b/libs/langchain/langchain_classic/chat_models/__init__.py @@ -3,19 +3,7 @@ While Chat Models use language models under the hood, the interface they expose is a bit different. Rather than expose a "text in, text out" API, they expose an interface where "chat messages" are the inputs and outputs. - -**Class hierarchy:** - -.. code-block:: - - BaseLanguageModel --> BaseChatModel --> # Examples: ChatOpenAI, ChatGooglePalm - -**Main helpers:** - -.. code-block:: - - AIMessage, BaseMessage, HumanMessage -""" # noqa: E501 +""" import warnings diff --git a/libs/langchain/langchain_classic/chat_models/base.py b/libs/langchain/langchain_classic/chat_models/base.py index 4b5f5b2a4b2..20a888e9166 100644 --- a/libs/langchain/langchain_classic/chat_models/base.py +++ b/libs/langchain/langchain_classic/chat_models/base.py @@ -170,121 +170,120 @@ def init_chat_model( ???+ note "Init non-configurable model" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic langchain-google-vertexai + from langchain_classic.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic langchain-google-vertexai - from langchain_classic.chat_models import init_chat_model - - o3_mini = init_chat_model("openai:o3-mini", temperature=0) - claude_sonnet = init_chat_model( - "anthropic:claude-3-5-sonnet-latest", temperature=0 - ) - gemini_2_flash = init_chat_model( - "google_vertexai:gemini-2.5-flash", temperature=0 - ) - - o3_mini.invoke("what's your name") - claude_sonnet.invoke("what's your name") - gemini_2_flash.invoke("what's your name") + o3_mini = init_chat_model("openai:o3-mini", temperature=0) + claude_sonnet = init_chat_model( + "anthropic:claude-3-5-sonnet-latest", temperature=0 + ) + gemini_2_flash = init_chat_model( + "google_vertexai:gemini-2.5-flash", temperature=0 + ) + o3_mini.invoke("what's your name") + claude_sonnet.invoke("what's your name") + gemini_2_flash.invoke("what's your name") + ``` ??? note "Partially configurable model with no default" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain_classic.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic - from langchain_classic.chat_models import init_chat_model + # We don't need to specify configurable=True if a model isn't specified. + configurable_model = init_chat_model(temperature=0) - # We don't need to specify configurable=True if a model isn't specified. - configurable_model = init_chat_model(temperature=0) + configurable_model.invoke( + "what's your name", config={"configurable": {"model": "gpt-4o"}} + ) + # GPT-4o response - configurable_model.invoke( - "what's your name", config={"configurable": {"model": "gpt-4o"}} - ) - # GPT-4o response - - configurable_model.invoke( - "what's your name", - config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, - ) - # claude-3.5 sonnet response + configurable_model.invoke( + "what's your name", + config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, + ) + # claude-3.5 sonnet response + ``` ??? note "Fully configurable model with a default" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain_classic.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic - from langchain_classic.chat_models import init_chat_model + configurable_model_with_default = init_chat_model( + "openai:gpt-4o", + configurable_fields="any", # this allows us to configure other params like temperature, max_tokens, etc at runtime. + config_prefix="foo", + temperature=0, + ) - configurable_model_with_default = init_chat_model( - "openai:gpt-4o", - configurable_fields="any", # this allows us to configure other params like temperature, max_tokens, etc at runtime. - config_prefix="foo", - temperature=0, - ) + configurable_model_with_default.invoke("what's your name") + # GPT-4o response with temperature 0 - configurable_model_with_default.invoke("what's your name") - # GPT-4o response with temperature 0 - - configurable_model_with_default.invoke( - "what's your name", - config={ - "configurable": { - "foo_model": "anthropic:claude-3-5-sonnet-latest", - "foo_temperature": 0.6, - } - }, - ) - # Claude-3.5 sonnet response with temperature 0.6 + configurable_model_with_default.invoke( + "what's your name", + config={ + "configurable": { + "foo_model": "anthropic:claude-3-5-sonnet-latest", + "foo_temperature": 0.6, + } + }, + ) + # Claude-3.5 sonnet response with temperature 0.6 + ``` ??? note "Bind tools to a configurable model" You can call any ChatModel declarative methods on a configurable model in the same way that you would with a normal model. - .. code-block:: python - - # pip install langchain langchain-openai langchain-anthropic - from langchain_classic.chat_models import init_chat_model - from pydantic import BaseModel, Field + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain_classic.chat_models import init_chat_model + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field( - ..., description="The city and state, e.g. San Francisco, CA" - ) - - - class GetPopulation(BaseModel): - '''Get the current population in a given location''' - - location: str = Field( - ..., description="The city and state, e.g. San Francisco, CA" - ) - - - configurable_model = init_chat_model( - "gpt-4o", configurable_fields=("model", "model_provider"), temperature=0 + location: str = Field( + ..., description="The city and state, e.g. San Francisco, CA" ) - configurable_model_with_tools = configurable_model.bind_tools( - [ - GetWeather, - GetPopulation, - ] - ) - configurable_model_with_tools.invoke( - "Which city is hotter today and which is bigger: LA or NY?" - ) - # GPT-4o response with tool calls - configurable_model_with_tools.invoke( - "Which city is hotter today and which is bigger: LA or NY?", - config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, + class GetPopulation(BaseModel): + '''Get the current population in a given location''' + + location: str = Field( + ..., description="The city and state, e.g. San Francisco, CA" ) - # Claude-3.5 sonnet response with tools + + + configurable_model = init_chat_model( + "gpt-4o", configurable_fields=("model", "model_provider"), temperature=0 + ) + + configurable_model_with_tools = configurable_model.bind_tools( + [ + GetWeather, + GetPopulation, + ] + ) + configurable_model_with_tools.invoke( + "Which city is hotter today and which is bigger: LA or NY?" + ) + # GPT-4o response with tool calls + + configurable_model_with_tools.invoke( + "Which city is hotter today and which is bigger: LA or NY?", + config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, + ) + # Claude-3.5 sonnet response with tools + ``` !!! version-added "Added in version 0.2.7" diff --git a/libs/langchain/langchain_classic/docstore/__init__.py b/libs/langchain/langchain_classic/docstore/__init__.py index c9014c6bca3..101255848b7 100644 --- a/libs/langchain/langchain_classic/docstore/__init__.py +++ b/libs/langchain/langchain_classic/docstore/__init__.py @@ -1,18 +1,6 @@ """**Docstores** are classes to store and load Documents. The **Docstore** is a simplified version of the Document Loader. - -**Class hierarchy:** - -.. code-block:: - - Docstore --> # Examples: InMemoryDocstore, Wikipedia - -**Main helpers:** - -.. code-block:: - - Document, AddableMixin """ from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/document_loaders/__init__.py b/libs/langchain/langchain_classic/document_loaders/__init__.py index b6a2eaf4caa..58ae5d5342d 100644 --- a/libs/langchain/langchain_classic/document_loaders/__init__.py +++ b/libs/langchain/langchain_classic/document_loaders/__init__.py @@ -1,18 +1,6 @@ """**Document Loaders** are classes to load Documents. **Document Loaders** are usually used to load a lot of Documents in a single run. - -**Class hierarchy:** - -.. code-block:: - - BaseLoader --> Loader # Examples: TextLoader, UnstructuredFileLoader - -**Main helpers:** - -.. code-block:: - - Document, TextSplitter """ from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/document_transformers/__init__.py b/libs/langchain/langchain_classic/document_transformers/__init__.py index 96f45e9eda1..20e3460c70e 100644 --- a/libs/langchain/langchain_classic/document_transformers/__init__.py +++ b/libs/langchain/langchain_classic/document_transformers/__init__.py @@ -1,19 +1,7 @@ """**Document Transformers** are classes to transform Documents. **Document Transformers** usually used to transform a lot of Documents in a single run. - -**Class hierarchy:** - -.. code-block:: - - BaseDocumentTransformer --> # Examples: DoctranQATransformer, DoctranTextTranslator - -**Main helpers:** - -.. code-block:: - - Document -""" # noqa: E501 +""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/embeddings/__init__.py b/libs/langchain/langchain_classic/embeddings/__init__.py index e4027dd0711..c10293c4574 100644 --- a/libs/langchain/langchain_classic/embeddings/__init__.py +++ b/libs/langchain/langchain_classic/embeddings/__init__.py @@ -4,12 +4,6 @@ from different APIs and services. Embedding models can be LLMs or not. - -**Class hierarchy:** - -.. code-block:: - - Embeddings --> Embeddings # Examples: OpenAIEmbeddings, HuggingFaceEmbeddings """ import logging diff --git a/libs/langchain/langchain_classic/embeddings/base.py b/libs/langchain/langchain_classic/embeddings/base.py index 887a3c33efe..d7f4f8021eb 100644 --- a/libs/langchain/langchain_classic/embeddings/base.py +++ b/libs/langchain/langchain_classic/embeddings/base.py @@ -36,13 +36,13 @@ def _parse_model_string(model_name: str) -> tuple[str, str]: Returns: A tuple of (provider, model_name) - .. code-block:: python + ```python + _parse_model_string("openai:text-embedding-3-small") + # Returns: ("openai", "text-embedding-3-small") - _parse_model_string("openai:text-embedding-3-small") - # Returns: ("openai", "text-embedding-3-small") - - _parse_model_string("bedrock:amazon.titan-embed-text-v1") - # Returns: ("bedrock", "amazon.titan-embed-text-v1") + _parse_model_string("bedrock:amazon.titan-embed-text-v1") + # Returns: ("bedrock", "amazon.titan-embed-text-v1") + ``` Raises: ValueError: If the model string is not in the correct format or @@ -132,8 +132,9 @@ def init_embeddings( ) -> Embeddings | Runnable[Any, list[float]]: """Initialize an embeddings model from a model name and optional provider. - **Note:** Must have the integration package corresponding to the model provider - installed. + !!! note + Must have the integration package corresponding to the model provider + installed. Args: model: Name of the model to use. Can be either: @@ -157,18 +158,18 @@ def init_embeddings( ???+ note "Example Usage" - .. code-block:: python + ```python + # Using a model string + model = init_embeddings("openai:text-embedding-3-small") + model.embed_query("Hello, world!") - # Using a model string - model = init_embeddings("openai:text-embedding-3-small") - model.embed_query("Hello, world!") + # Using explicit provider + model = init_embeddings(model="text-embedding-3-small", provider="openai") + model.embed_documents(["Hello, world!", "Goodbye, world!"]) - # Using explicit provider - model = init_embeddings(model="text-embedding-3-small", provider="openai") - model.embed_documents(["Hello, world!", "Goodbye, world!"]) - - # With additional parameters - model = init_embeddings("openai:text-embedding-3-small", api_key="sk-...") + # With additional parameters + model = init_embeddings("openai:text-embedding-3-small", api_key="sk-...") + ``` !!! version-added "Added in version 0.3.9" diff --git a/libs/langchain/langchain_classic/embeddings/cache.py b/libs/langchain/langchain_classic/embeddings/cache.py index 50e34fc05e2..98f6cf7376e 100644 --- a/libs/langchain/langchain_classic/embeddings/cache.py +++ b/libs/langchain/langchain_classic/embeddings/cache.py @@ -119,24 +119,24 @@ class CacheBackedEmbeddings(Embeddings): embeddings too, pass in a query_embedding_store to constructor. Examples: - .. code-block: python + ```python + from langchain_classic.embeddings import CacheBackedEmbeddings + from langchain_classic.storage import LocalFileStore + from langchain_community.embeddings import OpenAIEmbeddings - from langchain_classic.embeddings import CacheBackedEmbeddings - from langchain_classic.storage import LocalFileStore - from langchain_community.embeddings import OpenAIEmbeddings + store = LocalFileStore("./my_cache") - store = LocalFileStore('./my_cache') + underlying_embedder = OpenAIEmbeddings() + embedder = CacheBackedEmbeddings.from_bytes_store( + underlying_embedder, store, namespace=underlying_embedder.model + ) - underlying_embedder = OpenAIEmbeddings() - embedder = CacheBackedEmbeddings.from_bytes_store( - underlying_embedder, store, namespace=underlying_embedder.model - ) + # Embedding is computed and cached + embeddings = embedder.embed_documents(["hello", "goodbye"]) - # Embedding is computed and cached - embeddings = embedder.embed_documents(["hello", "goodbye"]) - - # Embeddings are retrieved from the cache, no computation is done - embeddings = embedder.embed_documents(["hello", "goodbye"]) + # Embeddings are retrieved from the cache, no computation is done + embeddings = embedder.embed_documents(["hello", "goodbye"]) + ``` """ def __init__( diff --git a/libs/langchain/langchain_classic/evaluation/__init__.py b/libs/langchain/langchain_classic/evaluation/__init__.py index a06ff31d1cb..fe33506ff22 100644 --- a/libs/langchain/langchain_classic/evaluation/__init__.py +++ b/libs/langchain/langchain_classic/evaluation/__init__.py @@ -9,16 +9,16 @@ To load an evaluator, you can use the `load_evaluators ` functions with the names of the evaluators to load. -.. code-block:: python +```python +from langchain_classic.evaluation import load_evaluator - from langchain_classic.evaluation import load_evaluator - - evaluator = load_evaluator("qa") - evaluator.evaluate_strings( - prediction="We sold more than 40,000 units last week", - input="How many units did we sell last week?", - reference="We sold 32,378 units", - ) +evaluator = load_evaluator("qa") +evaluator.evaluate_strings( + prediction="We sold more than 40,000 units last week", + input="How many units did we sell last week?", + reference="We sold 32,378 units", +) +``` The evaluator must be one of `EvaluatorType `. @@ -27,11 +27,11 @@ The evaluator must be one of `EvaluatorType ` function with the name of the dataset to load. -.. code-block:: python +```python +from langchain_classic.evaluation import load_dataset - from langchain_classic.evaluation import load_dataset - - ds = load_dataset("llm-math") +ds = load_dataset("llm-math") +``` **Some common use cases for evaluation include:** diff --git a/libs/langchain/langchain_classic/evaluation/agents/trajectory_eval_chain.py b/libs/langchain/langchain_classic/evaluation/agents/trajectory_eval_chain.py index 412b3f37627..e4cd9410d1f 100644 --- a/libs/langchain/langchain_classic/evaluation/agents/trajectory_eval_chain.py +++ b/libs/langchain/langchain_classic/evaluation/agents/trajectory_eval_chain.py @@ -57,14 +57,14 @@ class TrajectoryOutputParser(BaseOutputParser): """Parse the output text and extract the score and reasoning. Args: - text (str): The output text to parse. + text: The output text to parse. Returns: - TrajectoryEval: A named tuple containing the normalized score and reasoning. + A named tuple containing the normalized score and reasoning. Raises: - OutputParserException: If the score is not found in the output text or - if the LLM's score is not a digit in the range 1-5. + If the score is not found in the output text or if the LLM's score is not a + digit in the range 1-5. """ if "Score:" not in text: msg = f"Could not find score in model eval output: {text}" @@ -102,43 +102,42 @@ class TrajectoryEvalChain(AgentTrajectoryEvaluator, LLMEvalChain): (https://arxiv.org/abs/2210.03629) Example: + ```python + from langchain_classic.agents import AgentType, initialize_agent + from langchain_community.chat_models import ChatOpenAI + from langchain_classic.evaluation import TrajectoryEvalChain + from langchain_classic.tools import tool - .. code-block:: python + @tool + def geography_answers(country: str, question: str) -> str: + \"\"\"Very helpful answers to geography questions.\"\"\" + return f"{country}? IDK - We may never know {question}." - from langchain_classic.agents import AgentType, initialize_agent - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.evaluation import TrajectoryEvalChain - from langchain_classic.tools import tool + llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) + agent = initialize_agent( + tools=[geography_answers], + llm=llm, + agent=AgentType.OPENAI_FUNCTIONS, + return_intermediate_steps=True, + ) - @tool - def geography_answers(country: str, question: str) -> str: - \"\"\"Very helpful answers to geography questions.\"\"\" - return f"{country}? IDK - We may never know {question}." + question = "How many dwell in the largest minor region in Argentina?" + response = agent(question) - llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0) - agent = initialize_agent( - tools=[geography_answers], - llm=llm, - agent=AgentType.OPENAI_FUNCTIONS, - return_intermediate_steps=True, - ) + eval_chain = TrajectoryEvalChain.from_llm( + llm=llm, agent_tools=[geography_answers], return_reasoning=True + ) - question = "How many dwell in the largest minor region in Argentina?" - response = agent(question) - - eval_chain = TrajectoryEvalChain.from_llm( - llm=llm, agent_tools=[geography_answers], return_reasoning=True - ) - - result = eval_chain.evaluate_agent_trajectory( - input=question, - agent_trajectory=response["intermediate_steps"], - prediction=response["output"], - reference="Paris", - ) - print(result["score"]) # noqa: T201 - # 0 + result = eval_chain.evaluate_agent_trajectory( + input=question, + agent_trajectory=response["intermediate_steps"], + prediction=response["output"], + reference="Paris", + ) + print(result["score"]) # noqa: T201 + # 0 + ``` """ agent_tools: list[BaseTool] | None = None @@ -208,10 +207,10 @@ Tool output: {output}""" """Format the reference text. Args: - reference (str): The reference text. + reference: The reference text. Returns: - str: The formatted reference text. + The formatted reference text. """ if not reference: return "" diff --git a/libs/langchain/langchain_classic/evaluation/comparison/eval_chain.py b/libs/langchain/langchain_classic/evaluation/comparison/eval_chain.py index e35dfec3b6e..51ef0838cb9 100644 --- a/libs/langchain/langchain_classic/evaluation/comparison/eval_chain.py +++ b/libs/langchain/langchain_classic/evaluation/comparison/eval_chain.py @@ -100,7 +100,7 @@ class PairwiseStringResultOutputParser(BaseOutputParser[dict]): """A parser for the output of the PairwiseStringEvalChain. Attributes: - _type (str): The type of the output parser. + _type: The type of the output parser. """ @@ -109,7 +109,7 @@ class PairwiseStringResultOutputParser(BaseOutputParser[dict]): """Return the type of the output parser. Returns: - str: The type of the output parser. + The type of the output parser. """ return "pairwise_string_result" @@ -118,10 +118,10 @@ class PairwiseStringResultOutputParser(BaseOutputParser[dict]): """Parse the output text. Args: - text (str): The output text to parse. + text: The output text to parse. Returns: - Dict: The parsed output. + The parsed output. Raises: ValueError: If the verdict is invalid. @@ -207,7 +207,7 @@ class PairwiseStringEvalChain(PairwiseStringEvaluator, LLMEvalChain, LLMChain): """Return whether the chain requires a reference. Returns: - bool: True if the chain requires a reference, False otherwise. + True if the chain requires a reference, False otherwise. """ return False @@ -290,13 +290,13 @@ Performance may be significantly worse with other models.", """Prepare the input for the chain. Args: - prediction (str): The output string from the first model. - prediction_b (str): The output string from the second model. - input_ (str, optional): The input or task string. - reference (str, optional): The reference string, if any. + prediction: The output string from the first model. + prediction_b: The output string from the second model. + input_: The input or task string. + reference: The reference string, if any. Returns: - dict: The prepared input for the chain. + The prepared input for the chain. """ input_dict = { diff --git a/libs/langchain/langchain_classic/evaluation/criteria/eval_chain.py b/libs/langchain/langchain_classic/evaluation/criteria/eval_chain.py index 210f915800f..80d964c451f 100644 --- a/libs/langchain/langchain_classic/evaluation/criteria/eval_chain.py +++ b/libs/langchain/langchain_classic/evaluation/criteria/eval_chain.py @@ -74,10 +74,10 @@ class CriteriaResultOutputParser(BaseOutputParser[dict]): """Parse the output text. Args: - text (str): The output text to parse. + text: The output text to parse. Returns: - Dict: The parsed output. + The parsed output. """ verdict = None score = None diff --git a/libs/langchain/langchain_classic/evaluation/loading.py b/libs/langchain/langchain_classic/evaluation/loading.py index 7a47a1914a1..8209db60815 100644 --- a/libs/langchain/langchain_classic/evaluation/loading.py +++ b/libs/langchain/langchain_classic/evaluation/loading.py @@ -60,18 +60,17 @@ def load_dataset(uri: str) -> list[dict]: **Prerequisites** - .. code-block:: shell - - pip install datasets + ```bash + pip install datasets + ``` Examples: -------- - .. code-block:: python - - from langchain_classic.evaluation import load_dataset - - ds = load_dataset("llm-math") + ```python + from langchain_classic.evaluation import load_dataset + ds = load_dataset("llm-math") + ``` """ try: from datasets import load_dataset diff --git a/libs/langchain/langchain_classic/evaluation/parsing/base.py b/libs/langchain/langchain_classic/evaluation/parsing/base.py index 33d78678080..98141790096 100644 --- a/libs/langchain/langchain_classic/evaluation/parsing/base.py +++ b/libs/langchain/langchain_classic/evaluation/parsing/base.py @@ -21,11 +21,11 @@ class JsonValidityEvaluator(StringEvaluator): require any input or reference. Attributes: - requires_input (bool): Whether this evaluator requires an input + requires_input: Whether this evaluator requires an input string. Always False. - requires_reference (bool): Whether this evaluator requires a + requires_reference: Whether this evaluator requires a reference string. Always False. - evaluation_name (str): The name of the evaluation metric. + evaluation_name: The name of the evaluation metric. Always "json". Examples: @@ -98,11 +98,11 @@ class JsonEqualityEvaluator(StringEvaluator): which is also parsed as JSON. It does not require an input string. Attributes: - requires_input (bool): Whether this evaluator requires an + requires_input: Whether this evaluator requires an input string. Always False. - requires_reference (bool): Whether this evaluator requires + requires_reference: Whether this evaluator requires a reference string. Always True. - evaluation_name (str): The name of the evaluation metric. + evaluation_name: The name of the evaluation metric. Always "parsed_equality". Examples: diff --git a/libs/langchain/langchain_classic/evaluation/parsing/json_schema.py b/libs/langchain/langchain_classic/evaluation/parsing/json_schema.py index ed533339a71..2f5a20c2fb1 100644 --- a/libs/langchain/langchain_classic/evaluation/parsing/json_schema.py +++ b/libs/langchain/langchain_classic/evaluation/parsing/json_schema.py @@ -13,9 +13,9 @@ class JsonSchemaEvaluator(StringEvaluator): If the prediction is valid, the score is True (no errors). Otherwise, the score is False (error occurred). Attributes: - requires_input (bool): Whether the evaluator requires input. - requires_reference (bool): Whether the evaluator requires reference. - evaluation_name (str): The name of the evaluation. + requires_input: Whether the evaluator requires input. + requires_reference: Whether the evaluator requires reference. + evaluation_name: The name of the evaluation. Examples: evaluator = JsonSchemaEvaluator() diff --git a/libs/langchain/langchain_classic/evaluation/qa/eval_chain.py b/libs/langchain/langchain_classic/evaluation/qa/eval_chain.py index 1a2d8a7681a..189a2f53212 100644 --- a/libs/langchain/langchain_classic/evaluation/qa/eval_chain.py +++ b/libs/langchain/langchain_classic/evaluation/qa/eval_chain.py @@ -56,10 +56,10 @@ def _parse_string_eval_output(text: str) -> dict: """Parse the output text. Args: - text (str): The output text to parse. + text: The output text to parse. Returns: - Any: The parsed output. + The parsed output. """ reasoning = text.strip() parsed_scores = _get_score(reasoning) @@ -177,17 +177,15 @@ class QAEvalChain(LLMChain, StringEvaluator, LLMEvalChain): """Evaluate Chain or LLM output, based on optional input and label. Args: - prediction (str): the LLM or chain prediction to evaluate. - reference (str | None, optional): the reference label - to evaluate against. - input (str | None, optional): the input to consider during evaluation - callbacks (Callbacks, optional): the callbacks to use for tracing. - include_run_info (bool, optional): whether to include run info in the - returned results. - **kwargs: additional keyword arguments, including callbacks, tags, etc. + prediction: The LLM or chain prediction to evaluate. + reference: The reference label to evaluate against. + input: The input to consider during evaluation + callbacks: The callbacks to use for tracing. + include_run_info: Whether to include run info in the returned results. + **kwargs: Additional keyword arguments, including callbacks, tags, etc. Returns: - dict: The evaluation results containing the score or value. + The evaluation results containing the score or value. """ result = self( { diff --git a/libs/langchain/langchain_classic/evaluation/scoring/eval_chain.py b/libs/langchain/langchain_classic/evaluation/scoring/eval_chain.py index 61b12eaf967..cbc01d75bdf 100644 --- a/libs/langchain/langchain_classic/evaluation/scoring/eval_chain.py +++ b/libs/langchain/langchain_classic/evaluation/scoring/eval_chain.py @@ -101,7 +101,7 @@ class ScoreStringResultOutputParser(BaseOutputParser[dict]): """A parser for the output of the ScoreStringEvalChain. Attributes: - _type (str): The type of the output parser. + _type: The type of the output parser. """ @@ -110,7 +110,7 @@ class ScoreStringResultOutputParser(BaseOutputParser[dict]): """Return the type of the output parser. Returns: - str: The type of the output parser. + The type of the output parser. """ return "pairwise_string_result" @@ -119,10 +119,10 @@ class ScoreStringResultOutputParser(BaseOutputParser[dict]): """Parse the output text. Args: - text (str): The output text to parse. + text: The output text to parse. Returns: - Dict: The parsed output. + The parsed output. Raises: ValueError: If the verdict is invalid. @@ -302,13 +302,13 @@ Performance may be significantly worse with other models.", """Prepare the input for the chain. Args: - prediction (str): The output string from the first model. - prediction_b (str): The output string from the second model. - input_ (str, optional): The input or task string. - reference (str, optional): The reference string, if any. + prediction: The output string from the first model. + prediction_b: The output string from the second model. + input_: The input or task string. + reference: The reference string, if any. Returns: - dict: The prepared input for the chain. + The prepared input for the chain. """ input_dict = { diff --git a/libs/langchain/langchain_classic/evaluation/string_distance/base.py b/libs/langchain/langchain_classic/evaluation/string_distance/base.py index 24136e3be0d..e8d5c06fa9a 100644 --- a/libs/langchain/langchain_classic/evaluation/string_distance/base.py +++ b/libs/langchain/langchain_classic/evaluation/string_distance/base.py @@ -153,11 +153,11 @@ class _RapidFuzzChainMixin(Chain): """Compute the distance between two strings. Args: - a (str): The first string. - b (str): The second string. + a: The first string. + b: The second string. Returns: - float: The distance between the two strings. + The distance between the two strings. """ return self.metric(a, b) diff --git a/libs/langchain/langchain_classic/llms/__init__.py b/libs/langchain/langchain_classic/llms/__init__.py index 0d5fc5b8d14..d0691634ede 100644 --- a/libs/langchain/langchain_classic/llms/__init__.py +++ b/libs/langchain/langchain_classic/llms/__init__.py @@ -1,22 +1,7 @@ """**LLMs**. **LLM** classes provide access to the large language model (**LLM**) APIs and services. - -**Class hierarchy:** - -.. code-block:: - - BaseLanguageModel --> BaseLLM --> LLM --> # Examples: AI21, HuggingFaceHub, OpenAI - -**Main helpers:** - -.. code-block:: - - LLMResult, PromptValue, - CallbackManagerForLLMRun, AsyncCallbackManagerForLLMRun, - CallbackManager, AsyncCallbackManager, - AIMessage, BaseMessage -""" # noqa: E501 +""" import warnings from collections.abc import Callable diff --git a/libs/langchain/langchain_classic/memory/__init__.py b/libs/langchain/langchain_classic/memory/__init__.py index abd97a96d82..1d3a718d7ea 100644 --- a/libs/langchain/langchain_classic/memory/__init__.py +++ b/libs/langchain/langchain_classic/memory/__init__.py @@ -1,31 +1,4 @@ -"""**Memory** maintains Chain state, incorporating context from past runs. - -**Class hierarchy for Memory:** - -.. code-block:: - - BaseMemory --> BaseChatMemory --> Memory # Examples: ZepMemory, MotorheadMemory - -**Main helpers:** - -.. code-block:: - - BaseChatMessageHistory - -**Chat Message History** stores the chat message history in different stores. - -**Class hierarchy for ChatMessageHistory:** - -.. code-block:: - - BaseChatMessageHistory --> ChatMessageHistory # Example: ZepChatMessageHistory - -**Main helpers:** - -.. code-block:: - - AIMessage, BaseMessage, HumanMessage -""" # noqa: E501 +"""**Memory** maintains Chain state, incorporating context from past runs.""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/memory/vectorstore_token_buffer_memory.py b/libs/langchain/langchain_classic/memory/vectorstore_token_buffer_memory.py index 525b322b472..ede7673f9a5 100644 --- a/libs/langchain/langchain_classic/memory/vectorstore_token_buffer_memory.py +++ b/libs/langchain/langchain_classic/memory/vectorstore_token_buffer_memory.py @@ -64,47 +64,44 @@ class ConversationVectorStoreTokenBufferMemory(ConversationTokenBufferMemory): Example using ChromaDB: - .. code-block:: python + ```python + from langchain_classic.memory.token_buffer_vectorstore_memory import ( + ConversationVectorStoreTokenBufferMemory, + ) + from langchain_chroma import Chroma + from langchain_community.embeddings import HuggingFaceInstructEmbeddings + from langchain_openai import OpenAI - from langchain_classic.memory.token_buffer_vectorstore_memory import ( - ConversationVectorStoreTokenBufferMemory, - ) - from langchain_chroma import Chroma - from langchain_community.embeddings import HuggingFaceInstructEmbeddings - from langchain_openai import OpenAI + embedder = HuggingFaceInstructEmbeddings( + query_instruction="Represent the query for retrieval: " + ) + chroma = Chroma( + collection_name="demo", + embedding_function=embedder, + collection_metadata={"hnsw:space": "cosine"}, + ) - embedder = HuggingFaceInstructEmbeddings( - query_instruction="Represent the query for retrieval: " - ) - chroma = Chroma( - collection_name="demo", - embedding_function=embedder, - collection_metadata={"hnsw:space": "cosine"}, - ) + retriever = chroma.as_retriever( + search_type="similarity_score_threshold", + search_kwargs={ + "k": 5, + "score_threshold": 0.75, + }, + ) - retriever = chroma.as_retriever( - search_type="similarity_score_threshold", - search_kwargs={ - "k": 5, - "score_threshold": 0.75, - }, - ) - - conversation_memory = ConversationVectorStoreTokenBufferMemory( - return_messages=True, - llm=OpenAI(), - retriever=retriever, - max_token_limit=1000, - ) - - conversation_memory.save_context( - {"Human": "Hi there"}, {"AI": "Nice to meet you!"} - ) - conversation_memory.save_context( - {"Human": "Nice day isn't it?"}, {"AI": "I love Wednesdays."} - ) - conversation_memory.load_memory_variables({"input": "What time is it?"}) + conversation_memory = ConversationVectorStoreTokenBufferMemory( + return_messages=True, + llm=OpenAI(), + retriever=retriever, + max_token_limit=1000, + ) + conversation_memory.save_context({"Human": "Hi there"}, {"AI": "Nice to meet you!"}) + conversation_memory.save_context( + {"Human": "Nice day isn't it?"}, {"AI": "I love Wednesdays."} + ) + conversation_memory.load_memory_variables({"input": "What time is it?"}) + ``` """ retriever: VectorStoreRetriever = Field(exclude=True) diff --git a/libs/langchain/langchain_classic/output_parsers/__init__.py b/libs/langchain/langchain_classic/output_parsers/__init__.py index cfa1d6e44de..900c7f07cd7 100644 --- a/libs/langchain/langchain_classic/output_parsers/__init__.py +++ b/libs/langchain/langchain_classic/output_parsers/__init__.py @@ -1,17 +1,4 @@ -"""**OutputParser** classes parse the output of an LLM call. - -**Class hierarchy:** - -.. code-block:: - - BaseLLMOutputParser --> BaseOutputParser --> OutputParser # ListOutputParser, PydanticOutputParser - -**Main helpers:** - -.. code-block:: - - Serializable, Generation, PromptValue -""" # noqa: E501 +"""**OutputParser** classes parse the output of an LLM call.""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/prompts/__init__.py b/libs/langchain/langchain_classic/prompts/__init__.py index f3b4ad4b362..62c703d7a03 100644 --- a/libs/langchain/langchain_classic/prompts/__init__.py +++ b/libs/langchain/langchain_classic/prompts/__init__.py @@ -1,31 +1,9 @@ """**Prompt** is the input to the model. Prompt is often constructed -from multiple components. Prompt classes and functions make constructing - and working with prompts easy. - -**Class hierarchy:** - -.. code-block:: - - BasePromptTemplate --> StringPromptTemplate --> PromptTemplate - FewShotPromptTemplate - FewShotPromptWithTemplates - BaseChatPromptTemplate --> AutoGPTPrompt - ChatPromptTemplate --> AgentScratchPadChatPromptTemplate - - - - BaseMessagePromptTemplate --> MessagesPlaceholder - BaseStringMessagePromptTemplate --> ChatMessagePromptTemplate - HumanMessagePromptTemplate - AIMessagePromptTemplate - SystemMessagePromptTemplate - - PromptValue --> StringPromptValue - ChatPromptValue - -""" # noqa: E501 +from multiple components. Prompt classes and functions make constructing and working +with prompts easy. +""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/retrievers/__init__.py b/libs/langchain/langchain_classic/retrievers/__init__.py index 440e9841973..68449d14204 100644 --- a/libs/langchain/langchain_classic/retrievers/__init__.py +++ b/libs/langchain/langchain_classic/retrievers/__init__.py @@ -3,19 +3,6 @@ It is more general than a vector store. A retriever does not need to be able to store documents, only to return (or retrieve) it. Vector stores can be used as the backbone of a retriever, but there are other types of retrievers as well. - -**Class hierarchy:** - -.. code-block:: - - BaseRetriever --> Retriever # Examples: ArxivRetriever, MergerRetriever - -**Main helpers:** - -.. code-block:: - - Document, Serializable, Callbacks, - CallbackManagerForRetrieverRun, AsyncCallbackManagerForRetrieverRun """ from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/langchain_classic/retrievers/document_compressors/listwise_rerank.py b/libs/langchain/langchain_classic/retrievers/document_compressors/listwise_rerank.py index 10fd973060f..d12f2cb925e 100644 --- a/libs/langchain/langchain_classic/retrievers/document_compressors/listwise_rerank.py +++ b/libs/langchain/langchain_classic/retrievers/document_compressors/listwise_rerank.py @@ -45,33 +45,33 @@ class LLMListwiseRerank(BaseDocumentCompressor): `LLMListwiseRerank` uses a language model to rerank a list of documents based on their relevance to a query. - **NOTE**: requires that underlying model implement `with_structured_output`. + !!! note + Requires that underlying model implement `with_structured_output`. Example usage: - .. code-block:: python + ```python + from langchain_classic.retrievers.document_compressors.listwise_rerank import ( + LLMListwiseRerank, + ) + from langchain_core.documents import Document + from langchain_openai import ChatOpenAI - from langchain_classic.retrievers.document_compressors.listwise_rerank import ( - LLMListwiseRerank, - ) - from langchain_core.documents import Document - from langchain_openai import ChatOpenAI + documents = [ + Document("Sally is my friend from school"), + Document("Steve is my friend from home"), + Document("I didn't always like yogurt"), + Document("I wonder why it's called football"), + Document("Where's waldo"), + ] - documents = [ - Document("Sally is my friend from school"), - Document("Steve is my friend from home"), - Document("I didn't always like yogurt"), - Document("I wonder why it's called football"), - Document("Where's waldo"), - ] - - reranker = LLMListwiseRerank.from_llm( - llm=ChatOpenAI(model="gpt-3.5-turbo"), top_n=3 - ) - compressed_docs = reranker.compress_documents(documents, "Who is steve") - assert len(compressed_docs) == 3 - assert "Steve" in compressed_docs[0].page_content - - """ # noqa: E501 + reranker = LLMListwiseRerank.from_llm( + llm=ChatOpenAI(model="gpt-3.5-turbo"), top_n=3 + ) + compressed_docs = reranker.compress_documents(documents, "Who is steve") + assert len(compressed_docs) == 3 + assert "Steve" in compressed_docs[0].page_content + ``` + """ reranker: Runnable[dict, list[Document]] """LLM-based reranker to use for filtering documents. Expected to take in a dict diff --git a/libs/langchain/langchain_classic/retrievers/parent_document_retriever.py b/libs/langchain/langchain_classic/retrievers/parent_document_retriever.py index 6f7322697ba..9d4e47d2bae 100644 --- a/libs/langchain/langchain_classic/retrievers/parent_document_retriever.py +++ b/libs/langchain/langchain_classic/retrievers/parent_document_retriever.py @@ -29,36 +29,34 @@ class ParentDocumentRetriever(MultiVectorRetriever): chunk. Examples: + ```python + from langchain_chroma import Chroma + from langchain_community.embeddings import OpenAIEmbeddings + from langchain_text_splitters import RecursiveCharacterTextSplitter + from langchain_classic.storage import InMemoryStore - .. code-block:: python - - from langchain_chroma import Chroma - from langchain_community.embeddings import OpenAIEmbeddings - from langchain_text_splitters import RecursiveCharacterTextSplitter - from langchain_classic.storage import InMemoryStore - - # This text splitter is used to create the parent documents - parent_splitter = RecursiveCharacterTextSplitter( - chunk_size=2000, add_start_index=True - ) - # This text splitter is used to create the child documents - # It should create documents smaller than the parent - child_splitter = RecursiveCharacterTextSplitter( - chunk_size=400, add_start_index=True - ) - # The vectorstore to use to index the child chunks - vectorstore = Chroma(embedding_function=OpenAIEmbeddings()) - # The storage layer for the parent documents - store = InMemoryStore() - - # Initialize the retriever - retriever = ParentDocumentRetriever( - vectorstore=vectorstore, - docstore=store, - child_splitter=child_splitter, - parent_splitter=parent_splitter, - ) + # This text splitter is used to create the parent documents + parent_splitter = RecursiveCharacterTextSplitter( + chunk_size=2000, add_start_index=True + ) + # This text splitter is used to create the child documents + # It should create documents smaller than the parent + child_splitter = RecursiveCharacterTextSplitter( + chunk_size=400, add_start_index=True + ) + # The vectorstore to use to index the child chunks + vectorstore = Chroma(embedding_function=OpenAIEmbeddings()) + # The storage layer for the parent documents + store = InMemoryStore() + # Initialize the retriever + retriever = ParentDocumentRetriever( + vectorstore=vectorstore, + docstore=store, + child_splitter=child_splitter, + parent_splitter=parent_splitter, + ) + ``` """ child_splitter: TextSplitter diff --git a/libs/langchain/langchain_classic/smith/__init__.py b/libs/langchain/langchain_classic/smith/__init__.py index 8965a15f802..4f06ae3af34 100644 --- a/libs/langchain/langchain_classic/smith/__init__.py +++ b/libs/langchain/langchain_classic/smith/__init__.py @@ -12,98 +12,97 @@ using a number of LangChain evaluators. An example of this is shown below, assuming you've created a LangSmith dataset called ``: -.. code-block:: python - - from langsmith import Client - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.chains import LLMChain - from langchain_classic.smith import RunEvalConfig, run_on_dataset +```python +from langsmith import Client +from langchain_openai import ChatOpenAI +from langchain_classic.chains import LLMChain +from langchain_classic.smith import RunEvalConfig, run_on_dataset - # Chains may have memory. Passing in a constructor function lets the - # evaluation framework avoid cross-contamination between runs. - def construct_chain(): - llm = ChatOpenAI(temperature=0) - chain = LLMChain.from_string(llm, "What's the answer to {your_input_key}") - return chain +# Chains may have memory. Passing in a constructor function lets the +# evaluation framework avoid cross-contamination between runs. +def construct_chain(): + llm = ChatOpenAI(temperature=0) + chain = LLMChain.from_string(llm, "What's the answer to {your_input_key}") + return chain - # Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) - evaluation_config = RunEvalConfig( - evaluators=[ - "qa", # "Correctness" against a reference answer - "embedding_distance", - RunEvalConfig.Criteria("helpfulness"), - RunEvalConfig.Criteria( - { - "fifth-grader-score": "Do you have to be smarter than a fifth " - "grader to answer this question?" - } - ), - ] - ) +# Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) +evaluation_config = RunEvalConfig( + evaluators=[ + "qa", # "Correctness" against a reference answer + "embedding_distance", + RunEvalConfig.Criteria("helpfulness"), + RunEvalConfig.Criteria( + { + "fifth-grader-score": "Do you have to be smarter than a fifth " + "grader to answer this question?" + } + ), + ] +) - client = Client() - run_on_dataset( - client, - "", - construct_chain, - evaluation=evaluation_config, - ) +client = Client() +run_on_dataset( + client, + "", + construct_chain, + evaluation=evaluation_config, +) +``` You can also create custom evaluators by subclassing the `StringEvaluator ` or LangSmith's `RunEvaluator` classes. -.. code-block:: python - - from typing import Optional - from langchain_classic.evaluation import StringEvaluator +```python +from typing import Optional +from langchain_classic.evaluation import StringEvaluator - class MyStringEvaluator(StringEvaluator): - @property - def requires_input(self) -> bool: - return False +class MyStringEvaluator(StringEvaluator): + @property + def requires_input(self) -> bool: + return False - @property - def requires_reference(self) -> bool: - return True + @property + def requires_reference(self) -> bool: + return True - @property - def evaluation_name(self) -> str: - return "exact_match" + @property + def evaluation_name(self) -> str: + return "exact_match" - def _evaluate_strings( - self, prediction, reference=None, input=None, **kwargs - ) -> dict: - return {"score": prediction == reference} + def _evaluate_strings( + self, prediction, reference=None, input=None, **kwargs + ) -> dict: + return {"score": prediction == reference} - evaluation_config = RunEvalConfig( - custom_evaluators=[MyStringEvaluator()], - ) +evaluation_config = RunEvalConfig( + custom_evaluators=[MyStringEvaluator()], +) - run_on_dataset( - client, - "", - construct_chain, - evaluation=evaluation_config, - ) +run_on_dataset( + client, + "", + construct_chain, + evaluation=evaluation_config, +) +``` **Primary Functions** - `arun_on_dataset `: - Asynchronous function to evaluate a chain, agent, or other LangChain component over a - dataset. + Asynchronous function to evaluate a chain, agent, or other LangChain component over + a dataset. - `run_on_dataset `: - Function to evaluate a chain, agent, or other LangChain component over a dataset. + Function to evaluate a chain, agent, or other LangChain component over a dataset. - `RunEvalConfig `: - Class representing the configuration for running evaluation. - You can select evaluators by - `EvaluatorType ` or config, - or you can pass in `custom_evaluators`. - + Class representing the configuration for running evaluation. + You can select evaluators by + `EvaluatorType ` or config, + or you can pass in `custom_evaluators`. """ from langchain_classic.smith.evaluation import ( diff --git a/libs/langchain/langchain_classic/smith/evaluation/__init__.py b/libs/langchain/langchain_classic/smith/evaluation/__init__.py index 4038a324569..78e7fc70ab5 100644 --- a/libs/langchain/langchain_classic/smith/evaluation/__init__.py +++ b/libs/langchain/langchain_classic/smith/evaluation/__init__.py @@ -8,38 +8,38 @@ For more information on the LangSmith API, see the **Example** -.. code-block:: python - - from langsmith import Client - from langchain_community.chat_models import ChatOpenAI - from langchain_classic.chains import LLMChain - from langchain_classic.smith import EvaluatorType, RunEvalConfig, run_on_dataset +```python +from langsmith import Client +from langchain_openai import ChatOpenAI +from langchain_classic.chains import LLMChain +from langchain_classic.smith import EvaluatorType, RunEvalConfig, run_on_dataset - def construct_chain(): - llm = ChatOpenAI(temperature=0) - chain = LLMChain.from_string(llm, "What's the answer to {your_input_key}") - return chain +def construct_chain(): + llm = ChatOpenAI(temperature=0) + chain = LLMChain.from_string(llm, "What's the answer to {your_input_key}") + return chain - evaluation_config = RunEvalConfig( - evaluators=[ - EvaluatorType.QA, # "Correctness" against a reference answer - EvaluatorType.EMBEDDING_DISTANCE, - RunEvalConfig.Criteria("helpfulness"), - RunEvalConfig.Criteria( - { - "fifth-grader-score": "Do you have to be smarter than a fifth " - "grader to answer this question?" - } - ), - ] - ) +evaluation_config = RunEvalConfig( + evaluators=[ + EvaluatorType.QA, # "Correctness" against a reference answer + EvaluatorType.EMBEDDING_DISTANCE, + RunEvalConfig.Criteria("helpfulness"), + RunEvalConfig.Criteria( + { + "fifth-grader-score": "Do you have to be smarter than a fifth " + "grader to answer this question?" + } + ), + ] +) - client = Client() - run_on_dataset( - client, "", construct_chain, evaluation=evaluation_config - ) +client = Client() +run_on_dataset( + client, "", construct_chain, evaluation=evaluation_config +) +``` **Attributes** diff --git a/libs/langchain/langchain_classic/smith/evaluation/runner_utils.py b/libs/langchain/langchain_classic/smith/evaluation/runner_utils.py index c207e0011ec..d52e037eb8b 100644 --- a/libs/langchain/langchain_classic/smith/evaluation/runner_utils.py +++ b/libs/langchain/langchain_classic/smith/evaluation/runner_utils.py @@ -1387,85 +1387,82 @@ async def arun_on_dataset( A dictionary containing the run's project name and the resulting model outputs. Examples: + ```python + from langsmith import Client + from langchain_openai import ChatOpenAI + from langchain_classic.chains import LLMChain + from langchain_classic.smith import smith_eval.RunEvalConfig, run_on_dataset - .. code-block:: python - - from langsmith import Client - from langchain_openai import ChatOpenAI - from langchain_classic.chains import LLMChain - from langchain_classic.smith import smith_eval.RunEvalConfig, run_on_dataset - - # Chains may have memory. Passing in a constructor function lets the - # evaluation framework avoid cross-contamination between runs. - def construct_chain(): - llm = ChatOpenAI(temperature=0) - chain = LLMChain.from_string( - llm, - "What's the answer to {your_input_key}" - ) - return chain - - # Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) - evaluation_config = smith_eval.RunEvalConfig( - evaluators=[ - "qa", # "Correctness" against a reference answer - "embedding_distance", - smith_eval.RunEvalConfig.Criteria("helpfulness"), - smith_eval.RunEvalConfig.Criteria({ - "fifth-grader-score": "Do you have to be smarter than a fifth " - "grader to answer this question?" - }), - ] + # Chains may have memory. Passing in a constructor function lets the + # evaluation framework avoid cross-contamination between runs. + def construct_chain(): + llm = ChatOpenAI(temperature=0) + chain = LLMChain.from_string( + llm, + "What's the answer to {your_input_key}" ) + return chain - client = Client() - await arun_on_dataset( - client, - dataset_name="", - llm_or_chain_factory=construct_chain, - evaluation=evaluation_config, - ) + # Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) + evaluation_config = smith_eval.RunEvalConfig( + evaluators=[ + "qa", # "Correctness" against a reference answer + "embedding_distance", + smith_eval.RunEvalConfig.Criteria("helpfulness"), + smith_eval.RunEvalConfig.Criteria({ + "fifth-grader-score": "Do you have to be smarter than a fifth " + "grader to answer this question?" + }), + ] + ) + client = Client() + await arun_on_dataset( + client, + dataset_name="", + llm_or_chain_factory=construct_chain, + evaluation=evaluation_config, + ) + ``` You can also create custom evaluators by subclassing the `StringEvaluator ` or LangSmith's `RunEvaluator` classes. - .. code-block:: python - - from typing import Optional - from langchain_classic.evaluation import StringEvaluator + ```python + from typing import Optional + from langchain_classic.evaluation import StringEvaluator - class MyStringEvaluator(StringEvaluator): - @property - def requires_input(self) -> bool: - return False + class MyStringEvaluator(StringEvaluator): + @property + def requires_input(self) -> bool: + return False - @property - def requires_reference(self) -> bool: - return True + @property + def requires_reference(self) -> bool: + return True - @property - def evaluation_name(self) -> str: - return "exact_match" + @property + def evaluation_name(self) -> str: + return "exact_match" - def _evaluate_strings( - self, prediction, reference=None, input=None, **kwargs - ) -> dict: - return {"score": prediction == reference} + def _evaluate_strings( + self, prediction, reference=None, input=None, **kwargs + ) -> dict: + return {"score": prediction == reference} - evaluation_config = smith_eval.RunEvalConfig( - custom_evaluators=[MyStringEvaluator()], - ) - - await arun_on_dataset( - client, - dataset_name="", - llm_or_chain_factory=construct_chain, - evaluation=evaluation_config, - ) + evaluation_config = smith_eval.RunEvalConfig( + custom_evaluators=[MyStringEvaluator()], + ) + await arun_on_dataset( + client, + dataset_name="", + llm_or_chain_factory=construct_chain, + evaluation=evaluation_config, + ) + ``` """ input_mapper = kwargs.pop("input_mapper", None) if input_mapper: @@ -1565,85 +1562,83 @@ def run_on_dataset( A dictionary containing the run's project name and the resulting model outputs. Examples: + ```python + from langsmith import Client + from langchain_openai import ChatOpenAI + from langchain_classic.chains import LLMChain + from langchain_classic.smith import smith_eval.RunEvalConfig, run_on_dataset - .. code-block:: python - - from langsmith import Client - from langchain_openai import ChatOpenAI - from langchain_classic.chains import LLMChain - from langchain_classic.smith import smith_eval.RunEvalConfig, run_on_dataset - - # Chains may have memory. Passing in a constructor function lets the - # evaluation framework avoid cross-contamination between runs. - def construct_chain(): - llm = ChatOpenAI(temperature=0) - chain = LLMChain.from_string( - llm, - "What's the answer to {your_input_key}" - ) - return chain - - # Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) - evaluation_config = smith_eval.RunEvalConfig( - evaluators=[ - "qa", # "Correctness" against a reference answer - "embedding_distance", - smith_eval.RunEvalConfig.Criteria("helpfulness"), - smith_eval.RunEvalConfig.Criteria({ - "fifth-grader-score": "Do you have to be smarter than a fifth " - "grader to answer this question?" - }), - ] + # Chains may have memory. Passing in a constructor function lets the + # evaluation framework avoid cross-contamination between runs. + def construct_chain(): + llm = ChatOpenAI(temperature=0) + chain = LLMChain.from_string( + llm, + "What's the answer to {your_input_key}" ) + return chain - client = Client() - run_on_dataset( - client, - dataset_name="", - llm_or_chain_factory=construct_chain, - evaluation=evaluation_config, - ) + # Load off-the-shelf evaluators via config or the EvaluatorType (string or enum) + evaluation_config = smith_eval.RunEvalConfig( + evaluators=[ + "qa", # "Correctness" against a reference answer + "embedding_distance", + smith_eval.RunEvalConfig.Criteria("helpfulness"), + smith_eval.RunEvalConfig.Criteria({ + "fifth-grader-score": "Do you have to be smarter than a fifth " + "grader to answer this question?" + }), + ] + ) + + client = Client() + run_on_dataset( + client, + dataset_name="", + llm_or_chain_factory=construct_chain, + evaluation=evaluation_config, + ) + ``` You can also create custom evaluators by subclassing the `StringEvaluator ` or LangSmith's `RunEvaluator` classes. - .. code-block:: python - - from typing import Optional - from langchain_classic.evaluation import StringEvaluator + ```python + from typing import Optional + from langchain_classic.evaluation import StringEvaluator - class MyStringEvaluator(StringEvaluator): - @property - def requires_input(self) -> bool: - return False + class MyStringEvaluator(StringEvaluator): + @property + def requires_input(self) -> bool: + return False - @property - def requires_reference(self) -> bool: - return True + @property + def requires_reference(self) -> bool: + return True - @property - def evaluation_name(self) -> str: - return "exact_match" + @property + def evaluation_name(self) -> str: + return "exact_match" - def _evaluate_strings( - self, prediction, reference=None, input=None, **kwargs - ) -> dict: - return {"score": prediction == reference} + def _evaluate_strings( + self, prediction, reference=None, input=None, **kwargs + ) -> dict: + return {"score": prediction == reference} - evaluation_config = smith_eval.RunEvalConfig( - custom_evaluators=[MyStringEvaluator()], - ) - - run_on_dataset( - client, - dataset_name="", - llm_or_chain_factory=construct_chain, - evaluation=evaluation_config, - ) + evaluation_config = smith_eval.RunEvalConfig( + custom_evaluators=[MyStringEvaluator()], + ) + run_on_dataset( + client, + dataset_name="", + llm_or_chain_factory=construct_chain, + evaluation=evaluation_config, + ) + ``` """ input_mapper = kwargs.pop("input_mapper", None) if input_mapper: diff --git a/libs/langchain/langchain_classic/storage/encoder_backed.py b/libs/langchain/langchain_classic/storage/encoder_backed.py index 16cb57958e1..c3a02acadff 100644 --- a/libs/langchain/langchain_classic/storage/encoder_backed.py +++ b/libs/langchain/langchain_classic/storage/encoder_backed.py @@ -15,39 +15,38 @@ class EncoderBackedStore(BaseStore[K, V]): Examples that uses JSON for encoding/decoding: - .. code-block:: python - - import json + ```python + import json - def key_encoder(key: int) -> str: - return json.dumps(key) + def key_encoder(key: int) -> str: + return json.dumps(key) - def value_serializer(value: float) -> str: - return json.dumps(value) + def value_serializer(value: float) -> str: + return json.dumps(value) - def value_deserializer(serialized_value: str) -> float: - return json.loads(serialized_value) + def value_deserializer(serialized_value: str) -> float: + return json.loads(serialized_value) - # Create an instance of the abstract store - abstract_store = MyCustomStore() + # Create an instance of the abstract store + abstract_store = MyCustomStore() - # Create an instance of the encoder-backed store - store = EncoderBackedStore( - store=abstract_store, - key_encoder=key_encoder, - value_serializer=value_serializer, - value_deserializer=value_deserializer, - ) - - # Use the encoder-backed store methods - store.mset([(1, 3.14), (2, 2.718)]) - values = store.mget([1, 2]) # Retrieves [3.14, 2.718] - store.mdelete([1, 2]) # Deletes the keys 1 and 2 + # Create an instance of the encoder-backed store + store = EncoderBackedStore( + store=abstract_store, + key_encoder=key_encoder, + value_serializer=value_serializer, + value_deserializer=value_deserializer, + ) + # Use the encoder-backed store methods + store.mset([(1, 3.14), (2, 2.718)]) + values = store.mget([1, 2]) # Retrieves [3.14, 2.718] + store.mdelete([1, 2]) # Deletes the keys 1 and 2 + ``` """ def __init__( diff --git a/libs/langchain/langchain_classic/storage/file_system.py b/libs/langchain/langchain_classic/storage/file_system.py index 66b6b97fcbc..226768451ef 100644 --- a/libs/langchain/langchain_classic/storage/file_system.py +++ b/libs/langchain/langchain_classic/storage/file_system.py @@ -15,26 +15,25 @@ class LocalFileStore(ByteStore): Examples: Create a LocalFileStore instance and perform operations on it: - .. code-block:: python + ```python + from langchain_classic.storage import LocalFileStore - from langchain_classic.storage import LocalFileStore + # Instantiate the LocalFileStore with the root path + file_store = LocalFileStore("/path/to/root") - # Instantiate the LocalFileStore with the root path - file_store = LocalFileStore("/path/to/root") + # Set values for keys + file_store.mset([("key1", b"value1"), ("key2", b"value2")]) - # Set values for keys - file_store.mset([("key1", b"value1"), ("key2", b"value2")]) + # Get values for keys + values = file_store.mget(["key1", "key2"]) # Returns [b"value1", b"value2"] - # Get values for keys - values = file_store.mget(["key1", "key2"]) # Returns [b"value1", b"value2"] - - # Delete keys - file_store.mdelete(["key1"]) - - # Iterate over keys - for key in file_store.yield_keys(): - print(key) # noqa: T201 + # Delete keys + file_store.mdelete(["key1"]) + # Iterate over keys + for key in file_store.yield_keys(): + print(key) # noqa: T201 + ``` """ def __init__( diff --git a/libs/langchain/langchain_classic/tools/__init__.py b/libs/langchain/langchain_classic/tools/__init__.py index 2ffd733934d..9e058eaa96e 100644 --- a/libs/langchain/langchain_classic/tools/__init__.py +++ b/libs/langchain/langchain_classic/tools/__init__.py @@ -2,19 +2,6 @@ Each tool has a **description**. Agent uses the description to choose the right tool for the job. - -**Class hierarchy:** - -.. code-block:: - - ToolMetaclass --> BaseTool --> Tool # Examples: AIPluginTool, BaseGraphQLTool - # Examples: BraveSearch, HumanInputRun - -**Main helpers:** - -.. code-block:: - - CallbackManagerForToolRun, AsyncCallbackManagerForToolRun """ import warnings diff --git a/libs/langchain/langchain_classic/vectorstores/__init__.py b/libs/langchain/langchain_classic/vectorstores/__init__.py index c5a40cebf88..6b61ff01cb1 100644 --- a/libs/langchain/langchain_classic/vectorstores/__init__.py +++ b/libs/langchain/langchain_classic/vectorstores/__init__.py @@ -3,21 +3,7 @@ One of the most common ways to store and search over unstructured data is to embed it and store the resulting embedding vectors, and then query the store and retrieve the data that are 'most similar' to the embedded query. - -**Class hierarchy:** - -.. code-block:: - - VectorStore --> # Examples: Annoy, FAISS, Milvus - - BaseRetriever --> VectorStoreRetriever --> Retriever # Example: VespaRetriever - -**Main helpers:** - -.. code-block:: - - Embeddings, Document -""" # noqa: E501 +""" from typing import TYPE_CHECKING, Any diff --git a/libs/langchain/tests/unit_tests/chat_models/test_base.py b/libs/langchain/tests/unit_tests/chat_models/test_base.py index 8e023f9de12..400deabe9c6 100644 --- a/libs/langchain/tests/unit_tests/chat_models/test_base.py +++ b/libs/langchain/tests/unit_tests/chat_models/test_base.py @@ -77,18 +77,16 @@ def test_configurable() -> None: - Properly resolves to the configured model type when parameters are provided Example: + ```python + # This creates a configurable model without specifying which model + model = init_chat_model() - .. code-block:: python - - # This creates a configurable model without specifying which model - model = init_chat_model() - - # This will FAIL - no model specified yet - model.get_num_tokens("hello") # AttributeError! - - # This works - provides model at runtime - response = model.invoke("Hello", config={"configurable": {"model": "gpt-4o"}}) + # This will FAIL - no model specified yet + model.get_num_tokens("hello") # AttributeError! + # This works - provides model at runtime + response = model.invoke("Hello", config={"configurable": {"model": "gpt-4o"}}) + ``` """ model = init_chat_model() @@ -203,23 +201,19 @@ def test_configurable_with_default() -> None: - Can be used in chains with different model providers via configuration Example: + ```python + # This creates a configurable model with default parameters (model) + model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar") - .. code-block:: python - - # This creates a configurable model with default parameters (model) - model = init_chat_model( - "gpt-4o", configurable_fields="any", config_prefix="bar" - ) - - # This works immediately - uses default gpt-4o - tokens = model.get_num_tokens("hello") - - # This also works - switches to Claude at runtime - response = model.invoke( - "Hello", - config={"configurable": {"my_model_model": "claude-3-sonnet-20240229"}}, - ) + # This works immediately - uses default gpt-4o + tokens = model.get_num_tokens("hello") + # This also works - switches to Claude at runtime + response = model.invoke( + "Hello", + config={"configurable": {"my_model_model": "claude-3-sonnet-20240229"}}, + ) + ``` """ model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar") for method in ( diff --git a/libs/langchain/tests/unit_tests/conftest.py b/libs/langchain/tests/unit_tests/conftest.py index 518b70246e5..694db091b3c 100644 --- a/libs/langchain/tests/unit_tests/conftest.py +++ b/libs/langchain/tests/unit_tests/conftest.py @@ -73,11 +73,10 @@ def pytest_collection_modifyitems( The `requires` marker syntax is: - .. code-block:: python - - @pytest.mark.requires("package1", "package2") - def test_something(): ... - + ```python + @pytest.mark.requires("package1", "package2") + def test_something(): ... + ``` """ # Mapping from the name of a package to whether it is installed or not. # Used to avoid repeated calls to `util.find_spec` diff --git a/libs/langchain/tests/unit_tests/test_imports.py b/libs/langchain/tests/unit_tests/test_imports.py index b3a799ef90b..f13d1e18f32 100644 --- a/libs/langchain/tests/unit_tests/test_imports.py +++ b/libs/langchain/tests/unit_tests/test_imports.py @@ -158,10 +158,7 @@ def _literal_eval_str(node: ast.AST) -> str: Returns: str: The corresponding string value. """ - if ( - isinstance(node, ast.Constant) # Python 3.8+ - and isinstance(node.value, str) - ): + if isinstance(node, ast.Constant) and isinstance(node.value, str): return node.value msg = f"Invalid DEPRECATED_LOOKUP format: expected str, got {type(node).__name__}" raise AssertionError(msg) diff --git a/libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py b/libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py index d698c6d7e27..071a8d9cf4a 100644 --- a/libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py +++ b/libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py @@ -118,33 +118,33 @@ class ToolConfig(TypedDict): Can be either: - A static string describing the approval request - A callable that dynamically generates the description based on agent state, - runtime, and tool call information + runtime, and tool call information Example: - .. code-block:: python + ```python + # Static string description + config = ToolConfig( + allow_accept=True, + description="Please review this tool execution" + ) - # Static string description - config = ToolConfig( - allow_accept=True, - description="Please review this tool execution" + # Dynamic callable description + def format_tool_description( + tool_call: ToolCall, + state: AgentState, + runtime: Runtime + ) -> str: + import json + return ( + f"Tool: {tool_call['name']}\\n" + f"Arguments:\\n{json.dumps(tool_call['args'], indent=2)}" ) - # Dynamic callable description - def format_tool_description( - tool_call: ToolCall, - state: AgentState, - runtime: Runtime - ) -> str: - import json - return ( - f"Tool: {tool_call['name']}\\n" - f"Arguments:\\n{json.dumps(tool_call['args'], indent=2)}" - ) - - config = ToolConfig( - allow_accept=True, - description=format_tool_description - ) + config = ToolConfig( + allow_accept=True, + description=format_tool_description + ) + ``` """ diff --git a/libs/langchain_v1/langchain/chat_models/base.py b/libs/langchain_v1/langchain/chat_models/base.py index f49b2af7c3b..28e92b0458f 100644 --- a/libs/langchain_v1/langchain/chat_models/base.py +++ b/libs/langchain_v1/langchain/chat_models/base.py @@ -73,8 +73,9 @@ def init_chat_model( ) -> BaseChatModel | _ConfigurableModel: """Initialize a ChatModel from the model name and provider. - **Note:** Must have the integration package corresponding to the model provider - installed. + !!! note + Must have the integration package corresponding to the model provider + installed. Args: model: The name of the model, e.g. "o3-mini", "claude-3-5-sonnet-latest". You can @@ -165,109 +166,104 @@ def init_chat_model( ???+ note "Init non-configurable model" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic langchain-google-vertexai + from langchain.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic langchain-google-vertexai - from langchain.chat_models import init_chat_model - - o3_mini = init_chat_model("openai:o3-mini", temperature=0) - claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0) - gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0) - - o3_mini.invoke("what's your name") - claude_sonnet.invoke("what's your name") - gemini_2_flash.invoke("what's your name") + o3_mini = init_chat_model("openai:o3-mini", temperature=0) + claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0) + gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0) + o3_mini.invoke("what's your name") + claude_sonnet.invoke("what's your name") + gemini_2_flash.invoke("what's your name") + ``` ??? note "Partially configurable model with no default" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic - from langchain.chat_models import init_chat_model + # We don't need to specify configurable=True if a model isn't specified. + configurable_model = init_chat_model(temperature=0) - # We don't need to specify configurable=True if a model isn't specified. - configurable_model = init_chat_model(temperature=0) + configurable_model.invoke("what's your name", config={"configurable": {"model": "gpt-4o"}}) + # GPT-4o response - configurable_model.invoke( - "what's your name", config={"configurable": {"model": "gpt-4o"}} - ) - # GPT-4o response - - configurable_model.invoke( - "what's your name", config={"configurable": {"model": "claude-3-5-sonnet-latest"}} - ) - # claude-3.5 sonnet response + configurable_model.invoke( + "what's your name", config={"configurable": {"model": "claude-3-5-sonnet-latest"}} + ) + # claude-3.5 sonnet response + ``` ??? note "Fully configurable model with a default" - .. code-block:: python + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain.chat_models import init_chat_model - # pip install langchain langchain-openai langchain-anthropic - from langchain.chat_models import init_chat_model + configurable_model_with_default = init_chat_model( + "openai:gpt-4o", + configurable_fields="any", # this allows us to configure other params like temperature, max_tokens, etc at runtime. + config_prefix="foo", + temperature=0, + ) - configurable_model_with_default = init_chat_model( - "openai:gpt-4o", - configurable_fields="any", # this allows us to configure other params like temperature, max_tokens, etc at runtime. - config_prefix="foo", - temperature=0, - ) + configurable_model_with_default.invoke("what's your name") + # GPT-4o response with temperature 0 - configurable_model_with_default.invoke("what's your name") - # GPT-4o response with temperature 0 - - configurable_model_with_default.invoke( - "what's your name", - config={ - "configurable": { - "foo_model": "anthropic:claude-3-5-sonnet-latest", - "foo_temperature": 0.6, - } - }, - ) - # Claude-3.5 sonnet response with temperature 0.6 + configurable_model_with_default.invoke( + "what's your name", + config={ + "configurable": { + "foo_model": "anthropic:claude-3-5-sonnet-latest", + "foo_temperature": 0.6, + } + }, + ) + # Claude-3.5 sonnet response with temperature 0.6 + ``` ??? note "Bind tools to a configurable model" You can call any ChatModel declarative methods on a configurable model in the same way that you would with a normal model. - .. code-block:: python - - # pip install langchain langchain-openai langchain-anthropic - from langchain.chat_models import init_chat_model - from pydantic import BaseModel, Field + ```python + # pip install langchain langchain-openai langchain-anthropic + from langchain.chat_models import init_chat_model + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - configurable_model = init_chat_model( - "gpt-4o", configurable_fields=("model", "model_provider"), temperature=0 - ) + configurable_model = init_chat_model( + "gpt-4o", configurable_fields=("model", "model_provider"), temperature=0 + ) - configurable_model_with_tools = configurable_model.bind_tools( - [GetWeather, GetPopulation] - ) - configurable_model_with_tools.invoke( - "Which city is hotter today and which is bigger: LA or NY?" - ) - # GPT-4o response with tool calls + configurable_model_with_tools = configurable_model.bind_tools([GetWeather, GetPopulation]) + configurable_model_with_tools.invoke( + "Which city is hotter today and which is bigger: LA or NY?" + ) + # GPT-4o response with tool calls - configurable_model_with_tools.invoke( - "Which city is hotter today and which is bigger: LA or NY?", - config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, - ) - # Claude-3.5 sonnet response with tools + configurable_model_with_tools.invoke( + "Which city is hotter today and which is bigger: LA or NY?", + config={"configurable": {"model": "claude-3-5-sonnet-latest"}}, + ) + # Claude-3.5 sonnet response with tools + ``` !!! version-added "Added in version 0.2.7" diff --git a/libs/langchain_v1/langchain/embeddings/base.py b/libs/langchain_v1/langchain/embeddings/base.py index 598b721d153..5a5ed45712a 100644 --- a/libs/langchain_v1/langchain/embeddings/base.py +++ b/libs/langchain_v1/langchain/embeddings/base.py @@ -35,13 +35,13 @@ def _parse_model_string(model_name: str) -> tuple[str, str]: Returns: A tuple of (provider, model_name) - .. code-block:: python + ```python + _parse_model_string("openai:text-embedding-3-small") + # Returns: ("openai", "text-embedding-3-small") - _parse_model_string("openai:text-embedding-3-small") - # Returns: ("openai", "text-embedding-3-small") - - _parse_model_string("bedrock:amazon.titan-embed-text-v1") - # Returns: ("bedrock", "amazon.titan-embed-text-v1") + _parse_model_string("bedrock:amazon.titan-embed-text-v1") + # Returns: ("bedrock", "amazon.titan-embed-text-v1") + ``` Raises: ValueError: If the model string is not in the correct format or @@ -128,8 +128,9 @@ def init_embeddings( ) -> Embeddings: """Initialize an embeddings model from a model name and optional provider. - **Note:** Must have the integration package corresponding to the model provider - installed. + !!! note + Must have the integration package corresponding to the model provider + installed. Args: model: Name of the model to use. Can be either: @@ -153,18 +154,18 @@ def init_embeddings( ???+ note "Example Usage" - .. code-block:: python + ```python + # Using a model string + model = init_embeddings("openai:text-embedding-3-small") + model.embed_query("Hello, world!") - # Using a model string - model = init_embeddings("openai:text-embedding-3-small") - model.embed_query("Hello, world!") + # Using explicit provider + model = init_embeddings(model="text-embedding-3-small", provider="openai") + model.embed_documents(["Hello, world!", "Goodbye, world!"]) - # Using explicit provider - model = init_embeddings(model="text-embedding-3-small", provider="openai") - model.embed_documents(["Hello, world!", "Goodbye, world!"]) - - # With additional parameters - model = init_embeddings("openai:text-embedding-3-small", api_key="sk-...") + # With additional parameters + model = init_embeddings("openai:text-embedding-3-small", api_key="sk-...") + ``` !!! version-added "Added in version 0.3.9" diff --git a/libs/langchain_v1/langchain/embeddings/cache.py b/libs/langchain_v1/langchain/embeddings/cache.py index 83d071853d8..3096d340cf2 100644 --- a/libs/langchain_v1/langchain/embeddings/cache.py +++ b/libs/langchain_v1/langchain/embeddings/cache.py @@ -122,24 +122,24 @@ class CacheBackedEmbeddings(Embeddings): embeddings too, pass in a query_embedding_store to constructor. Examples: - .. code-block: python + ```python + from langchain.embeddings import CacheBackedEmbeddings + from langchain.storage import LocalFileStore + from langchain_community.embeddings import OpenAIEmbeddings - from langchain.embeddings import CacheBackedEmbeddings - from langchain.storage import LocalFileStore - from langchain_community.embeddings import OpenAIEmbeddings + store = LocalFileStore("./my_cache") - store = LocalFileStore('./my_cache') + underlying_embedder = OpenAIEmbeddings() + embedder = CacheBackedEmbeddings.from_bytes_store( + underlying_embedder, store, namespace=underlying_embedder.model + ) - underlying_embedder = OpenAIEmbeddings() - embedder = CacheBackedEmbeddings.from_bytes_store( - underlying_embedder, store, namespace=underlying_embedder.model - ) + # Embedding is computed and cached + embeddings = embedder.embed_documents(["hello", "goodbye"]) - # Embedding is computed and cached - embeddings = embedder.embed_documents(["hello", "goodbye"]) - - # Embeddings are retrieved from the cache, no computation is done - embeddings = embedder.embed_documents(["hello", "goodbye"]) + # Embeddings are retrieved from the cache, no computation is done + embeddings = embedder.embed_documents(["hello", "goodbye"]) + ``` """ def __init__( diff --git a/libs/langchain_v1/langchain/storage/encoder_backed.py b/libs/langchain_v1/langchain/storage/encoder_backed.py index 670040b50a0..80e60c32a53 100644 --- a/libs/langchain_v1/langchain/storage/encoder_backed.py +++ b/libs/langchain_v1/langchain/storage/encoder_backed.py @@ -17,39 +17,38 @@ class EncoderBackedStore(BaseStore[K, V]): Examples that uses JSON for encoding/decoding: - .. code-block:: python - - import json + ```python + import json - def key_encoder(key: int) -> str: - return json.dumps(key) + def key_encoder(key: int) -> str: + return json.dumps(key) - def value_serializer(value: float) -> str: - return json.dumps(value) + def value_serializer(value: float) -> str: + return json.dumps(value) - def value_deserializer(serialized_value: str) -> float: - return json.loads(serialized_value) + def value_deserializer(serialized_value: str) -> float: + return json.loads(serialized_value) - # Create an instance of the abstract store - abstract_store = MyCustomStore() + # Create an instance of the abstract store + abstract_store = MyCustomStore() - # Create an instance of the encoder-backed store - store = EncoderBackedStore( - store=abstract_store, - key_encoder=key_encoder, - value_serializer=value_serializer, - value_deserializer=value_deserializer, - ) - - # Use the encoder-backed store methods - store.mset([(1, 3.14), (2, 2.718)]) - values = store.mget([1, 2]) # Retrieves [3.14, 2.718] - store.mdelete([1, 2]) # Deletes the keys 1 and 2 + # Create an instance of the encoder-backed store + store = EncoderBackedStore( + store=abstract_store, + key_encoder=key_encoder, + value_serializer=value_serializer, + value_deserializer=value_deserializer, + ) + # Use the encoder-backed store methods + store.mset([(1, 3.14), (2, 2.718)]) + values = store.mget([1, 2]) # Retrieves [3.14, 2.718] + store.mdelete([1, 2]) # Deletes the keys 1 and 2 + ``` """ def __init__( diff --git a/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py b/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py index 295002a9b2f..75b0a86b121 100644 --- a/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py +++ b/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py @@ -78,18 +78,16 @@ def test_configurable() -> None: - Properly resolves to the configured model type when parameters are provided Example: + ```python + # This creates a configurable model without specifying which model + model = init_chat_model() - .. code-block:: python - - # This creates a configurable model without specifying which model - model = init_chat_model() - - # This will FAIL - no model specified yet - model.get_num_tokens("hello") # AttributeError! - - # This works - provides model at runtime - response = model.invoke("Hello", config={"configurable": {"model": "gpt-4o"}}) + # This will FAIL - no model specified yet + model.get_num_tokens("hello") # AttributeError! + # This works - provides model at runtime + response = model.invoke("Hello", config={"configurable": {"model": "gpt-4o"}}) + ``` """ model = init_chat_model() @@ -204,20 +202,18 @@ def test_configurable_with_default() -> None: - Can be used in chains with different model providers via configuration Example: + ```python + # This creates a configurable model with default parameters (model) + model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar") - .. code-block:: python - - # This creates a configurable model with default parameters (model) - model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar") - - # This works immediately - uses default gpt-4o - tokens = model.get_num_tokens("hello") - - # This also works - switches to Claude at runtime - response = model.invoke( - "Hello", config={"configurable": {"my_model_model": "claude-3-sonnet-20240229"}} - ) + # This works immediately - uses default gpt-4o + tokens = model.get_num_tokens("hello") + # This also works - switches to Claude at runtime + response = model.invoke( + "Hello", config={"configurable": {"my_model_model": "claude-3-sonnet-20240229"}} + ) + ``` """ model = init_chat_model("gpt-4o", configurable_fields="any", config_prefix="bar") for method in ( diff --git a/libs/langchain_v1/tests/unit_tests/conftest.py b/libs/langchain_v1/tests/unit_tests/conftest.py index 01c42bd20ff..96f36ceae77 100644 --- a/libs/langchain_v1/tests/unit_tests/conftest.py +++ b/libs/langchain_v1/tests/unit_tests/conftest.py @@ -30,11 +30,10 @@ def pytest_collection_modifyitems(config: pytest.Config, items: Sequence[pytest. The `requires` marker syntax is: - .. code-block:: python - - @pytest.mark.requires("package1", "package2") - def test_something(): ... - + ```python + @pytest.mark.requires("package1", "package2") + def test_something(): ... + ``` """ # Mapping from the name of a package to whether it is installed or not. # Used to avoid repeated calls to `util.find_spec` diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 8335b39d240..fb494fffcab 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -575,10 +575,10 @@ class ChatAnthropic(BaseChatModel): Setup: Install `langchain-anthropic` and set environment variable `ANTHROPIC_API_KEY`. - .. code-block:: bash - - pip install -U langchain-anthropic - export ANTHROPIC_API_KEY="your-api-key" + ```bash + pip install -U langchain-anthropic + export ANTHROPIC_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -606,27 +606,27 @@ class ChatAnthropic(BaseChatModel): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic( + model="claude-3-7-sonnet-20250219", + temperature=0, + max_tokens=1024, + timeout=None, + max_retries=2, + # api_key="...", + # base_url="...", + # other params... + ) + ``` - llm = ChatAnthropic( - model="claude-3-7-sonnet-20250219", - temperature=0, - max_tokens=1024, - timeout=None, - max_retries=2, - # api_key="...", - # base_url="...", - # other params... - ) - - **NOTE**: Any param which is not explicitly supported will be passed directly to the - `anthropic.Anthropic.messages.create(...)` API every time to the model is - invoked. For example: - - .. code-block:: python + !!! note + Any param which is not explicitly supported will be passed directly to the + `anthropic.Anthropic.messages.create(...)` API every time to the model is + invoked. For example: + ```python from langchain_anthropic import ChatAnthropic import anthropic @@ -639,172 +639,173 @@ class ChatAnthropic(BaseChatModel): # which is also equivalent to: ChatAnthropic(...).invoke(..., extra_headers={}) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ( + "system", + "You are a helpful translator. Translate the user sentence to French.", + ), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` - messages = [ - ( - "system", - "You are a helpful translator. Translate the user sentence to French.", - ), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - AIMessage( - content="J'aime la programmation.", - response_metadata={ - "id": "msg_01Trik66aiQ9Z1higrD5XFx3", - "model": "claude-3-7-sonnet-20250219", - "stop_reason": "end_turn", - "stop_sequence": None, - "usage": {"input_tokens": 25, "output_tokens": 11}, - }, - id="run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0", - usage_metadata={ - "input_tokens": 25, - "output_tokens": 11, - "total_tokens": 36, - }, - ) + ```python + AIMessage( + content="J'aime la programmation.", + response_metadata={ + "id": "msg_01Trik66aiQ9Z1higrD5XFx3", + "model": "claude-3-7-sonnet-20250219", + "stop_reason": "end_turn", + "stop_sequence": None, + "usage": {"input_tokens": 25, "output_tokens": 11}, + }, + id="run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0", + usage_metadata={ + "input_tokens": 25, + "output_tokens": 11, + "total_tokens": 36, + }, + ) + ``` Stream: - .. code-block:: python + ```python + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` - for chunk in llm.stream(messages): - print(chunk.text, end="") + ```python + AIMessageChunk(content="J", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content="'", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content="a", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content="ime", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content=" la", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content=" programm", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content="ation", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + AIMessageChunk(content=".", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") + ``` - .. code-block:: python + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` - AIMessageChunk(content="J", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content="'", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content="a", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content="ime", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content=" la", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content=" programm", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content="ation", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - AIMessageChunk(content=".", id="run-272ff5f9-8485-402c-b90d-eac8babc5b25") - - .. code-block:: python - - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full - - .. code-block:: python - - AIMessageChunk(content="J'aime la programmation.", id="run-b34faef0-882f-4869-a19c-ed2b856e6361") + ```python + AIMessageChunk(content="J'aime la programmation.", id="run-b34faef0-882f-4869-a19c-ed2b856e6361") + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke(messages) - await llm.ainvoke(messages) + # stream: + # async for chunk in (await llm.astream(messages)) - # stream: - # async for chunk in (await llm.astream(messages)) + # batch: + # await llm.abatch([messages]) + ``` - # batch: - # await llm.abatch([messages]) - - .. code-block:: python - - AIMessage( - content="J'aime la programmation.", - response_metadata={ - "id": "msg_01Trik66aiQ9Z1higrD5XFx3", - "model": "claude-3-7-sonnet-20250219", - "stop_reason": "end_turn", - "stop_sequence": None, - "usage": {"input_tokens": 25, "output_tokens": 11}, - }, - id="run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0", - usage_metadata={ - "input_tokens": 25, - "output_tokens": 11, - "total_tokens": 36, - }, - ) + ```python + AIMessage( + content="J'aime la programmation.", + response_metadata={ + "id": "msg_01Trik66aiQ9Z1higrD5XFx3", + "model": "claude-3-7-sonnet-20250219", + "stop_reason": "end_turn", + "stop_sequence": None, + "usage": {"input_tokens": 25, "output_tokens": 11}, + }, + id="run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0", + usage_metadata={ + "input_tokens": 25, + "output_tokens": 11, + "total_tokens": 36, + }, + ) + ``` Tool calling: - .. code-block:: python - - from pydantic import BaseModel, Field + ```python + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") - ai_msg.tool_calls + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") + ai_msg.tool_calls + ``` - .. code-block:: python - - [ - { - "name": "GetWeather", - "args": {"location": "Los Angeles, CA"}, - "id": "toolu_01KzpPEAgzura7hpBqwHbWdo", - }, - { - "name": "GetWeather", - "args": {"location": "New York, NY"}, - "id": "toolu_01JtgbVGVJbiSwtZk3Uycezx", - }, - { - "name": "GetPopulation", - "args": {"location": "Los Angeles, CA"}, - "id": "toolu_01429aygngesudV9nTbCKGuw", - }, - { - "name": "GetPopulation", - "args": {"location": "New York, NY"}, - "id": "toolu_01JPktyd44tVMeBcPPnFSEJG", - }, - ] + ```python + [ + { + "name": "GetWeather", + "args": {"location": "Los Angeles, CA"}, + "id": "toolu_01KzpPEAgzura7hpBqwHbWdo", + }, + { + "name": "GetWeather", + "args": {"location": "New York, NY"}, + "id": "toolu_01JtgbVGVJbiSwtZk3Uycezx", + }, + { + "name": "GetPopulation", + "args": {"location": "Los Angeles, CA"}, + "id": "toolu_01429aygngesudV9nTbCKGuw", + }, + { + "name": "GetPopulation", + "args": {"location": "New York, NY"}, + "id": "toolu_01JPktyd44tVMeBcPPnFSEJG", + }, + ] + ``` See `ChatAnthropic.bind_tools()` method for more. Structured output: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from pydantic import BaseModel, Field + from pydantic import BaseModel, Field - class Joke(BaseModel): - '''Joke to tell user.''' + 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") - rating: int | None = Field(description="How funny the joke is, from 1 to 10") + setup: str = Field(description="The setup of the joke") + punchline: str = Field(description="The punchline to the joke") + rating: int | None = Field(description="How funny the joke is, from 1 to 10") - structured_llm = llm.with_structured_output(Joke) - structured_llm.invoke("Tell me a joke about cats") + structured_llm = llm.with_structured_output(Joke) + structured_llm.invoke("Tell me a joke about cats") + ``` - .. code-block:: python - - Joke( - setup="Why was the cat sitting on the computer?", - punchline="To keep an eye on the mouse!", - rating=None, - ) + ```python + Joke( + setup="Why was the cat sitting on the computer?", + punchline="To keep an eye on the mouse!", + rating=None, + ) + ``` See `ChatAnthropic.with_structured_output()` for more. @@ -812,132 +813,132 @@ class ChatAnthropic(BaseChatModel): See [multimodal guides](https://python.langchain.com/docs/how_to/multimodal_inputs/) for more detail. - .. code-block:: python + ```python + import base64 - import base64 + import httpx + from langchain_anthropic import ChatAnthropic + from langchain_core.messages import HumanMessage - import httpx - from langchain_anthropic import ChatAnthropic - from langchain_core.messages import HumanMessage + image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") - image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" - image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") + llm = ChatAnthropic(model="claude-3-5-sonnet-latest") + message = HumanMessage( + content=[ + { + "type": "text", + "text": "Can you highlight the differences between these two images?", + }, + { + "type": "image", + "base64": image_data, + "mime_type": "image/jpeg", + }, + { + "type": "image", + "url": image_url, + }, + ], + ) + ai_msg = llm.invoke([message]) + ai_msg.content + ``` - llm = ChatAnthropic(model="claude-3-5-sonnet-latest") - message = HumanMessage( - content=[ - { - "type": "text", - "text": "Can you highlight the differences between these two images?", - }, - { - "type": "image", - "base64": image_data, - "mime_type": "image/jpeg", - }, - { - "type": "image", - "url": image_url, - }, - ], - ) - ai_msg = llm.invoke([message]) - ai_msg.content - - .. code-block:: python - - "After examining both images carefully, I can see that they are actually identical." + ```python + "After examining both images carefully, I can see that they are actually identical." + ``` ??? note "Files API" You can also pass in files that are managed through Anthropic's [Files API](https://docs.anthropic.com/en/docs/build-with-claude/files): - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic - - llm = ChatAnthropic( - model="claude-sonnet-4-20250514", - betas=["files-api-2025-04-14"], - ) - input_message = { - "role": "user", - "content": [ - { - "type": "text", - "text": "Describe this document.", - }, - { - "type": "image", - "id": "file_abc123...", - }, - ], - } - llm.invoke([input_message]) + llm = ChatAnthropic( + model="claude-sonnet-4-20250514", + betas=["files-api-2025-04-14"], + ) + input_message = { + "role": "user", + "content": [ + { + "type": "text", + "text": "Describe this document.", + }, + { + "type": "image", + "id": "file_abc123...", + }, + ], + } + llm.invoke([input_message]) + ``` PDF input: See [multimodal guides](https://python.langchain.com/docs/how_to/multimodal_inputs/) for more detail. - .. code-block:: python + ```python + from base64 import b64encode + from langchain_anthropic import ChatAnthropic + from langchain_core.messages import HumanMessage + import requests - from base64 import b64encode - from langchain_anthropic import ChatAnthropic - from langchain_core.messages import HumanMessage - import requests + url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" + data = b64encode(requests.get(url).content).decode() - url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" - data = b64encode(requests.get(url).content).decode() + llm = ChatAnthropic(model="claude-3-5-sonnet-latest") + ai_msg = llm.invoke( + [ + HumanMessage( + [ + "Summarize this document.", + { + "type": "file", + "mime_type": "application/pdf", + "base64": data, + }, + ] + ) + ] + ) + ai_msg.content + ``` - llm = ChatAnthropic(model="claude-3-5-sonnet-latest") - ai_msg = llm.invoke( - [ - HumanMessage( - [ - "Summarize this document.", - { - "type": "file", - "mime_type": "application/pdf", - "base64": data, - }, - ] - ) - ] - ) - ai_msg.content - - .. code-block:: python - - "This appears to be a simple document..." + ```python + "This appears to be a simple document..." + ``` ??? note "Files API" You can also pass in files that are managed through Anthropic's [Files API](https://docs.anthropic.com/en/docs/build-with-claude/files): - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic - - llm = ChatAnthropic( - model="claude-sonnet-4-20250514", - betas=["files-api-2025-04-14"], - ) - input_message = { - "role": "user", - "content": [ - { - "type": "text", - "text": "Describe this document.", - }, - { - "type": "file", - "id": "file_abc123...", - }, - ], - } - llm.invoke([input_message]) + llm = ChatAnthropic( + model="claude-sonnet-4-20250514", + betas=["files-api-2025-04-14"], + ) + input_message = { + "role": "user", + "content": [ + { + "type": "text", + "text": "Describe this document.", + }, + { + "type": "file", + "id": "file_abc123...", + }, + ], + } + llm.invoke([input_message]) + ``` Extended thinking: Claude 3.7 Sonnet supports an @@ -950,29 +951,29 @@ class ChatAnthropic(BaseChatModel): You will need to specify a token budget to use this feature. See usage example: - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic( + model="claude-3-7-sonnet-latest", + max_tokens=5000, + thinking={"type": "enabled", "budget_tokens": 2000}, + ) - llm = ChatAnthropic( - model="claude-3-7-sonnet-latest", - max_tokens=5000, - thinking={"type": "enabled", "budget_tokens": 2000}, - ) + response = llm.invoke("What is the cube root of 50.653?") + response.content + ``` - response = llm.invoke("What is the cube root of 50.653?") - response.content - - .. code-block:: python - - [ - { - "signature": "...", - "thinking": "To find the cube root of 50.653...", - "type": "thinking", - }, - {"text": "The cube root of 50.653 is ...", "type": "text"}, - ] + ```python + [ + { + "signature": "...", + "thinking": "To find the cube root of 50.653...", + "type": "thinking", + }, + {"text": "The cube root of 50.653 is ...", "type": "text"}, + ] + ``` Citations: Anthropic supports a @@ -983,94 +984,94 @@ class ChatAnthropic(BaseChatModel): with `"citations": {"enabled": True}` are included in a query, Claude may generate citations in its response. - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic(model="claude-3-5-haiku-latest") - llm = ChatAnthropic(model="claude-3-5-haiku-latest") - - messages = [ - { - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "text", - "media_type": "text/plain", - "data": "The grass is green. The sky is blue.", - }, - "title": "My Document", - "context": "This is a trustworthy document.", - "citations": {"enabled": True}, + messages = [ + { + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "text", + "media_type": "text/plain", + "data": "The grass is green. The sky is blue.", }, - {"type": "text", "text": "What color is the grass and sky?"}, - ], - } - ] - response = llm.invoke(messages) - response.content + "title": "My Document", + "context": "This is a trustworthy document.", + "citations": {"enabled": True}, + }, + {"type": "text", "text": "What color is the grass and sky?"}, + ], + } + ] + response = llm.invoke(messages) + response.content + ``` - .. code-block:: python - - [ - {"text": "Based on the document, ", "type": "text"}, - { - "text": "the grass is green", - "type": "text", - "citations": [ - { - "type": "char_location", - "cited_text": "The grass is green. ", - "document_index": 0, - "document_title": "My Document", - "start_char_index": 0, - "end_char_index": 20, - } - ], - }, - {"text": ", and ", "type": "text"}, - { - "text": "the sky is blue", - "type": "text", - "citations": [ - { - "type": "char_location", - "cited_text": "The sky is blue.", - "document_index": 0, - "document_title": "My Document", - "start_char_index": 20, - "end_char_index": 36, - } - ], - }, - {"text": ".", "type": "text"}, - ] + ```python + [ + {"text": "Based on the document, ", "type": "text"}, + { + "text": "the grass is green", + "type": "text", + "citations": [ + { + "type": "char_location", + "cited_text": "The grass is green. ", + "document_index": 0, + "document_title": "My Document", + "start_char_index": 0, + "end_char_index": 20, + } + ], + }, + {"text": ", and ", "type": "text"}, + { + "text": "the sky is blue", + "type": "text", + "citations": [ + { + "type": "char_location", + "cited_text": "The sky is blue.", + "document_index": 0, + "document_title": "My Document", + "start_char_index": 20, + "end_char_index": 36, + } + ], + }, + {"text": ".", "type": "text"}, + ] + ``` Token usage: - .. code-block:: python + ```python + ai_msg = llm.invoke(messages) + ai_msg.usage_metadata + ``` - ai_msg = llm.invoke(messages) - ai_msg.usage_metadata - - .. code-block:: python - - {"input_tokens": 25, "output_tokens": 11, "total_tokens": 36} + ```python + {"input_tokens": 25, "output_tokens": 11, "total_tokens": 36} + ``` Message chunks containing token usage will be included during streaming by default: - .. code-block:: python + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full.usage_metadata + ``` - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full.usage_metadata - - .. code-block:: python - - {"input_tokens": 25, "output_tokens": 11, "total_tokens": 36} + ```python + {"input_tokens": 25, "output_tokens": 11, "total_tokens": 36} + ``` These can be disabled by setting `stream_usage=False` in the stream method, or by setting `stream_usage=False` when initializing ChatAnthropic. @@ -1084,99 +1085,99 @@ class ChatAnthropic(BaseChatModel): See the [Claude documentation](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#supported-models) for a full list. - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") - llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") + messages = [ + { + "role": "system", + "content": [ + { + "type": "text", + "text": "Below is some long context:", + }, + { + "type": "text", + "text": f"{long_text}", + "cache_control": {"type": "ephemeral"}, + }, + ], + }, + { + "role": "user", + "content": "What's that about?", + }, + ] - messages = [ - { - "role": "system", - "content": [ - { - "type": "text", - "text": "Below is some long context:", - }, - { - "type": "text", - "text": f"{long_text}", - "cache_control": {"type": "ephemeral"}, - }, - ], - }, - { - "role": "user", - "content": "What's that about?", - }, - ] + response = llm.invoke(messages) + response.usage_metadata["input_token_details"] + ``` - response = llm.invoke(messages) - response.usage_metadata["input_token_details"] - - .. code-block:: python - - {"cache_read": 0, "cache_creation": 1458} + ```python + {"cache_read": 0, "cache_creation": 1458} + ``` Alternatively, you may enable prompt caching at invocation time. You may want to conditionally cache based on runtime conditions, such as the length of the context. Alternatively, this is useful for app-level decisions about what to cache. - .. code-block:: python - - response = llm.invoke( - messages, - cache_control={"type": "ephemeral"}, - ) + ```python + response = llm.invoke( + messages, + cache_control={"type": "ephemeral"}, + ) + ``` ??? note "Extended caching" The cache lifetime is 5 minutes by default. If this is too short, you can apply one hour caching by setting `ttl` to `'1h'`. - .. code-block:: python + ```python + llm = ChatAnthropic( + model="claude-3-7-sonnet-20250219", + ) - llm = ChatAnthropic( - model="claude-3-7-sonnet-20250219", - ) + messages = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": f"{long_text}", + "cache_control": {"type": "ephemeral", "ttl": "1h"}, + }, + ], + } + ] - messages = [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": f"{long_text}", - "cache_control": {"type": "ephemeral", "ttl": "1h"}, - }, - ], - } - ] - - response = llm.invoke(messages) + response = llm.invoke(messages) + ``` Details of cached token counts will be included on the `InputTokenDetails` of response's `usage_metadata`: - .. code-block:: python + ```python + response = llm.invoke(messages) + response.usage_metadata + ``` - response = llm.invoke(messages) - response.usage_metadata - - .. code-block:: python - - { - "input_tokens": 1500, - "output_tokens": 200, - "total_tokens": 1700, - "input_token_details": { - "cache_read": 0, - "cache_creation": 1000, - "ephemeral_1h_input_tokens": 750, - "ephemeral_5m_input_tokens": 250, - }, - } + ```python + { + "input_tokens": 1500, + "output_tokens": 200, + "total_tokens": 1700, + "input_token_details": { + "cache_read": 0, + "cache_creation": 1000, + "ephemeral_1h_input_tokens": 750, + "ephemeral_5m_input_tokens": 250, + }, + } + ``` See [Claude documentation](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration-beta) for detail. @@ -1185,32 +1186,32 @@ class ChatAnthropic(BaseChatModel): Claude Sonnet 4 supports a 1-million token context window, available in beta for organizations in usage tier 4 and organizations with custom rate limits. - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic( + model="claude-sonnet-4-20250514", + betas=["context-1m-2025-08-07"], # Enable 1M context beta + ) - llm = ChatAnthropic( - model="claude-sonnet-4-20250514", - betas=["context-1m-2025-08-07"], # Enable 1M context beta - ) + long_document = \"\"\" + This is a very long document that would benefit from the extended 1M + context window... + [imagine this continues for hundreds of thousands of tokens] + \"\"\" - long_document = \"\"\" - This is a very long document that would benefit from the extended 1M - context window... - [imagine this continues for hundreds of thousands of tokens] - \"\"\" + messages = [ + HumanMessage(f\"\"\" + Please analyze this document and provide a summary: - messages = [ - HumanMessage(f\"\"\" - Please analyze this document and provide a summary: + {long_document} - {long_document} + What are the key themes and main conclusions? + \"\"\") + ] - What are the key themes and main conclusions? - \"\"\") - ] - - response = llm.invoke(messages) + response = llm.invoke(messages) + ``` See [Claude documentation](https://docs.anthropic.com/en/docs/build-with-claude/context-windows#1m-token-context-window) for detail. @@ -1220,38 +1221,37 @@ class ChatAnthropic(BaseChatModel): See LangChain [docs](https://python.langchain.com/docs/integrations/chat/anthropic/) for more detail. - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic + from langchain_core.tools import tool - from langchain_anthropic import ChatAnthropic - from langchain_core.tools import tool - - llm = ChatAnthropic( - model="claude-3-7-sonnet-20250219", - temperature=0, - model_kwargs={ - "extra_headers": { - "anthropic-beta": "token-efficient-tools-2025-02-19" - } + llm = ChatAnthropic( + model="claude-3-7-sonnet-20250219", + temperature=0, + model_kwargs={ + "extra_headers": { + "anthropic-beta": "token-efficient-tools-2025-02-19" } - ) + } + ) - @tool - def get_weather(location: str) -> str: - \"\"\"Get the weather at a location.\"\"\" - return "It's sunny." + @tool + def get_weather(location: str) -> str: + \"\"\"Get the weather at a location.\"\"\" + return "It's sunny." - llm_with_tools = llm.bind_tools([get_weather]) - response = llm_with_tools.invoke( - "What's the weather in San Francisco?" - ) - print(response.tool_calls) - print(f'Total tokens: {response.usage_metadata["total_tokens"]}') + llm_with_tools = llm.bind_tools([get_weather]) + response = llm_with_tools.invoke( + "What's the weather in San Francisco?" + ) + print(response.tool_calls) + print(f'Total tokens: {response.usage_metadata["total_tokens"]}') + ``` - .. code-block:: - - [{'name': 'get_weather', 'args': {'location': 'San Francisco'}, 'id': 'toolu_01HLjQMSb1nWmgevQUtEyz17', 'type': 'tool_call'}] - - Total tokens: 408 + ```txt + [{'name': 'get_weather', 'args': {'location': 'San Francisco'}, 'id': 'toolu_01HLjQMSb1nWmgevQUtEyz17', 'type': 'tool_call'}] + Total tokens: 408 + ``` Context management: Anthropic supports a context editing feature that will automatically manage the @@ -1260,17 +1260,17 @@ class ChatAnthropic(BaseChatModel): See [Anthropic documentation](https://docs.claude.com/en/docs/build-with-claude/context-editing) for details and configuration options. - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic - - llm = ChatAnthropic( - model="claude-sonnet-4-5-20250929", - betas=["context-management-2025-06-27"], - context_management={"edits": [{"type": "clear_tool_uses_20250919"}]}, - ) - llm_with_tools = llm.bind_tools([{"type": "web_search_20250305", "name": "web_search"}]) - response = llm_with_tools.invoke("Search for recent developments in AI") + llm = ChatAnthropic( + model="claude-sonnet-4-5-20250929", + betas=["context-management-2025-06-27"], + context_management={"edits": [{"type": "clear_tool_uses_20250919"}]}, + ) + llm_with_tools = llm.bind_tools([{"type": "web_search_20250305", "name": "web_search"}]) + response = llm_with_tools.invoke("Search for recent developments in AI") + ``` Built-in tools: See LangChain [docs](https://python.langchain.com/docs/integrations/chat/anthropic/#built-in-tools) @@ -1278,142 +1278,142 @@ class ChatAnthropic(BaseChatModel): ??? note "Web search" - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic(model="claude-3-5-haiku-latest") - llm = ChatAnthropic(model="claude-3-5-haiku-latest") + tool = { + "type": "web_search_20250305", + "name": "web_search", + "max_uses": 3, + } + llm_with_tools = llm.bind_tools([tool]) - tool = { - "type": "web_search_20250305", - "name": "web_search", - "max_uses": 3, - } - llm_with_tools = llm.bind_tools([tool]) - - response = llm_with_tools.invoke("How do I update a web app to TypeScript 5.5?") + response = llm_with_tools.invoke("How do I update a web app to TypeScript 5.5?") + ``` ??? note "Web fetch (beta)" - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic( + model="claude-3-5-haiku-latest", + betas=["web-fetch-2025-09-10"], # Enable web fetch beta + ) - llm = ChatAnthropic( - model="claude-3-5-haiku-latest", - betas=["web-fetch-2025-09-10"], # Enable web fetch beta - ) + tool = { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 3, + } + llm_with_tools = llm.bind_tools([tool]) - tool = { - "type": "web_fetch_20250910", - "name": "web_fetch", - "max_uses": 3, - } - llm_with_tools = llm.bind_tools([tool]) - - response = llm_with_tools.invoke("Please analyze the content at https://example.com/article") + response = llm_with_tools.invoke("Please analyze the content at https://example.com/article") + ``` ??? note "Code execution" - .. code-block:: python + ```python + llm = ChatAnthropic( + model="claude-sonnet-4-20250514", + betas=["code-execution-2025-05-22"], + ) - llm = ChatAnthropic( - model="claude-sonnet-4-20250514", - betas=["code-execution-2025-05-22"], - ) + tool = {"type": "code_execution_20250522", "name": "code_execution"} + llm_with_tools = llm.bind_tools([tool]) - tool = {"type": "code_execution_20250522", "name": "code_execution"} - llm_with_tools = llm.bind_tools([tool]) - - response = llm_with_tools.invoke( - "Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" - ) + response = llm_with_tools.invoke( + "Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ) + ``` ??? note "Remote MCP" - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + mcp_servers = [ + { + "type": "url", + "url": "https://mcp.deepwiki.com/mcp", + "name": "deepwiki", + "tool_configuration": { # optional configuration + "enabled": True, + "allowed_tools": ["ask_question"], + }, + "authorization_token": "PLACEHOLDER", # optional authorization + } + ] - mcp_servers = [ - { - "type": "url", - "url": "https://mcp.deepwiki.com/mcp", - "name": "deepwiki", - "tool_configuration": { # optional configuration - "enabled": True, - "allowed_tools": ["ask_question"], - }, - "authorization_token": "PLACEHOLDER", # optional authorization - } - ] + llm = ChatAnthropic( + model="claude-sonnet-4-20250514", + betas=["mcp-client-2025-04-04"], + mcp_servers=mcp_servers, + ) - llm = ChatAnthropic( - model="claude-sonnet-4-20250514", - betas=["mcp-client-2025-04-04"], - mcp_servers=mcp_servers, - ) - - response = llm.invoke( - "What transport protocols does the 2025-03-26 version of the MCP " - "spec (modelcontextprotocol/modelcontextprotocol) support?" - ) + response = llm.invoke( + "What transport protocols does the 2025-03-26 version of the MCP " + "spec (modelcontextprotocol/modelcontextprotocol) support?" + ) + ``` ??? note "Text editor" - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic + llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") - llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") + tool = {"type": "text_editor_20250124", "name": "str_replace_editor"} + llm_with_tools = llm.bind_tools([tool]) - tool = {"type": "text_editor_20250124", "name": "str_replace_editor"} - llm_with_tools = llm.bind_tools([tool]) + response = llm_with_tools.invoke( + "There's a syntax error in my primes.py file. Can you help me fix it?" + ) + print(response.text) + response.tool_calls + ``` - response = llm_with_tools.invoke( - "There's a syntax error in my primes.py file. Can you help me fix it?" - ) - print(response.text) - response.tool_calls + ```txt + I'd be happy to help you fix the syntax error in your primes.py file. First, let's look at the current content of the file to identify the error. - .. code-block:: - - I'd be happy to help you fix the syntax error in your primes.py file. First, let's look at the current content of the file to identify the error. - - [{'name': 'str_replace_editor', - 'args': {'command': 'view', 'path': '/repo/primes.py'}, - 'id': 'toolu_01VdNgt1YV7kGfj9LFLm6HyQ', - 'type': 'tool_call'}] + [{'name': 'str_replace_editor', + 'args': {'command': 'view', 'path': '/repo/primes.py'}, + 'id': 'toolu_01VdNgt1YV7kGfj9LFLm6HyQ', + 'type': 'tool_call'}] + ``` ??? note "Memory tool" - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic - - llm = ChatAnthropic( - model="claude-sonnet-4-5-20250929", - betas=["context-management-2025-06-27"], - ) - llm_with_tools = llm.bind_tools([{"type": "memory_20250818", "name": "memory"}]) - response = llm_with_tools.invoke("What are my interests?") + llm = ChatAnthropic( + model="claude-sonnet-4-5-20250929", + betas=["context-management-2025-06-27"], + ) + llm_with_tools = llm.bind_tools([{"type": "memory_20250818", "name": "memory"}]) + response = llm_with_tools.invoke("What are my interests?") + ``` Response metadata - .. code-block:: python - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - - { - "id": "msg_013xU6FHEGEq76aP4RgFerVT", - "model": "claude-3-7-sonnet-20250219", - "stop_reason": "end_turn", - "stop_sequence": None, - "usage": {"input_tokens": 25, "output_tokens": 11}, - } + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` + ```python + { + "id": "msg_013xU6FHEGEq76aP4RgFerVT", + "model": "claude-3-7-sonnet-20250219", + "stop_reason": "end_turn", + "stop_sequence": None, + "usage": {"input_tokens": 25, "output_tokens": 11}, + } + ``` """ # noqa: E501 model_config = ConfigDict( @@ -1922,217 +1922,214 @@ class ChatAnthropic(BaseChatModel): kwargs: Any additional parameters are passed directly to `bind`. Example: - - .. code-block:: python - - from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel, Field + ```python + from langchain_anthropic import ChatAnthropic + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPrice(BaseModel): - '''Get the price of a specific product.''' + class GetPrice(BaseModel): + '''Get the price of a specific product.''' - product: str = Field(..., description="The product to look up.") + product: str = Field(..., description="The product to look up.") - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - llm_with_tools = llm.bind_tools([GetWeather, GetPrice]) - llm_with_tools.invoke( - "What is the weather like in San Francisco", - ) - # -> AIMessage( - # content=[ - # {'text': '\nBased on the user\'s question, the relevant function to call is GetWeather, which requires the "location" parameter.\n\nThe user has directly specified the location as "San Francisco". Since San Francisco is a well known city, I can reasonably infer they mean San Francisco, CA without needing the state specified.\n\nAll the required parameters are provided, so I can proceed with the API call.\n', 'type': 'text'}, - # {'text': None, 'type': 'tool_use', 'id': 'toolu_01SCgExKzQ7eqSkMHfygvYuu', 'name': 'GetWeather', 'input': {'location': 'San Francisco, CA'}} - # ], - # response_metadata={'id': 'msg_01GM3zQtoFv8jGQMW7abLnhi', 'model': 'claude-3-5-sonnet-latest', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 487, 'output_tokens': 145}}, - # id='run-87b1331e-9251-4a68-acef-f0a018b639cc-0' - # ) + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) + llm_with_tools = llm.bind_tools([GetWeather, GetPrice]) + llm_with_tools.invoke( + "What is the weather like in San Francisco", + ) + # -> AIMessage( + # content=[ + # {'text': '\nBased on the user\'s question, the relevant function to call is GetWeather, which requires the "location" parameter.\n\nThe user has directly specified the location as "San Francisco". Since San Francisco is a well known city, I can reasonably infer they mean San Francisco, CA without needing the state specified.\n\nAll the required parameters are provided, so I can proceed with the API call.\n', 'type': 'text'}, + # {'text': None, 'type': 'tool_use', 'id': 'toolu_01SCgExKzQ7eqSkMHfygvYuu', 'name': 'GetWeather', 'input': {'location': 'San Francisco, CA'}} + # ], + # response_metadata={'id': 'msg_01GM3zQtoFv8jGQMW7abLnhi', 'model': 'claude-3-5-sonnet-latest', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 487, 'output_tokens': 145}}, + # id='run-87b1331e-9251-4a68-acef-f0a018b639cc-0' + # ) + ``` Example — force tool call with tool_choice `'any'`: - .. code-block:: python - - from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel, Field + ```python + from langchain_anthropic import ChatAnthropic + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPrice(BaseModel): - '''Get the price of a specific product.''' + class GetPrice(BaseModel): + '''Get the price of a specific product.''' - product: str = Field(..., description="The product to look up.") + product: str = Field(..., description="The product to look up.") - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - llm_with_tools = llm.bind_tools([GetWeather, GetPrice], tool_choice="any") - llm_with_tools.invoke( - "what is the weather like in San Francisco", - ) - + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) + llm_with_tools = llm.bind_tools([GetWeather, GetPrice], tool_choice="any") + llm_with_tools.invoke( + "what is the weather like in San Francisco", + ) + ``` Example — force specific tool call with tool_choice `''`: - .. code-block:: python - - from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel, Field + ```python + from langchain_anthropic import ChatAnthropic + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPrice(BaseModel): - '''Get the price of a specific product.''' + class GetPrice(BaseModel): + '''Get the price of a specific product.''' - product: str = Field(..., description="The product to look up.") + product: str = Field(..., description="The product to look up.") - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - llm_with_tools = llm.bind_tools([GetWeather, GetPrice], tool_choice="GetWeather") - llm_with_tools.invoke("What is the weather like in San Francisco") + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) + llm_with_tools = llm.bind_tools([GetWeather, GetPrice], tool_choice="GetWeather") + llm_with_tools.invoke("What is the weather like in San Francisco") + ``` Example — cache specific tools: - .. code-block:: python - - from langchain_anthropic import ChatAnthropic, convert_to_anthropic_tool - from pydantic import BaseModel, Field + ```python + from langchain_anthropic import ChatAnthropic, convert_to_anthropic_tool + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPrice(BaseModel): - '''Get the price of a specific product.''' + class GetPrice(BaseModel): + '''Get the price of a specific product.''' - product: str = Field(..., description="The product to look up.") + product: str = Field(..., description="The product to look up.") - # We'll convert our pydantic class to the anthropic tool format - # before passing to bind_tools so that we can set the 'cache_control' - # field on our tool. - cached_price_tool = convert_to_anthropic_tool(GetPrice) - # Currently the only supported "cache_control" value is - # {"type": "ephemeral"}. - cached_price_tool["cache_control"] = {"type": "ephemeral"} + # We'll convert our pydantic class to the anthropic tool format + # before passing to bind_tools so that we can set the 'cache_control' + # field on our tool. + cached_price_tool = convert_to_anthropic_tool(GetPrice) + # Currently the only supported "cache_control" value is + # {"type": "ephemeral"}. + cached_price_tool["cache_control"] = {"type": "ephemeral"} - # We need to pass in extra headers to enable use of the beta cache - # control API. - llm = ChatAnthropic( - model="claude-3-5-sonnet-latest", - temperature=0, - ) - llm_with_tools = llm.bind_tools([GetWeather, cached_price_tool]) - llm_with_tools.invoke("What is the weather like in San Francisco") + # We need to pass in extra headers to enable use of the beta cache + # control API. + llm = ChatAnthropic( + model="claude-3-5-sonnet-latest", + temperature=0, + ) + llm_with_tools = llm.bind_tools([GetWeather, cached_price_tool]) + llm_with_tools.invoke("What is the weather like in San Francisco") + ``` This outputs: - .. code-block:: python - - AIMessage( - content=[ - { - "text": "Certainly! I can help you find out the current weather in San Francisco. To get this information, I'll use the GetWeather function. Let me fetch that data for you right away.", - "type": "text", - }, - { - "id": "toolu_01TS5h8LNo7p5imcG7yRiaUM", - "input": {"location": "San Francisco, CA"}, - "name": "GetWeather", - "type": "tool_use", - }, - ], - response_metadata={ - "id": "msg_01Xg7Wr5inFWgBxE5jH9rpRo", - "model": "claude-3-5-sonnet-latest", - "stop_reason": "tool_use", - "stop_sequence": None, - "usage": { - "input_tokens": 171, - "output_tokens": 96, - "cache_creation_input_tokens": 1470, - "cache_read_input_tokens": 0, - }, + ```python + AIMessage( + content=[ + { + "text": "Certainly! I can help you find out the current weather in San Francisco. To get this information, I'll use the GetWeather function. Let me fetch that data for you right away.", + "type": "text", }, - id="run-b36a5b54-5d69-470e-a1b0-b932d00b089e-0", - tool_calls=[ - { - "name": "GetWeather", - "args": {"location": "San Francisco, CA"}, - "id": "toolu_01TS5h8LNo7p5imcG7yRiaUM", - "type": "tool_call", - } - ], - usage_metadata={ + { + "id": "toolu_01TS5h8LNo7p5imcG7yRiaUM", + "input": {"location": "San Francisco, CA"}, + "name": "GetWeather", + "type": "tool_use", + }, + ], + response_metadata={ + "id": "msg_01Xg7Wr5inFWgBxE5jH9rpRo", + "model": "claude-3-5-sonnet-latest", + "stop_reason": "tool_use", + "stop_sequence": None, + "usage": { "input_tokens": 171, "output_tokens": 96, - "total_tokens": 267, + "cache_creation_input_tokens": 1470, + "cache_read_input_tokens": 0, }, - ) + }, + id="run-b36a5b54-5d69-470e-a1b0-b932d00b089e-0", + tool_calls=[ + { + "name": "GetWeather", + "args": {"location": "San Francisco, CA"}, + "id": "toolu_01TS5h8LNo7p5imcG7yRiaUM", + "type": "tool_call", + } + ], + usage_metadata={ + "input_tokens": 171, + "output_tokens": 96, + "total_tokens": 267, + }, + ) + ``` If we invoke the tool again, we can see that the "usage" information in the AIMessage.response_metadata shows that we had a cache hit: - .. code-block:: python - - AIMessage( - content=[ - { - "text": "To get the current weather in San Francisco, I can use the GetWeather function. Let me check that for you.", - "type": "text", - }, - { - "id": "toolu_01HtVtY1qhMFdPprx42qU2eA", - "input": {"location": "San Francisco, CA"}, - "name": "GetWeather", - "type": "tool_use", - }, - ], - response_metadata={ - "id": "msg_016RfWHrRvW6DAGCdwB6Ac64", - "model": "claude-3-5-sonnet-latest", - "stop_reason": "tool_use", - "stop_sequence": None, - "usage": { - "input_tokens": 171, - "output_tokens": 82, - "cache_creation_input_tokens": 0, - "cache_read_input_tokens": 1470, - }, + ```python + AIMessage( + content=[ + { + "text": "To get the current weather in San Francisco, I can use the GetWeather function. Let me check that for you.", + "type": "text", }, - id="run-88b1f825-dcb7-4277-ac27-53df55d22001-0", - tool_calls=[ - { - "name": "GetWeather", - "args": {"location": "San Francisco, CA"}, - "id": "toolu_01HtVtY1qhMFdPprx42qU2eA", - "type": "tool_call", - } - ], - usage_metadata={ + { + "id": "toolu_01HtVtY1qhMFdPprx42qU2eA", + "input": {"location": "San Francisco, CA"}, + "name": "GetWeather", + "type": "tool_use", + }, + ], + response_metadata={ + "id": "msg_016RfWHrRvW6DAGCdwB6Ac64", + "model": "claude-3-5-sonnet-latest", + "stop_reason": "tool_use", + "stop_sequence": None, + "usage": { "input_tokens": 171, "output_tokens": 82, - "total_tokens": 253, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 1470, }, - ) - + }, + id="run-88b1f825-dcb7-4277-ac27-53df55d22001-0", + tool_calls=[ + { + "name": "GetWeather", + "args": {"location": "San Francisco, CA"}, + "id": "toolu_01HtVtY1qhMFdPprx42qU2eA", + "type": "tool_call", + } + ], + usage_metadata={ + "input_tokens": 171, + "output_tokens": 82, + "total_tokens": 253, + }, + ) + ``` """ # noqa: E501 formatted_tools = [ tool if _is_builtin_tool(tool) else convert_to_anthropic_tool(tool) @@ -2218,80 +2215,80 @@ class ChatAnthropic(BaseChatModel): Example: Pydantic schema (include_raw=False): - .. code-block:: python - - from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel + ```python + from langchain_anthropic import ChatAnthropic + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) + structured_llm = llm.with_structured_output(AnswerWithJustification) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: Pydantic schema (include_raw=True): - .. code-block:: python - - from langchain_anthropic import ChatAnthropic - from pydantic import BaseModel + ```python + from langchain_anthropic import ChatAnthropic + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification, include_raw=True) + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` Example: Dict schema (include_raw=False): - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic - from langchain_anthropic import ChatAnthropic - - schema = { - "name": "AnswerWithJustification", - "description": "An answer to the user question along with justification for the answer.", - "input_schema": { - "type": "object", - "properties": { - "answer": {"type": "string"}, - "justification": {"type": "string"}, - }, - "required": ["answer", "justification"], + schema = { + "name": "AnswerWithJustification", + "description": "An answer to the user question along with justification for the answer.", + "input_schema": { + "type": "object", + "properties": { + "answer": {"type": "string"}, + "justification": {"type": "string"}, }, - } - llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) - structured_llm = llm.with_structured_output(schema) + "required": ["answer", "justification"], + }, + } + llm = ChatAnthropic(model="claude-3-5-sonnet-latest", temperature=0) + structured_llm = llm.with_structured_output(schema) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> { - # 'answer': 'They weigh the same', - # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` !!! warning "Behavior changed in 0.1.22" Added support for TypedDict class as `schema`. @@ -2355,50 +2352,50 @@ class ChatAnthropic(BaseChatModel): Basic usage: - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic + from langchain_core.messages import HumanMessage, SystemMessage - from langchain_anthropic import ChatAnthropic - from langchain_core.messages import HumanMessage, SystemMessage + llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") - llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") + messages = [ + SystemMessage(content="You are a scientist"), + HumanMessage(content="Hello, Claude"), + ] + llm.get_num_tokens_from_messages(messages) + ``` - messages = [ - SystemMessage(content="You are a scientist"), - HumanMessage(content="Hello, Claude"), - ] - llm.get_num_tokens_from_messages(messages) - - .. code-block:: - - 14 + ```txt + 14 + ``` Pass tool schemas: - .. code-block:: python + ```python + from langchain_anthropic import ChatAnthropic + from langchain_core.messages import HumanMessage + from langchain_core.tools import tool - from langchain_anthropic import ChatAnthropic - from langchain_core.messages import HumanMessage - from langchain_core.tools import tool + llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") - llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") + @tool(parse_docstring=True) + def get_weather(location: str) -> str: + \"\"\"Get the current weather in a given location - @tool(parse_docstring=True) - def get_weather(location: str) -> str: - \"\"\"Get the current weather in a given location + Args: + location: The city and state, e.g. San Francisco, CA + \"\"\" + return "Sunny" - Args: - location: The city and state, e.g. San Francisco, CA - \"\"\" - return "Sunny" + messages = [ + HumanMessage(content="What's the weather like in San Francisco?"), + ] + llm.get_num_tokens_from_messages(messages, tools=[get_weather]) + ``` - messages = [ - HumanMessage(content="What's the weather like in San Francisco?"), - ] - llm.get_num_tokens_from_messages(messages, tools=[get_weather]) - - .. code-block:: - - 403 + ```txt + 403 + ``` !!! warning "Behavior changed in 0.3.0" Uses Anthropic's [token counting API](https://docs.anthropic.com/en/docs/build-with-claude/token-counting) to count tokens in messages. diff --git a/libs/partners/anthropic/langchain_anthropic/llms.py b/libs/partners/anthropic/langchain_anthropic/llms.py index 0398202597a..945d353fb85 100644 --- a/libs/partners/anthropic/langchain_anthropic/llms.py +++ b/libs/partners/anthropic/langchain_anthropic/llms.py @@ -133,12 +133,11 @@ class AnthropicLLM(LLM, _AnthropicCommon): set with your API key, or pass it as a named parameter to the constructor. Example: - .. code-block:: python - - from langchain_anthropic import AnthropicLLM - - model = AnthropicLLM() + ```python + from langchain_anthropic import AnthropicLLM + model = AnthropicLLM() + ``` """ model_config = ConfigDict( @@ -261,12 +260,11 @@ class AnthropicLLM(LLM, _AnthropicCommon): The string generated by the model. Example: - .. code-block:: python - - prompt = "What are the biggest risks facing humanity?" - prompt = f"\n\nHuman: {prompt}\n\nAssistant:" - response = model.invoke(prompt) - + ```python + prompt = "What are the biggest risks facing humanity?" + prompt = f"\n\nHuman: {prompt}\n\nAssistant:" + response = model.invoke(prompt) + ``` """ if self.streaming: completion = "" @@ -347,15 +345,13 @@ class AnthropicLLM(LLM, _AnthropicCommon): A generator representing the stream of tokens from Anthropic. Example: - - .. code-block:: python - - prompt = "Write a poem about a stream." - prompt = f"\n\nHuman: {prompt}\n\nAssistant:" - generator = anthropic.stream(prompt) - for token in generator: - yield token - + ```python + prompt = "Write a poem about a stream." + prompt = f"\n\nHuman: {prompt}\n\nAssistant:" + generator = anthropic.stream(prompt) + for token in generator: + yield token + ``` """ stop = self._get_anthropic_stop(stop) params = {**self._default_params, **kwargs} @@ -394,14 +390,13 @@ class AnthropicLLM(LLM, _AnthropicCommon): A generator representing the stream of tokens from Anthropic. Example: - .. code-block:: python - - prompt = "Write a poem about a stream." - prompt = f"\n\nHuman: {prompt}\n\nAssistant:" - generator = anthropic.stream(prompt) - for token in generator: - yield token - + ```python + prompt = "Write a poem about a stream." + prompt = f"\n\nHuman: {prompt}\n\nAssistant:" + generator = anthropic.stream(prompt) + for token in generator: + yield token + ``` """ stop = self._get_anthropic_stop(stop) params = {**self._default_params, **kwargs} diff --git a/libs/partners/chroma/langchain_chroma/vectorstores.py b/libs/partners/chroma/langchain_chroma/vectorstores.py index e0819cae0a8..94787621ae7 100644 --- a/libs/partners/chroma/langchain_chroma/vectorstores.py +++ b/libs/partners/chroma/langchain_chroma/vectorstores.py @@ -152,9 +152,9 @@ class Chroma(VectorStore): Setup: Install `chromadb`, `langchain-chroma` packages: - .. code-block:: bash - - pip install -qU chromadb langchain-chroma + ```bash + pip install -qU chromadb langchain-chroma + ``` Key init args — indexing params: collection_name: str @@ -185,114 +185,110 @@ class Chroma(VectorStore): Database name. Required for Chroma Cloud connections. Default is 'default_database'. Instantiate: - .. code-block:: python + ```python + from langchain_chroma import Chroma + from langchain_openai import OpenAIEmbeddings - from langchain_chroma import Chroma - from langchain_openai import OpenAIEmbeddings - - vector_store = Chroma( - collection_name="foo", - embedding_function=OpenAIEmbeddings(), - # other params... - ) + vector_store = Chroma( + collection_name="foo", + embedding_function=OpenAIEmbeddings(), + # other params... + ) + ``` Add Documents: - .. code-block:: python + ```python + from langchain_core.documents import Document - from langchain_core.documents import Document + document_1 = Document(page_content="foo", metadata={"baz": "bar"}) + document_2 = Document(page_content="thud", metadata={"bar": "baz"}) + document_3 = Document(page_content="i will be deleted :(") - document_1 = Document(page_content="foo", metadata={"baz": "bar"}) - document_2 = Document(page_content="thud", metadata={"bar": "baz"}) - document_3 = Document(page_content="i will be deleted :(") - - documents = [document_1, document_2, document_3] - ids = ["1", "2", "3"] - vector_store.add_documents(documents=documents, ids=ids) + documents = [document_1, document_2, document_3] + ids = ["1", "2", "3"] + vector_store.add_documents(documents=documents, ids=ids) + ``` Update Documents: - .. code-block:: python + ```python + updated_document = Document( + page_content="qux", + metadata={"bar": "baz"}, + ) - updated_document = Document( - page_content="qux", - metadata={"bar": "baz"}, - ) - - vector_store.update_documents(ids=["1"], documents=[updated_document]) + vector_store.update_documents(ids=["1"], documents=[updated_document]) + ``` Delete Documents: - .. code-block:: python - - vector_store.delete(ids=["3"]) + ```python + vector_store.delete(ids=["3"]) + ``` Search: - .. code-block:: python - - results = vector_store.similarity_search(query="thud", k=1) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - *thud[{"baz": "bar"}] + ```python + results = vector_store.similarity_search(query="thud", k=1) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` + ```python + *thud[{"baz": "bar"}] + ``` Search with filter: - .. code-block:: python - - results = vector_store.similarity_search( - query="thud", k=1, filter={"baz": "bar"} - ) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - *foo[{"baz": "bar"}] + ```python + results = vector_store.similarity_search( + query="thud", k=1, filter={"baz": "bar"} + ) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` + ```python + *foo[{"baz": "bar"}] + ``` Search with score: - .. code-block:: python - - results = vector_store.similarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - * [SIM=0.000000] qux [{'bar': 'baz', 'baz': 'bar'}] + ```python + results = vector_store.similarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` + ```python + * [SIM=0.000000] qux [{'bar': 'baz', 'baz': 'bar'}] + ``` Async: - .. code-block:: python + ```python + # add documents + # await vector_store.aadd_documents(documents=documents, ids=ids) - # add documents - # await vector_store.aadd_documents(documents=documents, ids=ids) + # delete documents + # await vector_store.adelete(ids=["3"]) - # delete documents - # await vector_store.adelete(ids=["3"]) + # search + # results = vector_store.asimilarity_search(query="thud",k=1) - # search - # results = vector_store.asimilarity_search(query="thud",k=1) + # search with score + results = await vector_store.asimilarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - # search with score - results = await vector_store.asimilarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - * [SIM=0.335463] foo [{'baz': 'bar'}] + ```python + * [SIM=0.335463] foo [{'baz': 'bar'}] + ``` Use as Retriever: - .. code-block:: python - - retriever = vector_store.as_retriever( - search_type="mmr", - search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, - ) - retriever.invoke("thud") - - .. code-block:: python - - [Document(metadata={"baz": "bar"}, page_content="thud")] + ```python + retriever = vector_store.as_retriever( + search_type="mmr", + search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, + ) + retriever.invoke("thud") + ``` + ```python + [Document(metadata={"baz": "bar"}, page_content="thud")] + ``` """ # noqa: E501 _LANGCHAIN_DEFAULT_COLLECTION_NAME = "langchain" diff --git a/libs/partners/deepseek/langchain_deepseek/chat_models.py b/libs/partners/deepseek/langchain_deepseek/chat_models.py index 8512b7dbc8a..7abfd3ebd63 100644 --- a/libs/partners/deepseek/langchain_deepseek/chat_models.py +++ b/libs/partners/deepseek/langchain_deepseek/chat_models.py @@ -32,10 +32,10 @@ class ChatDeepSeek(BaseChatOpenAI): Setup: Install `langchain-deepseek` and set environment variable `DEEPSEEK_API_KEY`. - .. code-block:: bash - - pip install -U langchain-deepseek - export DEEPSEEK_API_KEY="your-api-key" + ```bash + pip install -U langchain-deepseek + export DEEPSEEK_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -56,115 +56,111 @@ class ChatDeepSeek(BaseChatOpenAI): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_deepseek import ChatDeepSeek - from langchain_deepseek import ChatDeepSeek - - llm = ChatDeepSeek( - model="...", - temperature=0, - max_tokens=None, - timeout=None, - max_retries=2, - # api_key="...", - # other params... - ) + llm = ChatDeepSeek( + model="...", + temperature=0, + max_tokens=None, + timeout=None, + max_retries=2, + # api_key="...", + # other params... + ) + ``` Invoke: - .. code-block:: python - - messages = [ - ("system", "You are a helpful translator. Translate the user sentence to French."), - ("human", "I love programming."), - ] - llm.invoke(messages) + ```python + messages = [ + ("system", "You are a helpful translator. Translate the user sentence to French."), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` Stream: - .. code-block:: python - - for chunk in llm.stream(messages): - print(chunk.text, end="") - - .. code-block:: python - - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full + ```python + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke(messages) - await llm.ainvoke(messages) + # stream: + # async for chunk in (await llm.astream(messages)) - # stream: - # async for chunk in (await llm.astream(messages)) - - # batch: - # await llm.abatch([messages]) + # batch: + # await llm.abatch([messages]) + ``` Tool calling: - .. code-block:: python - - from pydantic import BaseModel, Field + ```python + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") - ai_msg.tool_calls + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke("Which city is hotter today and which is bigger: LA or NY?") + ai_msg.tool_calls + ``` See `ChatDeepSeek.bind_tools()` method for more. Structured output: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from pydantic import BaseModel, Field + from pydantic import BaseModel, Field - class Joke(BaseModel): - '''Joke to tell user.''' + 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") - rating: int | None = Field(description="How funny the joke is, from 1 to 10") + setup: str = Field(description="The setup of the joke") + punchline: str = Field(description="The punchline to the joke") + rating: int | None = Field(description="How funny the joke is, from 1 to 10") - structured_llm = llm.with_structured_output(Joke) - structured_llm.invoke("Tell me a joke about cats") + structured_llm = llm.with_structured_output(Joke) + structured_llm.invoke("Tell me a joke about cats") + ``` See `ChatDeepSeek.with_structured_output()` for more. Token usage: - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.usage_metadata - - .. code-block:: python - - {"input_tokens": 28, "output_tokens": 5, "total_tokens": 33} - + ```python + ai_msg = llm.invoke(messages) + ai_msg.usage_metadata + ``` + ```python + {"input_tokens": 28, "output_tokens": 5, "total_tokens": 33} + ``` Response metadata - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` """ # noqa: E501 model_name: str = Field(alias="model") diff --git a/libs/partners/exa/langchain_exa/tools.py b/libs/partners/exa/langchain_exa/tools.py index eeb43cdd546..a6957f1f1eb 100644 --- a/libs/partners/exa/langchain_exa/tools.py +++ b/libs/partners/exa/langchain_exa/tools.py @@ -24,64 +24,63 @@ class ExaSearchResults(BaseTool): # type: ignore[override] Setup: Install `langchain-exa` and set environment variable `EXA_API_KEY`. - .. code-block:: bash - - pip install -U langchain-exa - export EXA_API_KEY="your-api-key" + ```bash + pip install -U langchain-exa + export EXA_API_KEY="your-api-key" + ``` Instantiation: - .. code-block:: python + ```python + from langchain-exa import ExaSearchResults - from langchain-exa import ExaSearchResults - - tool = ExaSearchResults() + tool = ExaSearchResults() + ``` Invocation with args: - .. code-block:: python + ```python + tool.invoke({"query": "what is the weather in SF", "num_results": 1}) + ``` - tool.invoke({"query": "what is the weather in SF", "num_results": 1}) - - .. code-block:: python - - SearchResponse( - results=[ - Result( - url="https://www.wunderground.com/weather/37.8,-122.4", - id="https://www.wunderground.com/weather/37.8,-122.4", - title="San Francisco, CA Weather Conditionsstar_ratehome", - score=0.1843988299369812, - published_date="2023-02-23T01:17:06.594Z", - author=None, - text="The time period when the sun is no more than 6 degrees below the horizon at either sunrise or sunset. The horizon should be clearly defined and the brightest stars should be visible under good atmospheric conditions (i.e. no moonlight, or other lights). One still should be able to carry on ordinary outdoor activities. The time period when the sun is between 6 and 12 degrees below the horizon at either sunrise or sunset. The horizon is well defined and the outline of objects might be visible without artificial light. Ordinary outdoor activities are not possible at this time without extra illumination. The time period when the sun is between 12 and 18 degrees below the horizon at either sunrise or sunset. The sun does not contribute to the illumination of the sky before this time in the morning, or after this time in the evening. In the beginning of morning astronomical twilight and at the end of astronomical twilight in the evening, sky illumination is very faint, and might be undetectable. The time of Civil Sunset minus the time of Civil Sunrise. The time of Actual Sunset minus the time of Actual Sunrise. The change in length of daylight between today and tomorrow is also listed when available.", - highlights=None, - highlight_scores=None, - summary=None, - ) - ], - autoprompt_string=None, - ) + ```python + SearchResponse( + results=[ + Result( + url="https://www.wunderground.com/weather/37.8,-122.4", + id="https://www.wunderground.com/weather/37.8,-122.4", + title="San Francisco, CA Weather Conditionsstar_ratehome", + score=0.1843988299369812, + published_date="2023-02-23T01:17:06.594Z", + author=None, + text="The time period when the sun is no more than 6 degrees below the horizon at either sunrise or sunset. The horizon should be clearly defined and the brightest stars should be visible under good atmospheric conditions (i.e. no moonlight, or other lights). One still should be able to carry on ordinary outdoor activities. The time period when the sun is between 6 and 12 degrees below the horizon at either sunrise or sunset. The horizon is well defined and the outline of objects might be visible without artificial light. Ordinary outdoor activities are not possible at this time without extra illumination. The time period when the sun is between 12 and 18 degrees below the horizon at either sunrise or sunset. The sun does not contribute to the illumination of the sky before this time in the morning, or after this time in the evening. In the beginning of morning astronomical twilight and at the end of astronomical twilight in the evening, sky illumination is very faint, and might be undetectable. The time of Civil Sunset minus the time of Civil Sunrise. The time of Actual Sunset minus the time of Actual Sunrise. The change in length of daylight between today and tomorrow is also listed when available.", + highlights=None, + highlight_scores=None, + summary=None, + ) + ], + autoprompt_string=None, + ) + ``` Invocation with ToolCall: - .. code-block:: python - - tool.invoke( - { - "args": {"query": "what is the weather in SF", "num_results": 1}, - "id": "1", - "name": tool.name, - "type": "tool_call", - } - ) - - .. code-block:: python - - ToolMessage( - content="Title: San Francisco, CA Weather Conditionsstar_ratehome\nURL: https://www.wunderground.com/weather/37.8,-122.4\nID: https://www.wunderground.com/weather/37.8,-122.4\nScore: 0.1843988299369812\nPublished Date: 2023-02-23T01:17:06.594Z\nAuthor: None\nText: The time period when the sun is no more than 6 degrees below the horizon at either sunrise or sunset. The horizon should be clearly defined and the brightest stars should be visible under good atmospheric conditions (i.e. no moonlight, or other lights). One still should be able to carry on ordinary outdoor activities. The time period when the sun is between 6 and 12 degrees below the horizon at either sunrise or sunset. The horizon is well defined and the outline of objects might be visible without artificial light. Ordinary outdoor activities are not possible at this time without extra illumination. The time period when the sun is between 12 and 18 degrees below the horizon at either sunrise or sunset. The sun does not contribute to the illumination of the sky before this time in the morning, or after this time in the evening. In the beginning of morning astronomical twilight and at the end of astronomical twilight in the evening, sky illumination is very faint, and might be undetectable. The time of Civil Sunset minus the time of Civil Sunrise. The time of Actual Sunset minus the time of Actual Sunrise. The change in length of daylight between today and tomorrow is also listed when available.\nHighlights: None\nHighlight Scores: None\nSummary: None\n", - name="exa_search_results_json", - tool_call_id="1", - ) + ```python + tool.invoke( + { + "args": {"query": "what is the weather in SF", "num_results": 1}, + "id": "1", + "name": tool.name, + "type": "tool_call", + } + ) + ``` + ```python + ToolMessage( + content="Title: San Francisco, CA Weather Conditionsstar_ratehome\nURL: https://www.wunderground.com/weather/37.8,-122.4\nID: https://www.wunderground.com/weather/37.8,-122.4\nScore: 0.1843988299369812\nPublished Date: 2023-02-23T01:17:06.594Z\nAuthor: None\nText: The time period when the sun is no more than 6 degrees below the horizon at either sunrise or sunset. The horizon should be clearly defined and the brightest stars should be visible under good atmospheric conditions (i.e. no moonlight, or other lights). One still should be able to carry on ordinary outdoor activities. The time period when the sun is between 6 and 12 degrees below the horizon at either sunrise or sunset. The horizon is well defined and the outline of objects might be visible without artificial light. Ordinary outdoor activities are not possible at this time without extra illumination. The time period when the sun is between 12 and 18 degrees below the horizon at either sunrise or sunset. The sun does not contribute to the illumination of the sky before this time in the morning, or after this time in the evening. In the beginning of morning astronomical twilight and at the end of astronomical twilight in the evening, sky illumination is very faint, and might be undetectable. The time of Civil Sunset minus the time of Civil Sunrise. The time of Actual Sunset minus the time of Actual Sunrise. The change in length of daylight between today and tomorrow is also listed when available.\nHighlights: None\nHighlight Scores: None\nSummary: None\n", + name="exa_search_results_json", + tool_call_id="1", + ) + ``` """ # noqa: E501 name: str = "exa_search_results_json" diff --git a/libs/partners/fireworks/langchain_fireworks/chat_models.py b/libs/partners/fireworks/langchain_fireworks/chat_models.py index 36021dc87ce..52be161d902 100644 --- a/libs/partners/fireworks/langchain_fireworks/chat_models.py +++ b/libs/partners/fireworks/langchain_fireworks/chat_models.py @@ -265,14 +265,13 @@ class ChatFireworks(BaseChatModel): can be passed in, even if not explicitly saved on this class. Example: - .. code-block:: python - - from langchain_fireworks.chat_models import ChatFireworks - - fireworks = ChatFireworks( - model_name="accounts/fireworks/models/llama-v3p1-8b-instruct" - ) + ```python + from langchain_fireworks.chat_models import ChatFireworks + fireworks = ChatFireworks( + model_name="accounts/fireworks/models/llama-v3p1-8b-instruct" + ) + ``` """ @property @@ -730,191 +729,193 @@ class ChatFireworks(BaseChatModel): Example: schema=Pydantic class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_fireworks import ChatFireworks - from pydantic import BaseModel, Field + from langchain_fireworks import ChatFireworks + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - # If we provide default values and/or descriptions for fields, these will be passed - # to the model. This is an important part of improving a model's ability to - # correctly return structured outputs. - justification: str | None = Field( - default=None, description="A justification for the answer." - ) - - - llm = ChatFireworks( - model="accounts/fireworks/models/firefunction-v1", - 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: str + # If we provide default values and/or descriptions for fields, these will be passed + # to the model. This is an important part of improving a model's ability to + # correctly return structured outputs. + justification: str | None = Field( + default=None, description="A justification for the answer." ) - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + + llm = ChatFireworks( + model="accounts/fireworks/models/firefunction-v1", + 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='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: schema=Pydantic class, method="function_calling", include_raw=True: - .. code-block:: python - - from langchain_fireworks import ChatFireworks - from pydantic import BaseModel + ```python + from langchain_fireworks import ChatFireworks + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatFireworks( - model="accounts/fireworks/models/firefunction-v1", - temperature=0, - ) - structured_llm = llm.with_structured_output( - AnswerWithJustification, include_raw=True - ) + llm = ChatFireworks( + model="accounts/fireworks/models/firefunction-v1", + 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` Example: schema=TypedDict class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict - # 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_fireworks import ChatFireworks + from langchain_fireworks import ChatFireworks - class AnswerWithJustification(TypedDict): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: Annotated[ - str | None, None, "A justification for the answer." - ] + answer: str + justification: Annotated[ + str | None, None, "A justification for the answer." + ] - llm = ChatFireworks( - model="accounts/fireworks/models/firefunction-v1", - temperature=0, - ) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatFireworks( + model="accounts/fireworks/models/firefunction-v1", + 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` Example: schema=OpenAI function schema, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from langchain_fireworks import ChatFireworks - from langchain_fireworks import ChatFireworks - - 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", - }, + 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"], }, - } + "required": ["answer"], + }, + } - llm = ChatFireworks( - model="accounts/fireworks/models/firefunction-v1", - temperature=0, - ) - structured_llm = llm.with_structured_output(oai_schema) + llm = ChatFireworks( + model="accounts/fireworks/models/firefunction-v1", + 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` Example: schema=Pydantic class, method="json_mode", include_raw=True: - .. code-block:: + ```python + from langchain_fireworks import ChatFireworks + from pydantic import BaseModel - from langchain_fireworks import ChatFireworks - from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - answer: str - justification: str + class AnswerWithJustification(BaseModel): + answer: str + justification: str - llm = ChatFireworks(model="accounts/fireworks/models/firefunction-v1", 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'. " - "What's heavier a pound of bricks or a pound of feathers?" - ) - # -> { - # 'raw': AIMessage(content='{"answer": "They are both the same weight.", "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight."}'), - # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), - # 'parsing_error': None - # } + llm = ChatFireworks( + model="accounts/fireworks/models/firefunction-v1", 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'. " + "What's heavier a pound of bricks or a pound of feathers?" + ) + # -> { + # 'raw': AIMessage(content='{"answer": "They are both the same weight.", "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight."}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + ``` Example: schema=None, method="json_mode", include_raw=True: - .. code-block:: + ```python + structured_llm = llm.with_structured_output( + method="json_mode", include_raw=True + ) - 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'. " - "What's heavier a pound of bricks or a pound of feathers?" - ) - # -> { - # 'raw': AIMessage(content='{"answer": "They are both the same weight.", "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight."}'), - # 'parsed': { - # 'answer': 'They are both the same weight.', - # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' - # }, - # 'parsing_error': None - # } + structured_llm.invoke( + "Answer the following question. " + "Make sure to return a JSON blob with keys 'answer' and 'justification'. " + "What's heavier a pound of bricks or a pound of feathers?" + ) + # -> { + # 'raw': AIMessage(content='{"answer": "They are both the same weight.", "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight."}'), + # 'parsed': { + # 'answer': 'They are both the same weight.', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' + # }, + # 'parsing_error': None + # } + ``` """ # noqa: E501 _ = kwargs.pop("strict", None) diff --git a/libs/partners/fireworks/langchain_fireworks/embeddings.py b/libs/partners/fireworks/langchain_fireworks/embeddings.py index 6389cab663e..a445d5a7432 100644 --- a/libs/partners/fireworks/langchain_fireworks/embeddings.py +++ b/libs/partners/fireworks/langchain_fireworks/embeddings.py @@ -13,10 +13,10 @@ class FireworksEmbeddings(BaseModel, Embeddings): Install `langchain_fireworks` and set environment variable `FIREWORKS_API_KEY`. - .. code-block:: bash - - pip install -U langchain_fireworks - export FIREWORKS_API_KEY="your-api-key" + ```bash + pip install -U langchain_fireworks + export FIREWORKS_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -30,42 +30,39 @@ class FireworksEmbeddings(BaseModel, Embeddings): Instantiate: - .. code-block:: python + ```python + from langchain_fireworks import FireworksEmbeddings - from langchain_fireworks import FireworksEmbeddings - - model = FireworksEmbeddings( - model="nomic-ai/nomic-embed-text-v1.5" - # Use FIREWORKS_API_KEY env var or pass it in directly - # fireworks_api_key="..." - ) + model = FireworksEmbeddings( + model="nomic-ai/nomic-embed-text-v1.5" + # Use FIREWORKS_API_KEY env var or pass it in directly + # fireworks_api_key="..." + ) + ``` Embed multiple texts: - .. code-block:: python - - vectors = embeddings.embed_documents(["hello", "goodbye"]) - # Showing only the first 3 coordinates - print(len(vectors)) - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + vectors = embeddings.embed_documents(["hello", "goodbye"]) + # Showing only the first 3 coordinates + print(len(vectors)) + print(vectors[0][:3]) + ``` + ```python + 2 + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embeddings.embed_query("hello") - print(vector[:3]) - - .. code-block:: python - - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] - + ```python + input_text = "The meaning of life is 42" + vector = embeddings.embed_query("hello") + print(vector[:3]) + ``` + ```python + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` """ client: OpenAI = Field(default=None, exclude=True) # type: ignore[assignment] # :meta private: diff --git a/libs/partners/fireworks/langchain_fireworks/llms.py b/libs/partners/fireworks/langchain_fireworks/llms.py index d2cd1764d67..00e76ed6dee 100644 --- a/libs/partners/fireworks/langchain_fireworks/llms.py +++ b/libs/partners/fireworks/langchain_fireworks/llms.py @@ -31,10 +31,9 @@ class Fireworks(LLM): [Fireworks AI API reference](https://readme.fireworks.ai/) Example: - - .. code-block:: python - response = fireworks.generate(["Tell me a joke."]) - + ```python + response = fireworks.generate(["Tell me a joke."]) + ``` """ base_url: str = "https://api.fireworks.ai/inference/v1/completions" diff --git a/libs/partners/groq/langchain_groq/chat_models.py b/libs/partners/groq/langchain_groq/chat_models.py index 50ee753eebe..7f4bb628638 100644 --- a/libs/partners/groq/langchain_groq/chat_models.py +++ b/libs/partners/groq/langchain_groq/chat_models.py @@ -73,10 +73,10 @@ class ChatGroq(BaseChatModel): Install `langchain-groq` and set environment variable `GROQ_API_KEY`. - .. code-block:: bash - - pip install -U langchain-groq - export GROQ_API_KEY="your-api-key" + ```bash + pip install -U langchain-groq + export GROQ_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -121,184 +121,182 @@ class ChatGroq(BaseChatModel): section. Instantiate: - .. code-block:: python + ```python + from langchain_groq import ChatGroq - from langchain_groq import ChatGroq - - llm = ChatGroq( - model="llama-3.1-8b-instant", - temperature=0.0, - max_retries=2, - # other params... - ) + llm = ChatGroq( + model="llama-3.1-8b-instant", + temperature=0.0, + max_retries=2, + # other params... + ) + ``` Invoke: - .. code-block:: python - - messages = [ - ("system", "You are a helpful translator. Translate the user sentence to French."), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - AIMessage(content='The English sentence "I love programming" can - be translated to French as "J\'aime programmer". The word - "programming" is translated as "programmer" in French.', - response_metadata={'token_usage': {'completion_tokens': 38, - 'prompt_tokens': 28, 'total_tokens': 66, 'completion_time': - 0.057975474, 'prompt_time': 0.005366091, 'queue_time': None, - 'total_time': 0.063341565}, 'model_name': 'llama-3.1-8b-instant', - 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', - 'logprobs': None}, id='run-ecc71d70-e10c-4b69-8b8c-b8027d95d4b8-0') + ```python + messages = [ + ("system", "You are a helpful translator. Translate the user sentence to French."), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` + ```python + AIMessage(content='The English sentence "I love programming" can + be translated to French as "J\'aime programmer". The word + "programming" is translated as "programmer" in French.', + response_metadata={'token_usage': {'completion_tokens': 38, + 'prompt_tokens': 28, 'total_tokens': 66, 'completion_time': + 0.057975474, 'prompt_time': 0.005366091, 'queue_time': None, + 'total_time': 0.063341565}, 'model_name': 'llama-3.1-8b-instant', + 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', + 'logprobs': None}, id='run-ecc71d70-e10c-4b69-8b8c-b8027d95d4b8-0') + ``` Stream: - .. code-block:: python + ```python + # Streaming `text` for each content chunk received + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` - # Streaming `text` for each content chunk received - for chunk in llm.stream(messages): - print(chunk.text, end="") + ```python + content='' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + content='The' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + content=' English' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + content=' sentence' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + ... + content=' program' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + content='".' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' + content='' response_metadata={'finish_reason': 'stop'} + id='run-4e9f926b-73f5-483b-8ef5-09533d925853 + ``` - .. code-block:: python + ```python + # Reconstructing a full response + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` - content='' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - content='The' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - content=' English' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - content=' sentence' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - ... - content=' program' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - content='".' id='run-4e9f926b-73f5-483b-8ef5-09533d925853' - content='' response_metadata={'finish_reason': 'stop'} - id='run-4e9f926b-73f5-483b-8ef5-09533d925853 - - .. code-block:: python - - # Reconstructing a full response - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full - - .. code-block:: python - - AIMessageChunk(content='The English sentence "I love programming" - can be translated to French as "J\'aime programmer". Here\'s the - breakdown of the sentence: "J\'aime" is the French equivalent of " - I love", and "programmer" is the French infinitive for "to program". - So, the literal translation is "I love to program". However, in - English we often omit the "to" when talking about activities we - love, and the same applies to French. Therefore, "J\'aime - programmer" is the correct and natural way to express "I love - programming" in French.', response_metadata={'finish_reason': - 'stop'}, id='run-a3c35ac4-0750-4d08-ac55-bfc63805de76') + ```python + AIMessageChunk(content='The English sentence "I love programming" + can be translated to French as "J\'aime programmer". Here\'s the + breakdown of the sentence: "J\'aime" is the French equivalent of " + I love", and "programmer" is the French infinitive for "to program". + So, the literal translation is "I love to program". However, in + English we often omit the "to" when talking about activities we + love, and the same applies to French. Therefore, "J\'aime + programmer" is the correct and natural way to express "I love + programming" in French.', response_metadata={'finish_reason': + 'stop'}, id='run-a3c35ac4-0750-4d08-ac55-bfc63805de76') + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke(messages) + ``` - await llm.ainvoke(messages) - - .. code-block:: python - - AIMessage(content='The English sentence "I love programming" can - be translated to French as "J\'aime programmer". The word - "programming" is translated as "programmer" in French. I hope - this helps! Let me know if you have any other questions.', - response_metadata={'token_usage': {'completion_tokens': 53, - 'prompt_tokens': 28, 'total_tokens': 81, 'completion_time': - 0.083623752, 'prompt_time': 0.007365126, 'queue_time': None, - 'total_time': 0.090988878}, 'model_name': 'llama-3.1-8b-instant', - 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', - 'logprobs': None}, id='run-897f3391-1bea-42e2-82e0-686e2367bcf8-0') + ```python + AIMessage(content='The English sentence "I love programming" can + be translated to French as "J\'aime programmer". The word + "programming" is translated as "programmer" in French. I hope + this helps! Let me know if you have any other questions.', + response_metadata={'token_usage': {'completion_tokens': 53, + 'prompt_tokens': 28, 'total_tokens': 81, 'completion_time': + 0.083623752, 'prompt_time': 0.007365126, 'queue_time': None, + 'total_time': 0.090988878}, 'model_name': 'llama-3.1-8b-instant', + 'system_fingerprint': 'fp_c5f20b5bb1', 'finish_reason': 'stop', + 'logprobs': None}, id='run-897f3391-1bea-42e2-82e0-686e2367bcf8-0') + ``` Tool calling: - .. code-block:: python - - from pydantic import BaseModel, Field + ```python + from pydantic import BaseModel, Field - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - model_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = model_with_tools.invoke("What is the population of NY?") - ai_msg.tool_calls + model_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = model_with_tools.invoke("What is the population of NY?") + ai_msg.tool_calls + ``` - .. code-block:: python - - [ - { - "name": "GetPopulation", - "args": {"location": "NY"}, - "id": "call_bb8d", - } - ] + ```python + [ + { + "name": "GetPopulation", + "args": {"location": "NY"}, + "id": "call_bb8d", + } + ] + ``` See `ChatGroq.bind_tools()` method for more. Structured output: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from pydantic import BaseModel, Field + from pydantic import BaseModel, Field - class Joke(BaseModel): - '''Joke to tell user.''' + 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") - rating: int | None = Field(description="How funny the joke is, from 1 to 10") + setup: str = Field(description="The setup of the joke") + punchline: str = Field(description="The punchline to the joke") + rating: int | None = Field(description="How funny the joke is, from 1 to 10") - structured_model = llm.with_structured_output(Joke) - structured_model.invoke("Tell me a joke about cats") + structured_model = llm.with_structured_output(Joke) + structured_model.invoke("Tell me a joke about cats") + ``` - .. code-block:: python - - Joke( - setup="Why don't cats play poker in the jungle?", - punchline="Too many cheetahs!", - rating=None, - ) + ```python + Joke( + setup="Why don't cats play poker in the jungle?", + punchline="Too many cheetahs!", + rating=None, + ) + ``` See `ChatGroq.with_structured_output()` for more. Response metadata: - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - - { - "token_usage": { - "completion_tokens": 70, - "prompt_tokens": 28, - "total_tokens": 98, - "completion_time": 0.111956391, - "prompt_time": 0.007518279, - "queue_time": None, - "total_time": 0.11947467, - }, - "model_name": "llama-3.1-8b-instant", - "system_fingerprint": "fp_c5f20b5bb1", - "finish_reason": "stop", - "logprobs": None, - } + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` + ```python + { + "token_usage": { + "completion_tokens": 70, + "prompt_tokens": 28, + "total_tokens": 98, + "completion_time": 0.111956391, + "prompt_time": 0.007518279, + "queue_time": None, + "total_time": 0.11947467, + }, + "model_name": "llama-3.1-8b-instant", + "system_fingerprint": "fp_c5f20b5bb1", + "finish_reason": "stop", + "logprobs": None, + } + ``` """ # noqa: E501 client: Any = Field(default=None, exclude=True) #: :meta private: @@ -906,104 +904,100 @@ class ChatGroq(BaseChatModel): Example: schema=Pydantic class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_groq import ChatGroq - from pydantic import BaseModel, Field + from langchain_groq import ChatGroq + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - # If we provide default values and/or descriptions for fields, these will be passed - # to the model. This is an important part of improving a model's ability to - # correctly return structured outputs. - justification: str | None = Field(default=None, description="A justification for the answer.") + answer: str + # If we provide default values and/or descriptions for fields, these will be passed + # to the model. This is an important part of improving a model's ability to + # correctly return structured outputs. + justification: str | None = Field(default=None, description="A justification for the answer.") - llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) + structured_llm = llm.with_structured_output(AnswerWithJustification) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: schema=Pydantic class, method="function_calling", include_raw=True: - .. code-block:: python - - from langchain_groq import ChatGroq - from pydantic import BaseModel + ```python + from langchain_groq import ChatGroq + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, - include_raw=True, - ) + llm = ChatGroq(model="openai/gpt-oss-120b", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` Example: schema=TypedDict class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict - # 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_groq import ChatGroq + from langchain_groq import ChatGroq - class AnswerWithJustification(TypedDict): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: Annotated[str | None, None, "A justification for the answer."] + answer: str + justification: Annotated[str | None, None, "A justification for the answer."] - llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatGroq(model="openai/gpt-oss-120b", 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` Example: schema=OpenAI function schema, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from langchain_groq import ChatGroq - from langchain_groq import ChatGroq - - 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'] - } - } + 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 = ChatGroq(model="openai/gpt-oss-120b", temperature=0) structured_llm = llm.with_structured_output(oai_schema) @@ -1015,66 +1009,67 @@ class ChatGroq(BaseChatModel): # 'answer': 'They weigh the same', # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' # } + ``` Example: schema=Pydantic class, method="json_schema", include_raw=False: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_groq import ChatGroq - from pydantic import BaseModel, Field + from langchain_groq import ChatGroq + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - # If we provide default values and/or descriptions for fields, these will be passed - # to the model. This is an important part of improving a model's ability to - # correctly return structured outputs. - justification: str | None = Field(default=None, description="A justification for the answer.") + answer: str + # If we provide default values and/or descriptions for fields, these will be passed + # to the model. This is an important part of improving a model's ability to + # correctly return structured outputs. + justification: str | None = Field(default=None, description="A justification for the answer.") - llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, - method="json_schema", - ) + llm = ChatGroq(model="openai/gpt-oss-120b", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, + method="json_schema", + ) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: schema=Pydantic class, method="json_mode", include_raw=True: - .. code-block:: + ```python + from langchain_groq import ChatGroq + from pydantic import BaseModel - from langchain_groq import ChatGroq - from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - answer: str - justification: str + class AnswerWithJustification(BaseModel): + answer: str + justification: str - llm = ChatGroq(model="openai/gpt-oss-120b", 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 both the same weight.",\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \n}'), - # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), - # 'parsing_error': None - # } + llm = ChatGroq(model="openai/gpt-oss-120b", 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 both the same weight.",\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \n}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + ``` """ # noqa: E501 _ = kwargs.pop("strict", None) diff --git a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py index 639b6bffbaa..2f65ee79c53 100644 --- a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py @@ -319,15 +319,15 @@ class ChatHuggingFace(BaseChatModel): Install `langchain-huggingface` and ensure your Hugging Face token is saved. - .. code-block:: bash + ```bash + pip install langchain-huggingface + ``` - pip install langchain-huggingface + ```python + from huggingface_hub import login - .. code-block:: python - - from huggingface_hub import login - - login() # You will be prompted for your HF key, which will then be saved locally + login() # You will be prompted for your HF key, which will then be saved locally + ``` Key init args — completion params: llm: `HuggingFaceTextGenInference`, `HuggingFaceEndpoint`, `HuggingFaceHub`, or @@ -348,129 +348,129 @@ class ChatHuggingFace(BaseChatModel): section. Instantiate: - .. code-block:: python + ```python + from langchain_huggingface import HuggingFaceEndpoint, + ChatHuggingFace - from langchain_huggingface import HuggingFaceEndpoint, - ChatHuggingFace + llm = HuggingFaceEndpoint( + repo_id="microsoft/Phi-3-mini-4k-instruct", + task="text-generation", + max_new_tokens=512, + do_sample=False, + repetition_penalty=1.03, + ) - llm = HuggingFaceEndpoint( - repo_id="microsoft/Phi-3-mini-4k-instruct", - task="text-generation", - max_new_tokens=512, - do_sample=False, - repetition_penalty=1.03, - ) - - chat = ChatHuggingFace(llm=llm, verbose=True) + chat = ChatHuggingFace(llm=llm, verbose=True) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ("system", "You are a helpful translator. Translate the user + sentence to French."), + ("human", "I love programming."), + ] - messages = [ - ("system", "You are a helpful translator. Translate the user - sentence to French."), - ("human", "I love programming."), - ] + chat(...).invoke(messages) + ``` - chat(...).invoke(messages) - - .. code-block:: python - - AIMessage(content='Je ai une passion pour le programme.\n\nIn - French, we use "ai" for masculine subjects and "a" for feminine - subjects. Since "programming" is gender-neutral in English, we - will go with the masculine "programme".\n\nConfirmation: "J\'aime - le programme." is more commonly used. The sentence above is - technically accurate, but less commonly used in spoken French as - "ai" is used less frequently in everyday speech.', - response_metadata={'token_usage': ChatCompletionOutputUsage - (completion_tokens=100, prompt_tokens=55, total_tokens=155), - 'model': '', 'finish_reason': 'length'}, - id='run-874c24b7-0272-4c99-b259-5d6d7facbc56-0') + ```python + AIMessage(content='Je ai une passion pour le programme.\n\nIn + French, we use "ai" for masculine subjects and "a" for feminine + subjects. Since "programming" is gender-neutral in English, we + will go with the masculine "programme".\n\nConfirmation: "J\'aime + le programme." is more commonly used. The sentence above is + technically accurate, but less commonly used in spoken French as + "ai" is used less frequently in everyday speech.', + response_metadata={'token_usage': ChatCompletionOutputUsage + (completion_tokens=100, prompt_tokens=55, total_tokens=155), + 'model': '', 'finish_reason': 'length'}, + id='run-874c24b7-0272-4c99-b259-5d6d7facbc56-0') + ``` Stream: - .. code-block:: python + ```python + for chunk in chat.stream(messages): + print(chunk) + ``` - for chunk in chat.stream(messages): - print(chunk) - - .. code-block:: python - - content='Je ai une passion pour le programme.\n\nIn French, we use - "ai" for masculine subjects and "a" for feminine subjects. - Since "programming" is gender-neutral in English, - we will go with the masculine "programme".\n\nConfirmation: - "J\'aime le programme." is more commonly used. The sentence - above is technically accurate, but less commonly used in spoken - French as "ai" is used less frequently in everyday speech.' - response_metadata={'token_usage': ChatCompletionOutputUsage - (completion_tokens=100, prompt_tokens=55, total_tokens=155), - 'model': '', 'finish_reason': 'length'} - id='run-7d7b1967-9612-4f9a-911a-b2b5ca85046a-0' + ```python + content='Je ai une passion pour le programme.\n\nIn French, we use + "ai" for masculine subjects and "a" for feminine subjects. + Since "programming" is gender-neutral in English, + we will go with the masculine "programme".\n\nConfirmation: + "J\'aime le programme." is more commonly used. The sentence + above is technically accurate, but less commonly used in spoken + French as "ai" is used less frequently in everyday speech.' + response_metadata={'token_usage': ChatCompletionOutputUsage + (completion_tokens=100, prompt_tokens=55, total_tokens=155), + 'model': '', 'finish_reason': 'length'} + id='run-7d7b1967-9612-4f9a-911a-b2b5ca85046a-0' + ``` Async: - .. code-block:: python + ```python + await chat.ainvoke(messages) + ``` - await chat.ainvoke(messages) - - .. code-block:: python - - AIMessage(content='Je déaime le programming.\n\nLittérale : Je - (j\'aime) déaime (le) programming.\n\nNote: "Programming" in - French is "programmation". But here, I used "programming" instead - of "programmation" because the user said "I love programming" - instead of "I love programming (in French)", which would be - "J\'aime la programmation". By translating the sentence - literally, I preserved the original meaning of the user\'s - sentence.', id='run-fd850318-e299-4735-b4c6-3496dc930b1d-0') + ```python + AIMessage(content='Je déaime le programming.\n\nLittérale : Je + (j\'aime) déaime (le) programming.\n\nNote: "Programming" in + French is "programmation". But here, I used "programming" instead + of "programmation" because the user said "I love programming" + instead of "I love programming (in French)", which would be + "J\'aime la programmation". By translating the sentence + literally, I preserved the original meaning of the user\'s + sentence.', id='run-fd850318-e299-4735-b4c6-3496dc930b1d-0') + ``` Tool calling: - .. code-block:: python + ```python + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + location: str = Field(..., description="The city and state, + e.g. San Francisco, CA") - location: str = Field(..., description="The city and state, - e.g. San Francisco, CA") + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + location: str = Field(..., description="The city and state, + e.g. San Francisco, CA") - location: str = Field(..., description="The city and state, - e.g. San Francisco, CA") + chat_with_tools = chat.bind_tools([GetWeather, GetPopulation]) + ai_msg = chat_with_tools.invoke("Which city is hotter today and + which is bigger: LA or NY?") + ai_msg.tool_calls + ``` - chat_with_tools = chat.bind_tools([GetWeather, GetPopulation]) - ai_msg = chat_with_tools.invoke("Which city is hotter today and - which is bigger: LA or NY?") - ai_msg.tool_calls - - .. code-block:: python - - [ - { - "name": "GetPopulation", - "args": {"location": "Los Angeles, CA"}, - "id": "0", - } - ] + ```python + [ + { + "name": "GetPopulation", + "args": {"location": "Los Angeles, CA"}, + "id": "0", + } + ] + ``` Response metadata - .. code-block:: python - - ai_msg = chat.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - { - "token_usage": ChatCompletionOutputUsage( - completion_tokens=100, prompt_tokens=8, total_tokens=108 - ), - "model": "", - "finish_reason": "length", - } + ```python + ai_msg = chat.invoke(messages) + ai_msg.response_metadata + ``` + ```python + { + "token_usage": ChatCompletionOutputUsage( + completion_tokens=100, prompt_tokens=8, total_tokens=108 + ), + "model": "", + "finish_reason": "length", + } + ``` """ # noqa: E501 llm: Any diff --git a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py index ab19422f629..6d8dbd73e53 100644 --- a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py @@ -23,19 +23,18 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): To use, you should have the `sentence_transformers` python package installed. Example: - .. code-block:: python - - from langchain_huggingface import HuggingFaceEmbeddings - - model_name = "sentence-transformers/all-mpnet-base-v2" - model_kwargs = {"device": "cpu"} - encode_kwargs = {"normalize_embeddings": False} - hf = HuggingFaceEmbeddings( - model_name=model_name, - model_kwargs=model_kwargs, - encode_kwargs=encode_kwargs, - ) + ```python + from langchain_huggingface import HuggingFaceEmbeddings + model_name = "sentence-transformers/all-mpnet-base-v2" + model_kwargs = {"device": "cpu"} + encode_kwargs = {"normalize_embeddings": False} + hf = HuggingFaceEmbeddings( + model_name=model_name, + model_kwargs=model_kwargs, + encode_kwargs=encode_kwargs, + ) + ``` """ model_name: str = Field(default=DEFAULT_MODEL_NAME, alias="model") diff --git a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py index 4fad630c73c..69d9fdf8844 100644 --- a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py +++ b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py @@ -20,17 +20,16 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings): it as a named parameter to the constructor. Example: - .. code-block:: python - - from langchain_huggingface import HuggingFaceEndpointEmbeddings - - model = "sentence-transformers/all-mpnet-base-v2" - hf = HuggingFaceEndpointEmbeddings( - model=model, - task="feature-extraction", - huggingfacehub_api_token="my-api-key", - ) + ```python + from langchain_huggingface import HuggingFaceEndpointEmbeddings + model = "sentence-transformers/all-mpnet-base-v2" + hf = HuggingFaceEndpointEmbeddings( + model=model, + task="feature-extraction", + huggingfacehub_api_token="my-api-key", + ) + ``` """ client: Any = None #: :meta private: diff --git a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py index eb3720833a4..4ee76521c36 100644 --- a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py +++ b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py @@ -34,49 +34,48 @@ class HuggingFaceEndpoint(LLM): or given as a named parameter to the constructor. Example: - .. code-block:: python + ```python + # Basic Example (no streaming) + llm = HuggingFaceEndpoint( + endpoint_url="http://localhost:8010/", + max_new_tokens=512, + top_k=10, + top_p=0.95, + typical_p=0.95, + temperature=0.01, + repetition_penalty=1.03, + huggingfacehub_api_token="my-api-key", + ) + print(llm.invoke("What is Deep Learning?")) - # Basic Example (no streaming) - llm = HuggingFaceEndpoint( - endpoint_url="http://localhost:8010/", - max_new_tokens=512, - top_k=10, - top_p=0.95, - typical_p=0.95, - temperature=0.01, - repetition_penalty=1.03, - huggingfacehub_api_token="my-api-key", - ) - print(llm.invoke("What is Deep Learning?")) + # Streaming response example + from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler - # Streaming response example - from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler - - callbacks = [StreamingStdOutCallbackHandler()] - llm = HuggingFaceEndpoint( - endpoint_url="http://localhost:8010/", - max_new_tokens=512, - top_k=10, - top_p=0.95, - typical_p=0.95, - temperature=0.01, - repetition_penalty=1.03, - callbacks=callbacks, - streaming=True, - huggingfacehub_api_token="my-api-key", - ) - print(llm.invoke("What is Deep Learning?")) - - # Basic Example (no streaming) with Mistral-Nemo-Base-2407 model using a third-party provider (Novita). - llm = HuggingFaceEndpoint( - repo_id="mistralai/Mistral-Nemo-Base-2407", - provider="novita", - max_new_tokens=100, - do_sample=False, - huggingfacehub_api_token="my-api-key", - ) - print(llm.invoke("What is Deep Learning?")) + callbacks = [StreamingStdOutCallbackHandler()] + llm = HuggingFaceEndpoint( + endpoint_url="http://localhost:8010/", + max_new_tokens=512, + top_k=10, + top_p=0.95, + typical_p=0.95, + temperature=0.01, + repetition_penalty=1.03, + callbacks=callbacks, + streaming=True, + huggingfacehub_api_token="my-api-key", + ) + print(llm.invoke("What is Deep Learning?")) + # Basic Example (no streaming) with Mistral-Nemo-Base-2407 model using a third-party provider (Novita). + llm = HuggingFaceEndpoint( + repo_id="mistralai/Mistral-Nemo-Base-2407", + provider="novita", + max_new_tokens=100, + do_sample=False, + huggingfacehub_api_token="my-api-key", + ) + print(llm.invoke("What is Deep Learning?")) + ``` """ # noqa: E501 endpoint_url: str | None = None diff --git a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py index 1bd278fb221..f6edf545536 100644 --- a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py +++ b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py @@ -43,31 +43,32 @@ class HuggingFacePipeline(BaseLLM): `summarization` and `translation` for now. Example using from_model_id: - .. code-block:: python + ```python + from langchain_huggingface import HuggingFacePipeline + + hf = HuggingFacePipeline.from_model_id( + model_id="gpt2", + task="text-generation", + pipeline_kwargs={"max_new_tokens": 10}, + ) + ``` - from langchain_huggingface import HuggingFacePipeline - hf = HuggingFacePipeline.from_model_id( - model_id="gpt2", - task="text-generation", - pipeline_kwargs={"max_new_tokens": 10}, - ) Example passing pipeline in directly: - .. code-block:: python - - from langchain_huggingface import HuggingFacePipeline - from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline - - model_id = "gpt2" - tokenizer = AutoTokenizer.from_pretrained(model_id) - model = AutoModelForCausalLM.from_pretrained(model_id) - pipe = pipeline( - "text-generation", - model=model, - tokenizer=tokenizer, - max_new_tokens=10, - ) - hf = HuggingFacePipeline(pipeline=pipe) + ```python + from langchain_huggingface import HuggingFacePipeline + from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline + model_id = "gpt2" + tokenizer = AutoTokenizer.from_pretrained(model_id) + model = AutoModelForCausalLM.from_pretrained(model_id) + pipe = pipeline( + "text-generation", + model=model, + tokenizer=tokenizer, + max_new_tokens=10, + ) + hf = HuggingFacePipeline(pipeline=pipe) + ``` """ pipeline: Any = None #: :meta private: diff --git a/libs/partners/mistralai/langchain_mistralai/chat_models.py b/libs/partners/mistralai/langchain_mistralai/chat_models.py index 9cdadbece5e..88e999db270 100644 --- a/libs/partners/mistralai/langchain_mistralai/chat_models.py +++ b/libs/partners/mistralai/langchain_mistralai/chat_models.py @@ -808,113 +808,109 @@ class ChatMistralAI(BaseChatModel): - `'parsing_error'`: BaseException | None Example: schema=Pydantic class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_mistralai import ChatMistralAI - from pydantic import BaseModel, Field + from langchain_mistralai import ChatMistralAI + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - # If we provide default values and/or descriptions for fields, these will be passed - # to the model. This is an important part of improving a model's ability to - # correctly return structured outputs. - justification: str | None = Field( - default=None, description="A justification for the answer." - ) - - - llm = ChatMistralAI(model="mistral-large-latest", 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: str + # If we provide default values and/or descriptions for fields, these will be passed + # to the model. This is an important part of improving a model's ability to + # correctly return structured outputs. + justification: str | None = Field( + default=None, description="A justification for the answer." ) - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + + llm = ChatMistralAI(model="mistral-large-latest", 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='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` Example: schema=Pydantic class, method="function_calling", include_raw=True: - .. code-block:: python - - from langchain_mistralai import ChatMistralAI - from pydantic import BaseModel + ```python + from langchain_mistralai import ChatMistralAI + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatMistralAI(model="mistral-large-latest", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, include_raw=True - ) + llm = ChatMistralAI(model="mistral-large-latest", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` Example: schema=TypedDict class, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict - # 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_mistralai import ChatMistralAI + from langchain_mistralai import ChatMistralAI - class AnswerWithJustification(TypedDict): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: Annotated[ - str | None, None, "A justification for the answer." - ] + answer: str + justification: Annotated[ + str | None, None, "A justification for the answer." + ] - llm = ChatMistralAI(model="mistral-large-latest", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatMistralAI(model="mistral-large-latest", 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` Example: schema=OpenAI function schema, method="function_calling", include_raw=False: - .. code-block:: python + ```python + from langchain_mistralai import ChatMistralAI - from langchain_mistralai import ChatMistralAI - - 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'] - } - } + 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 = ChatMistralAI(model="mistral-large-latest", temperature=0) structured_llm = llm.with_structured_output(oai_schema) @@ -926,53 +922,56 @@ class ChatMistralAI(BaseChatModel): # 'answer': 'They weigh the same', # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' # } + ``` Example: schema=Pydantic class, method="json_mode", include_raw=True: - .. code-block:: + ```python + from langchain_mistralai import ChatMistralAI + from pydantic import BaseModel - from langchain_mistralai import ChatMistralAI - from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - answer: str - justification: str + class AnswerWithJustification(BaseModel): + answer: str + justification: str - llm = ChatMistralAI(model="mistral-large-latest", 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), - # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), - # 'parsing_error': None - # } + llm = ChatMistralAI(model="mistral-large-latest", 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + ``` Example: schema=None, method="json_mode", include_raw=True: - .. code-block:: + ```python + structured_llm = llm.with_structured_output( + method="json_mode", include_raw=True + ) - 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), - # 'parsed': { - # 'answer': 'They are both the same weight.', - # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' - # }, - # 'parsing_error': None - # } + 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': { + # 'answer': 'They are both the same weight.', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' + # }, + # 'parsing_error': None + # } + ``` """ # noqa: E501 _ = kwargs.pop("strict", None) diff --git a/libs/partners/mistralai/langchain_mistralai/embeddings.py b/libs/partners/mistralai/langchain_mistralai/embeddings.py index 2b150aef6a0..26093d15c20 100644 --- a/libs/partners/mistralai/langchain_mistralai/embeddings.py +++ b/libs/partners/mistralai/langchain_mistralai/embeddings.py @@ -44,10 +44,10 @@ class MistralAIEmbeddings(BaseModel, Embeddings): Install `langchain_mistralai` and set environment variable `MISTRAL_API_KEY`. - .. code-block:: bash - - pip install -U langchain_mistralai - export MISTRAL_API_KEY="your-api-key" + ```bash + pip install -U langchain_mistralai + export MISTRAL_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -71,57 +71,53 @@ class MistralAIEmbeddings(BaseModel, Embeddings): Instantiate: - .. code-block:: python + ```python + from __module_name__ import MistralAIEmbeddings - from __module_name__ import MistralAIEmbeddings - - embed = MistralAIEmbeddings( - model="mistral-embed", - # api_key="...", - # other params... - ) + embed = MistralAIEmbeddings( + model="mistral-embed", + # api_key="...", + # other params... + ) + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embed.embed_query(input_text) - print(vector[:3]) - - .. code-block:: python - - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + input_text = "The meaning of life is 42" + vector = embed.embed_query(input_text) + print(vector[:3]) + ``` + ```python + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Embed multiple text: - .. code-block:: python - - input_texts = ["Document 1...", "Document 2..."] - vectors = embed.embed_documents(input_texts) - print(len(vectors)) - # The first 3 coordinates for the first vector - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + input_texts = ["Document 1...", "Document 2..."] + vectors = embed.embed_documents(input_texts) + print(len(vectors)) + # The first 3 coordinates for the first vector + print(vectors[0][:3]) + ``` + ```python + 2 + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Async: - .. code-block:: python - - vector = await embed.aembed_query(input_text) - print(vector[:3]) - - # multiple: - # await embed.aembed_documents(input_texts) - - .. code-block:: python - - [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ```python + vector = await embed.aembed_query(input_text) + print(vector[:3]) + # multiple: + # await embed.aembed_documents(input_texts) + ``` + ```python + [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ``` """ # The type for client and async_client is ignored because the type is not diff --git a/libs/partners/nomic/langchain_nomic/embeddings.py b/libs/partners/nomic/langchain_nomic/embeddings.py index a4a3537ca68..5e1253c1e92 100644 --- a/libs/partners/nomic/langchain_nomic/embeddings.py +++ b/libs/partners/nomic/langchain_nomic/embeddings.py @@ -14,13 +14,11 @@ class NomicEmbeddings(Embeddings): """NomicEmbeddings embedding model. Example: + ```python + from langchain_nomic import NomicEmbeddings - .. code-block:: python - - from langchain_nomic import NomicEmbeddings - - model = NomicEmbeddings() - + model = NomicEmbeddings() + ``` """ @overload diff --git a/libs/partners/ollama/langchain_ollama/chat_models.py b/libs/partners/ollama/langchain_ollama/chat_models.py index 8a0ff824683..2e63b51d310 100644 --- a/libs/partners/ollama/langchain_ollama/chat_models.py +++ b/libs/partners/ollama/langchain_ollama/chat_models.py @@ -261,10 +261,10 @@ class ChatOllama(BaseChatModel): Install `langchain-ollama` and download any models you want to use from ollama. - .. code-block:: bash - - ollama pull gpt-oss:20b - pip install -U langchain-ollama + ```bash + ollama pull gpt-oss:20b + pip install -U langchain-ollama + ``` Key init args — completion params: model: str @@ -291,193 +291,191 @@ class ChatOllama(BaseChatModel): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_ollama import ChatOllama - from langchain_ollama import ChatOllama - - llm = ChatOllama( - model="gpt-oss:20b", - validate_model_on_init=True, - temperature=0.8, - num_predict=256, - # other params ... - ) + llm = ChatOllama( + model="gpt-oss:20b", + validate_model_on_init=True, + temperature=0.8, + num_predict=256, + # other params ... + ) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ("system", "You are a helpful translator. Translate the user sentence to French."), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` - messages = [ - ("system", "You are a helpful translator. Translate the user sentence to French."), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - AIMessage(content='J'adore le programmation. (Note: "programming" can also refer to the act of writing code, so if you meant that, I could translate it as "J'adore programmer". But since you didn\'t specify, I assumed you were talking about the activity itself, which is what "le programmation" usually refers to.)', response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:37:50.182604Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 3576619666, 'load_duration': 788524916, 'prompt_eval_count': 32, 'prompt_eval_duration': 128125000, 'eval_count': 71, 'eval_duration': 2656556000}, id='run-ba48f958-6402-41a5-b461-5e250a4ebd36-0') + ```python + AIMessage(content='J'adore le programmation. (Note: "programming" can also refer to the act of writing code, so if you meant that, I could translate it as "J'adore programmer". But since you didn\'t specify, I assumed you were talking about the activity itself, which is what "le programmation" usually refers to.)', response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:37:50.182604Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 3576619666, 'load_duration': 788524916, 'prompt_eval_count': 32, 'prompt_eval_duration': 128125000, 'eval_count': 71, 'eval_duration': 2656556000}, id='run-ba48f958-6402-41a5-b461-5e250a4ebd36-0') + ``` Stream: - .. code-block:: python + ```python + for chunk in llm.stream("Return the words Hello World!"): + print(chunk.text, end="") + ``` - for chunk in llm.stream("Return the words Hello World!"): - print(chunk.text, end="") + ```python + content='Hello' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' + content=' World' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' + content='!' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' + content='' response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:39:42.274449Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 411875125, 'load_duration': 1898166, 'prompt_eval_count': 14, 'prompt_eval_duration': 297320000, 'eval_count': 4, 'eval_duration': 111099000} id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' + ``` - .. code-block:: python + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` - content='Hello' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' - content=' World' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' - content='!' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' - content='' response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:39:42.274449Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 411875125, 'load_duration': 1898166, 'prompt_eval_count': 14, 'prompt_eval_duration': 297320000, 'eval_count': 4, 'eval_duration': 111099000} id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1' - - - .. code-block:: python - - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full - - .. code-block:: python - - AIMessageChunk( - content='Je adore le programmation.(Note: "programmation" is the formal way to say "programming" in French, but informally, people might use the phrase "le développement logiciel" or simply "le code")', - response_metadata={ - "model": "llama3", - "created_at": "2024-07-04T03:38:54.933154Z", - "message": {"role": "assistant", "content": ""}, - "done_reason": "stop", - "done": True, - "total_duration": 1977300042, - "load_duration": 1345709, - "prompt_eval_duration": 159343000, - "eval_count": 47, - "eval_duration": 1815123000, - }, - id="run-3c81a3ed-3e79-4dd3-a796-04064d804890", - ) + ```python + AIMessageChunk( + content='Je adore le programmation.(Note: "programmation" is the formal way to say "programming" in French, but informally, people might use the phrase "le développement logiciel" or simply "le code")', + response_metadata={ + "model": "llama3", + "created_at": "2024-07-04T03:38:54.933154Z", + "message": {"role": "assistant", "content": ""}, + "done_reason": "stop", + "done": True, + "total_duration": 1977300042, + "load_duration": 1345709, + "prompt_eval_duration": 159343000, + "eval_count": 47, + "eval_duration": 1815123000, + }, + id="run-3c81a3ed-3e79-4dd3-a796-04064d804890", + ) + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke("Hello how are you!") + ``` - await llm.ainvoke("Hello how are you!") + ```python + AIMessage( + content="Hi there! I'm just an AI, so I don't have feelings or emotions like humans do. But I'm functioning properly and ready to help with any questions or tasks you may have! How can I assist you today?", + response_metadata={ + "model": "llama3", + "created_at": "2024-07-04T03:52:08.165478Z", + "message": {"role": "assistant", "content": ""}, + "done_reason": "stop", + "done": True, + "total_duration": 2138492875, + "load_duration": 1364000, + "prompt_eval_count": 10, + "prompt_eval_duration": 297081000, + "eval_count": 47, + "eval_duration": 1838524000, + }, + id="run-29c510ae-49a4-4cdd-8f23-b972bfab1c49-0", + ) + ``` - .. code-block:: python + ```python + async for chunk in llm.astream("Say hello world!"): + print(chunk.content) + ``` + ```python + HEL + LO + WORLD + ! + ``` + + ```python + messages = [("human", "Say hello world!"), ("human", "Say goodbye world!")] + await llm.abatch(messages) + ``` + + ```python + [ AIMessage( - content="Hi there! I'm just an AI, so I don't have feelings or emotions like humans do. But I'm functioning properly and ready to help with any questions or tasks you may have! How can I assist you today?", + content="HELLO, WORLD!", response_metadata={ "model": "llama3", - "created_at": "2024-07-04T03:52:08.165478Z", + "created_at": "2024-07-04T03:55:07.315396Z", "message": {"role": "assistant", "content": ""}, "done_reason": "stop", "done": True, - "total_duration": 2138492875, - "load_duration": 1364000, - "prompt_eval_count": 10, - "prompt_eval_duration": 297081000, - "eval_count": 47, - "eval_duration": 1838524000, + "total_duration": 1696745458, + "load_duration": 1505000, + "prompt_eval_count": 8, + "prompt_eval_duration": 111627000, + "eval_count": 6, + "eval_duration": 185181000, }, - id="run-29c510ae-49a4-4cdd-8f23-b972bfab1c49-0", - ) - - .. code-block:: python - - async for chunk in llm.astream("Say hello world!"): - print(chunk.content) - - .. code-block:: python - - HEL - LO - WORLD - ! - - .. code-block:: python - - messages = [("human", "Say hello world!"), ("human", "Say goodbye world!")] - await llm.abatch(messages) - - .. code-block:: python - - [ - AIMessage( - content="HELLO, WORLD!", - response_metadata={ - "model": "llama3", - "created_at": "2024-07-04T03:55:07.315396Z", - "message": {"role": "assistant", "content": ""}, - "done_reason": "stop", - "done": True, - "total_duration": 1696745458, - "load_duration": 1505000, - "prompt_eval_count": 8, - "prompt_eval_duration": 111627000, - "eval_count": 6, - "eval_duration": 185181000, - }, - id="run-da6c7562-e25a-4a44-987a-2c83cd8c2686-0", - ), - AIMessage( - content="It's been a blast chatting with you! Say goodbye to the world for me, and don't forget to come back and visit us again soon!", - response_metadata={ - "model": "llama3", - "created_at": "2024-07-04T03:55:07.018076Z", - "message": {"role": "assistant", "content": ""}, - "done_reason": "stop", - "done": True, - "total_duration": 1399391083, - "load_duration": 1187417, - "prompt_eval_count": 20, - "prompt_eval_duration": 230349000, - "eval_count": 31, - "eval_duration": 1166047000, - }, - id="run-96cad530-6f3e-4cf9-86b4-e0f8abba4cdb-0", - ), - ] + id="run-da6c7562-e25a-4a44-987a-2c83cd8c2686-0", + ), + AIMessage( + content="It's been a blast chatting with you! Say goodbye to the world for me, and don't forget to come back and visit us again soon!", + response_metadata={ + "model": "llama3", + "created_at": "2024-07-04T03:55:07.018076Z", + "message": {"role": "assistant", "content": ""}, + "done_reason": "stop", + "done": True, + "total_duration": 1399391083, + "load_duration": 1187417, + "prompt_eval_count": 20, + "prompt_eval_duration": 230349000, + "eval_count": 31, + "eval_duration": 1166047000, + }, + id="run-96cad530-6f3e-4cf9-86b4-e0f8abba4cdb-0", + ), + ] + ``` JSON mode: - .. code-block:: python + ```python + json_llm = ChatOllama(format="json") + llm.invoke( + "Return a query for the weather in a random location and time of day with two keys: location and time_of_day. " + "Respond using JSON only." + ).content + ``` - - json_llm = ChatOllama(format="json") - llm.invoke( - "Return a query for the weather in a random location and time of day with two keys: location and time_of_day. " - "Respond using JSON only." - ).content - - .. code-block:: python - - '{"location": "Pune, India", "time_of_day": "morning"}' + ```python + '{"location": "Pune, India", "time_of_day": "morning"}' + ``` Tool Calling: - .. code-block:: python - - from langchain_ollama import ChatOllama - from pydantic import BaseModel, Field + ```python + from langchain_ollama import ChatOllama + from pydantic import BaseModel, Field - class Multiply(BaseModel): - a: int = Field(..., description="First integer") - b: int = Field(..., description="Second integer") + class Multiply(BaseModel): + a: int = Field(..., description="First integer") + b: int = Field(..., description="Second integer") - ans = await chat.invoke("What is 45*67") - ans.tool_calls + ans = await chat.invoke("What is 45*67") + ans.tool_calls + ``` - .. code-block:: python - - [ - { - "name": "Multiply", - "args": {"a": 45, "b": 67}, - "id": "420c3f3b-df10-4188-945f-eb3abdb40622", - "type": "tool_call", - } - ] + ```python + [ + { + "name": "Multiply", + "args": {"a": 45, "b": 67}, + "id": "420c3f3b-df10-4188-945f-eb3abdb40622", + "type": "tool_call", + } + ] + ``` Thinking / Reasoning: You can enable reasoning mode for models that support it by setting @@ -494,30 +492,30 @@ class ChatOllama(BaseChatModel): !!! note This feature is only available for [models that support reasoning](https://ollama.com/search?c=thinking). - .. code-block:: python + ```python + from langchain_ollama import ChatOllama - from langchain_ollama import ChatOllama + llm = ChatOllama( + model="deepseek-r1:8b", + validate_model_on_init=True, + reasoning=True, + ) - llm = ChatOllama( - model="deepseek-r1:8b", - validate_model_on_init=True, - reasoning=True, - ) + llm.invoke("how many r in the word strawberry?") - llm.invoke("how many r in the word strawberry?") + # or, on an invocation basis: - # or, on an invocation basis: + llm.invoke("how many r in the word strawberry?", reasoning=True) + # or llm.stream("how many r in the word strawberry?", reasoning=True) - llm.invoke("how many r in the word strawberry?", reasoning=True) - # or llm.stream("how many r in the word strawberry?", reasoning=True) + # If not provided, the invocation will default to the ChatOllama reasoning + # param provided (None by default). + ``` - # If not provided, the invocation will default to the ChatOllama reasoning - # param provided (None by default). - - .. code-block:: python - - AIMessage(content='The word "strawberry" contains **three \'r\' letters**. Here\'s a breakdown for clarity:\n\n- The spelling of "strawberry" has two parts ... be 3.\n\nTo be thorough, let\'s confirm with an online source or common knowledge.\n\nI can recall that "strawberry" has: s-t-r-a-w-b-e-r-r-y — yes, three r\'s.\n\nPerhaps it\'s misspelled by some, but standard is correct.\n\nSo I think the response should be 3.\n'}, response_metadata={'model': 'deepseek-r1:8b', 'created_at': '2025-07-08T19:33:55.891269Z', 'done': True, 'done_reason': 'stop', 'total_duration': 98232561292, 'load_duration': 28036792, 'prompt_eval_count': 10, 'prompt_eval_duration': 40171834, 'eval_count': 3615, 'eval_duration': 98163832416, 'model_name': 'deepseek-r1:8b'}, id='run--18f8269f-6a35-4a7c-826d-b89d52c753b3-0', usage_metadata={'input_tokens': 10, 'output_tokens': 3615, 'total_tokens': 3625}) + ```python + AIMessage(content='The word "strawberry" contains **three \'r\' letters**. Here\'s a breakdown for clarity:\n\n- The spelling of "strawberry" has two parts ... be 3.\n\nTo be thorough, let\'s confirm with an online source or common knowledge.\n\nI can recall that "strawberry" has: s-t-r-a-w-b-e-r-r-y — yes, three r\'s.\n\nPerhaps it\'s misspelled by some, but standard is correct.\n\nSo I think the response should be 3.\n'}, response_metadata={'model': 'deepseek-r1:8b', 'created_at': '2025-07-08T19:33:55.891269Z', 'done': True, 'done_reason': 'stop', 'total_duration': 98232561292, 'load_duration': 28036792, 'prompt_eval_count': 10, 'prompt_eval_duration': 40171834, 'eval_count': 3615, 'eval_duration': 98163832416, 'model_name': 'deepseek-r1:8b'}, id='run--18f8269f-6a35-4a7c-826d-b89d52c753b3-0', usage_metadata={'input_tokens': 10, 'output_tokens': 3615, 'total_tokens': 3625}) + ``` """ # noqa: E501, pylint: disable=line-too-long model: str @@ -1312,140 +1310,136 @@ class ChatOllama(BaseChatModel): ??? note "Example: `schema=Pydantic` class, `method='json_schema'`, `include_raw=False`" - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_ollama import ChatOllama - from pydantic import BaseModel, Field + from langchain_ollama import ChatOllama + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str | None = Field( - default=..., - description="A justification for the answer.", - ) + answer: str + justification: str | None = Field( + default=..., + description="A justification for the answer.", + ) - llm = ChatOllama(model="llama3.1", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output(AnswerWithJustification) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` ??? note "Example: `schema=Pydantic` class, `method='json_schema'`, `include_raw=True`" - .. code-block:: python - - from langchain_ollama import ChatOllama - from pydantic import BaseModel + ```python + from langchain_ollama import ChatOllama + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = ChatOllama(model="llama3.1", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, - include_raw=True, - ) + llm = ChatOllama(model="llama3.1", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` ??? note "Example: `schema=Pydantic` class, `method='function_calling'`, `include_raw=False`" - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_ollama import ChatOllama - from pydantic import BaseModel, Field + from langchain_ollama import ChatOllama + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str | None = Field( - default=..., - description="A justification for the answer.", - ) - - - llm = ChatOllama(model="llama3.1", temperature=0) - structured_llm = llm.with_structured_output( - AnswerWithJustification, - method="function_calling", + answer: str + justification: str | None = Field( + default=..., + description="A justification for the answer.", ) - structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, + method="function_calling", + ) + + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` ??? note "Example: `schema=TypedDict` class, `method='function_calling'`, `include_raw=False`" - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict - # 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_ollama import ChatOllama + from langchain_ollama import ChatOllama - class AnswerWithJustification(TypedDict): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: Annotated[str | None, None, "A justification for the answer."] + answer: str + justification: Annotated[str | None, None, "A justification for the answer."] - llm = ChatOllama(model="llama3.1", temperature=0) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = ChatOllama(model="llama3.1", 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers") + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` ??? note "Example: `schema=OpenAI` function schema, `method='function_calling'`, `include_raw=False`" - .. code-block:: python + ```python + from langchain_ollama import ChatOllama - from langchain_ollama import ChatOllama - - 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'] - } - } + 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 = ChatOllama(model="llama3.1", temperature=0) structured_llm = llm.with_structured_output(oai_schema) @@ -1457,35 +1451,36 @@ class ChatOllama(BaseChatModel): # 'answer': 'They weigh the same', # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' # } + ``` ??? note "Example: `schema=Pydantic` class, `method='json_mode'`, `include_raw=True`" - .. code-block:: + ```python + from langchain_ollama import ChatOllama + from pydantic import BaseModel - from langchain_ollama import ChatOllama - from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - answer: str - justification: str + class AnswerWithJustification(BaseModel): + answer: str + justification: str - llm = ChatOllama(model="llama3.1", 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), - # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), - # 'parsing_error': None - # } + llm = ChatOllama(model="llama3.1", 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + ``` """ # noqa: E501 _ = kwargs.pop("strict", None) diff --git a/libs/partners/ollama/langchain_ollama/embeddings.py b/libs/partners/ollama/langchain_ollama/embeddings.py index 534569f591b..9d2d85eebea 100644 --- a/libs/partners/ollama/langchain_ollama/embeddings.py +++ b/libs/partners/ollama/langchain_ollama/embeddings.py @@ -27,9 +27,9 @@ class OllamaEmbeddings(BaseModel, Embeddings): For example, to pull the llama3 model: - .. code-block:: bash - - ollama pull llama3 + ```bash + ollama pull llama3 + ``` This will download the default tagged version of the model. Typically, the default points to the latest, smallest sized-parameter model. @@ -42,26 +42,26 @@ class OllamaEmbeddings(BaseModel, Embeddings): To view pulled models: - .. code-block:: bash - - ollama list + ```bash + ollama list + ``` To start serving: - .. code-block:: bash - - ollama serve + ```bash + ollama serve + ``` View the Ollama documentation for more commands. - .. code-block:: bash + ```bash + ollama help + ``` - ollama help - - Install the langchain-ollama integration package: - .. code-block:: bash - - pip install -U langchain_ollama + Install the `langchain-ollama` integration package: + ```bash + pip install -U langchain_ollama + ``` Key init args — completion params: model: str @@ -72,50 +72,49 @@ class OllamaEmbeddings(BaseModel, Embeddings): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_ollama import OllamaEmbeddings - from langchain_ollama import OllamaEmbeddings - - embed = OllamaEmbeddings(model="llama3") + embed = OllamaEmbeddings(model="llama3") + ``` Embed single text: - .. code-block:: python + ```python + input_text = "The meaning of life is 42" + vector = embed.embed_query(input_text) + print(vector[:3]) + ``` - input_text = "The meaning of life is 42" - vector = embed.embed_query(input_text) - print(vector[:3]) - - .. code-block:: python - - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Embed multiple texts: - .. code-block:: python + ```python + input_texts = ["Document 1...", "Document 2..."] + vectors = embed.embed_documents(input_texts) + print(len(vectors)) + # The first 3 coordinates for the first vector + print(vectors[0][:3]) + ``` - input_texts = ["Document 1...", "Document 2..."] - vectors = embed.embed_documents(input_texts) - print(len(vectors)) - # The first 3 coordinates for the first vector - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + 2 + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Async: - .. code-block:: python + ```python + vector = await embed.aembed_query(input_text) + print(vector[:3]) - vector = await embed.aembed_query(input_text) - print(vector[:3]) - - # multiple: - # await embed.aembed_documents(input_texts) - - .. code-block:: python - - [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + # multiple: + # await embed.aembed_documents(input_texts) + ``` + ```python + [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ``` """ # noqa: E501 model: str diff --git a/libs/partners/ollama/langchain_ollama/llms.py b/libs/partners/ollama/langchain_ollama/llms.py index c68fbd06999..45a0a79fece 100644 --- a/libs/partners/ollama/langchain_ollama/llms.py +++ b/libs/partners/ollama/langchain_ollama/llms.py @@ -24,17 +24,17 @@ class OllamaLLM(BaseLLM): Setup: Install `langchain-ollama` and install/run the Ollama server locally: - .. code-block:: bash - - pip install -U langchain-ollama - # Visit https://ollama.com/download to download and install Ollama - # (Linux users): start the server with `ollama serve` + ```bash + pip install -U langchain-ollama + # Visit https://ollama.com/download to download and install Ollama + # (Linux users): start the server with `ollama serve` + ``` Download a model to use: - .. code-block:: bash - - ollama pull llama3.1 + ```bash + ollama pull llama3.1 + ``` Key init args — generation params: model: str @@ -63,50 +63,47 @@ class OllamaLLM(BaseLLM): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_ollama import OllamaLLM - from langchain_ollama import OllamaLLM - - llm = OllamaLLM( - model="llama3.1", - temperature=0.7, - num_predict=256, - # base_url="http://localhost:11434", - # other params... - ) + llm = OllamaLLM( + model="llama3.1", + temperature=0.7, + num_predict=256, + # base_url="http://localhost:11434", + # other params... + ) + ``` Invoke: - .. code-block:: python - - input_text = "The meaning of life is " - response = llm.invoke(input_text) - print(response) - - .. code-block:: - - "a philosophical question that has been contemplated by humans for - centuries..." + ```python + input_text = "The meaning of life is " + response = llm.invoke(input_text) + print(response) + ``` + ```txt + "a philosophical question that has been contemplated by humans for + centuries..." + ``` Stream: - .. code-block:: python - - for chunk in llm.stream(input_text): - print(chunk, end="") - - .. code-block:: - - a philosophical question that has been contemplated by humans for - centuries... + ```python + for chunk in llm.stream(input_text): + print(chunk, end="") + ``` + ```txt + a philosophical question that has been contemplated by humans for + centuries... + ``` Async: - .. code-block:: python - - response = await llm.ainvoke(input_text) - - # stream: - # async for chunk in llm.astream(input_text): - # print(chunk, end="") + ```python + response = await llm.ainvoke(input_text) + # stream: + # async for chunk in llm.astream(input_text): + # print(chunk, end="") + ``` """ model: str diff --git a/libs/partners/openai/langchain_openai/chat_models/_compat.py b/libs/partners/openai/langchain_openai/chat_models/_compat.py index a762da564ce..642cafe6e1c 100644 --- a/libs/partners/openai/langchain_openai/chat_models/_compat.py +++ b/libs/partners/openai/langchain_openai/chat_models/_compat.py @@ -7,56 +7,56 @@ Supported values are `None`, `'v0'`, and `'responses/v1'`. `'v0'` corresponds to the format as of `ChatOpenAI` v0.3. For the Responses API, it stores reasoning and tool outputs in `AIMessage.additional_kwargs`: -.. code-block:: python - - AIMessage( - content=[ - {"type": "text", "text": "Hello, world!", "annotations": [{"type": "foo"}]} - ], - additional_kwargs={ - "reasoning": { - "type": "reasoning", - "id": "rs_123", - "summary": [{"type": "summary_text", "text": "Reasoning summary"}], - }, - "tool_outputs": [ - { - "type": "web_search_call", - "id": "websearch_123", - "status": "completed", - } - ], - "refusal": "I cannot assist with that.", +```python +AIMessage( + content=[ + {"type": "text", "text": "Hello, world!", "annotations": [{"type": "foo"}]} + ], + additional_kwargs={ + "reasoning": { + "type": "reasoning", + "id": "rs_123", + "summary": [{"type": "summary_text", "text": "Reasoning summary"}], }, - response_metadata={"id": "resp_123"}, - id="msg_123", - ) + "tool_outputs": [ + { + "type": "web_search_call", + "id": "websearch_123", + "status": "completed", + } + ], + "refusal": "I cannot assist with that.", + }, + response_metadata={"id": "resp_123"}, + id="msg_123", +) +``` `'responses/v1'` is only applicable to the Responses API. It retains information about response item sequencing and accommodates multiple reasoning items by representing these items in the content sequence: -.. code-block:: python - - AIMessage( - content=[ - { - "type": "reasoning", - "summary": [{"type": "summary_text", "text": "Reasoning summary"}], - "id": "rs_123", - }, - { - "type": "text", - "text": "Hello, world!", - "annotations": [{"type": "foo"}], - "id": "msg_123", - }, - {"type": "refusal", "refusal": "I cannot assist with that."}, - {"type": "web_search_call", "id": "websearch_123", "status": "completed"}, - ], - response_metadata={"id": "resp_123"}, - id="resp_123", - ) +```python +AIMessage( + content=[ + { + "type": "reasoning", + "summary": [{"type": "summary_text", "text": "Reasoning summary"}], + "id": "rs_123", + }, + { + "type": "text", + "text": "Hello, world!", + "annotations": [{"type": "foo"}], + "id": "msg_123", + }, + {"type": "refusal", "refusal": "I cannot assist with that."}, + {"type": "web_search_call", "id": "websearch_123", "status": "completed"}, + ], + response_metadata={"id": "resp_123"}, + id="resp_123", +) +``` There are other, small improvements as well-- e.g., we store message IDs on text content blocks, rather than on the AIMessage.id, which now stores the response ID. diff --git a/libs/partners/openai/langchain_openai/chat_models/azure.py b/libs/partners/openai/langchain_openai/chat_models/azure.py index 6bd18f14e3a..602b91f6980 100644 --- a/libs/partners/openai/langchain_openai/chat_models/azure.py +++ b/libs/partners/openai/langchain_openai/chat_models/azure.py @@ -41,12 +41,12 @@ class AzureChatOpenAI(BaseChatOpenAI): Then install `langchain-openai` and set environment variables `AZURE_OPENAI_API_KEY` and `AZURE_OPENAI_ENDPOINT`: - .. code-block:: bash + ```bash + pip install -U langchain-openai - pip install -U langchain-openai - - export AZURE_OPENAI_API_KEY="your-api-key" - export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/" + export AZURE_OPENAI_API_KEY="your-api-key" + export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/" + ``` Key init args — completion params: azure_deployment: str @@ -79,22 +79,22 @@ class AzureChatOpenAI(BaseChatOpenAI): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_openai import AzureChatOpenAI - from langchain_openai import AzureChatOpenAI - - llm = AzureChatOpenAI( - azure_deployment="your-deployment", - api_version="2024-05-01-preview", - temperature=0, - max_tokens=None, - timeout=None, - max_retries=2, - # organization="...", - # model="gpt-35-turbo", - # model_version="0125", - # other params... - ) + llm = AzureChatOpenAI( + azure_deployment="your-deployment", + api_version="2024-05-01-preview", + temperature=0, + max_tokens=None, + timeout=None, + max_retries=2, + # organization="...", + # model="gpt-35-turbo", + # model_version="0125", + # other params... + ) + ``` !!! note Any param which is not explicitly supported will be passed directly to the @@ -103,342 +103,49 @@ class AzureChatOpenAI(BaseChatOpenAI): For example: - .. code-block:: python + ```python + from langchain_openai import AzureChatOpenAI + import openai - from langchain_openai import AzureChatOpenAI - import openai + AzureChatOpenAI(..., logprobs=True).invoke(...) - AzureChatOpenAI(..., logprobs=True).invoke(...) + # results in underlying API call of: - # results in underlying API call of: + openai.AzureOpenAI(..).chat.completions.create(..., logprobs=True) - openai.AzureOpenAI(..).chat.completions.create(..., logprobs=True) + # which is also equivalent to: - # which is also equivalent to: - - AzureChatOpenAI(...).invoke(..., logprobs=True) + AzureChatOpenAI(...).invoke(..., logprobs=True) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ( + "system", + "You are a helpful translator. Translate the user sentence to French.", + ), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` - messages = [ - ( - "system", - "You are a helpful translator. Translate the user sentence to French.", - ), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - AIMessage( - content="J'adore programmer.", - usage_metadata={ - "input_tokens": 28, - "output_tokens": 6, - "total_tokens": 34, - }, - response_metadata={ - "token_usage": { - "completion_tokens": 6, - "prompt_tokens": 28, - "total_tokens": 34, - }, - "model_name": "gpt-4", - "system_fingerprint": "fp_7ec89fabc6", - "prompt_filter_results": [ - { - "prompt_index": 0, - "content_filter_results": { - "hate": {"filtered": False, "severity": "safe"}, - "self_harm": {"filtered": False, "severity": "safe"}, - "sexual": {"filtered": False, "severity": "safe"}, - "violence": {"filtered": False, "severity": "safe"}, - }, - } - ], - "finish_reason": "stop", - "logprobs": None, - "content_filter_results": { - "hate": {"filtered": False, "severity": "safe"}, - "self_harm": {"filtered": False, "severity": "safe"}, - "sexual": {"filtered": False, "severity": "safe"}, - "violence": {"filtered": False, "severity": "safe"}, - }, - }, - id="run-6d7a5282-0de0-4f27-9cc0-82a9db9a3ce9-0", - ) - - Stream: - .. code-block:: python - - for chunk in llm.stream(messages): - print(chunk.text, end="") - - .. code-block:: python - - AIMessageChunk(content="", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk(content="J", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk(content="'", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk(content="ad", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk(content="ore", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk(content=" la", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk( - content=" programm", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f" - ) - AIMessageChunk( - content="ation", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f" - ) - AIMessageChunk(content=".", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") - AIMessageChunk( - content="", - response_metadata={ - "finish_reason": "stop", - "model_name": "gpt-4", - "system_fingerprint": "fp_811936bd4f", - }, - id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f", - ) - - .. code-block:: python - - stream = llm.stream(messages) - full = next(stream) - for chunk in stream: - full += chunk - full - - .. code-block:: python - - AIMessageChunk( - content="J'adore la programmation.", - response_metadata={ - "finish_reason": "stop", - "model_name": "gpt-4", - "system_fingerprint": "fp_811936bd4f", - }, - id="run-ba60e41c-9258-44b8-8f3a-2f10599643b3", - ) - - Async: - .. code-block:: python - - await llm.ainvoke(messages) - - # stream: - # async for chunk in (await llm.astream(messages)) - - # batch: - # await llm.abatch([messages]) - - 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. San Francisco, CA" - ) - - - class GetPopulation(BaseModel): - '''Get the current population in a given location''' - - location: str = Field( - ..., description="The city and state, e.g. San Francisco, CA" - ) - - - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke( - "Which city is hotter today and which is bigger: LA or NY?" - ) - ai_msg.tool_calls - - .. code-block:: python - - [ - { - "name": "GetWeather", - "args": {"location": "Los Angeles, CA"}, - "id": "call_6XswGD5Pqk8Tt5atYr7tfenU", - }, - { - "name": "GetWeather", - "args": {"location": "New York, NY"}, - "id": "call_ZVL15vA8Y7kXqOy3dtmQgeCi", - }, - { - "name": "GetPopulation", - "args": {"location": "Los Angeles, CA"}, - "id": "call_49CFW8zqC9W7mh7hbMLSIrXw", - }, - { - "name": "GetPopulation", - "args": {"location": "New York, NY"}, - "id": "call_6ghfKxV264jEfe1mRIkS3PE7", - }, - ] - - 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") - rating: int | None = Field( - description="How funny the joke is, from 1 to 10" - ) - - - structured_llm = llm.with_structured_output(Joke) - structured_llm.invoke("Tell me a joke about cats") - - .. code-block:: python - - Joke( - setup="Why was the cat sitting on the computer?", - punchline="To keep an eye on the mouse!", - rating=None, - ) - - See `AzureChatOpenAI.with_structured_output()` for more. - - JSON mode: - .. code-block:: python - - json_llm = llm.bind(response_format={"type": "json_object"}) - ai_msg = json_llm.invoke( - "Return a JSON object with key 'random_ints' and a value of 10 random ints in [0-99]" - ) - ai_msg.content - - .. code-block:: python - - '\\n{\\n "random_ints": [23, 87, 45, 12, 78, 34, 56, 90, 11, 67]\\n}' - - Image input: - .. code-block:: python - - import base64 - import httpx - from langchain_core.messages import HumanMessage - - image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" - image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") - message = HumanMessage( - content=[ - {"type": "text", "text": "describe the weather in this image"}, - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ] - ) - ai_msg = llm.invoke([message]) - ai_msg.content - - .. code-block:: python - - "The weather in the image appears to be quite pleasant. The sky is mostly clear" - - Token usage: - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.usage_metadata - - .. code-block:: python - - {"input_tokens": 28, "output_tokens": 5, "total_tokens": 33} - - Logprobs: - .. code-block:: python - - logprobs_llm = llm.bind(logprobs=True) - ai_msg = logprobs_llm.invoke(messages) - ai_msg.response_metadata["logprobs"] - - .. code-block:: python - - { - "content": [ - { - "token": "J", - "bytes": [74], - "logprob": -4.9617593e-06, - "top_logprobs": [], - }, - { - "token": "'adore", - "bytes": [39, 97, 100, 111, 114, 101], - "logprob": -0.25202933, - "top_logprobs": [], - }, - { - "token": " la", - "bytes": [32, 108, 97], - "logprob": -0.20141791, - "top_logprobs": [], - }, - { - "token": " programmation", - "bytes": [ - 32, - 112, - 114, - 111, - 103, - 114, - 97, - 109, - 109, - 97, - 116, - 105, - 111, - 110, - ], - "logprob": -1.9361265e-07, - "top_logprobs": [], - }, - { - "token": ".", - "bytes": [46], - "logprob": -1.2233183e-05, - "top_logprobs": [], - }, - ] - } - - Response metadata - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - - { + ```python + AIMessage( + content="J'adore programmer.", + usage_metadata={ + "input_tokens": 28, + "output_tokens": 6, + "total_tokens": 34, + }, + response_metadata={ "token_usage": { "completion_tokens": 6, "prompt_tokens": 28, "total_tokens": 34, }, - "model_name": "gpt-35-turbo", - "system_fingerprint": None, + "model_name": "gpt-4", + "system_fingerprint": "fp_7ec89fabc6", "prompt_filter_results": [ { "prompt_index": 0, @@ -458,8 +165,297 @@ class AzureChatOpenAI(BaseChatOpenAI): "sexual": {"filtered": False, "severity": "safe"}, "violence": {"filtered": False, "severity": "safe"}, }, - } + }, + id="run-6d7a5282-0de0-4f27-9cc0-82a9db9a3ce9-0", + ) + ``` + Stream: + ```python + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` + + ```python + AIMessageChunk(content="", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content="J", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content="'", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content="ad", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content="ore", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content=" la", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk( + content=" programm", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f" + ) + AIMessageChunk(content="ation", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk(content=".", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f") + AIMessageChunk( + content="", + response_metadata={ + "finish_reason": "stop", + "model_name": "gpt-4", + "system_fingerprint": "fp_811936bd4f", + }, + id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f", + ) + ``` + + ```python + stream = llm.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + ``` + + ```python + AIMessageChunk( + content="J'adore la programmation.", + response_metadata={ + "finish_reason": "stop", + "model_name": "gpt-4", + "system_fingerprint": "fp_811936bd4f", + }, + id="run-ba60e41c-9258-44b8-8f3a-2f10599643b3", + ) + ``` + + Async: + ```python + await llm.ainvoke(messages) + + # stream: + # async for chunk in (await llm.astream(messages)) + + # batch: + # await llm.abatch([messages]) + ``` + + Tool calling: + ```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. San Francisco, CA" + ) + + + class GetPopulation(BaseModel): + '''Get the current population in a given location''' + + location: str = Field( + ..., description="The city and state, e.g. San Francisco, CA" + ) + + + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke( + "Which city is hotter today and which is bigger: LA or NY?" + ) + ai_msg.tool_calls + ``` + + ```python + [ + { + "name": "GetWeather", + "args": {"location": "Los Angeles, CA"}, + "id": "call_6XswGD5Pqk8Tt5atYr7tfenU", + }, + { + "name": "GetWeather", + "args": {"location": "New York, NY"}, + "id": "call_ZVL15vA8Y7kXqOy3dtmQgeCi", + }, + { + "name": "GetPopulation", + "args": {"location": "Los Angeles, CA"}, + "id": "call_49CFW8zqC9W7mh7hbMLSIrXw", + }, + { + "name": "GetPopulation", + "args": {"location": "New York, NY"}, + "id": "call_6ghfKxV264jEfe1mRIkS3PE7", + }, + ] + ``` + + Structured output: + ```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") + rating: int | None = Field( + description="How funny the joke is, from 1 to 10" + ) + + + structured_llm = llm.with_structured_output(Joke) + structured_llm.invoke("Tell me a joke about cats") + ``` + + ```python + Joke( + setup="Why was the cat sitting on the computer?", + punchline="To keep an eye on the mouse!", + rating=None, + ) + ``` + + See `AzureChatOpenAI.with_structured_output()` for more. + + JSON mode: + ```python + json_llm = llm.bind(response_format={"type": "json_object"}) + ai_msg = json_llm.invoke( + "Return a JSON object with key 'random_ints' and a value of 10 random ints in [0-99]" + ) + ai_msg.content + ``` + + ```python + '\\n{\\n "random_ints": [23, 87, 45, 12, 78, 34, 56, 90, 11, 67]\\n}' + ``` + + Image input: + ```python + import base64 + import httpx + from langchain_core.messages import HumanMessage + + image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8") + message = HumanMessage( + content=[ + {"type": "text", "text": "describe the weather in this image"}, + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ] + ) + ai_msg = llm.invoke([message]) + ai_msg.content + ``` + + ```python + "The weather in the image appears to be quite pleasant. The sky is mostly clear" + ``` + + Token usage: + ```python + ai_msg = llm.invoke(messages) + ai_msg.usage_metadata + ``` + + ```python + {"input_tokens": 28, "output_tokens": 5, "total_tokens": 33} + ``` + Logprobs: + ```python + logprobs_llm = llm.bind(logprobs=True) + ai_msg = logprobs_llm.invoke(messages) + ai_msg.response_metadata["logprobs"] + ``` + + ```python + { + "content": [ + { + "token": "J", + "bytes": [74], + "logprob": -4.9617593e-06, + "top_logprobs": [], + }, + { + "token": "'adore", + "bytes": [39, 97, 100, 111, 114, 101], + "logprob": -0.25202933, + "top_logprobs": [], + }, + { + "token": " la", + "bytes": [32, 108, 97], + "logprob": -0.20141791, + "top_logprobs": [], + }, + { + "token": " programmation", + "bytes": [ + 32, + 112, + 114, + 111, + 103, + 114, + 97, + 109, + 109, + 97, + 116, + 105, + 111, + 110, + ], + "logprob": -1.9361265e-07, + "top_logprobs": [], + }, + { + "token": ".", + "bytes": [46], + "logprob": -1.2233183e-05, + "top_logprobs": [], + }, + ] + } + ``` + + Response metadata + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` + + ```python + { + "token_usage": { + "completion_tokens": 6, + "prompt_tokens": 28, + "total_tokens": 34, + }, + "model_name": "gpt-35-turbo", + "system_fingerprint": None, + "prompt_filter_results": [ + { + "prompt_index": 0, + "content_filter_results": { + "hate": {"filtered": False, "severity": "safe"}, + "self_harm": {"filtered": False, "severity": "safe"}, + "sexual": {"filtered": False, "severity": "safe"}, + "violence": {"filtered": False, "severity": "safe"}, + }, + } + ], + "finish_reason": "stop", + "logprobs": None, + "content_filter_results": { + "hate": {"filtered": False, "severity": "safe"}, + "self_harm": {"filtered": False, "severity": "safe"}, + "sexual": {"filtered": False, "severity": "safe"}, + "violence": {"filtered": False, "severity": "safe"}, + }, + } + ``` """ # noqa: E501 azure_endpoint: str | None = Field( @@ -902,38 +898,38 @@ class AzureChatOpenAI(BaseChatOpenAI): ??? example - .. code-block:: python - - from langchain.chat_models import init_chat_model - from pydantic import BaseModel + ```python + from langchain.chat_models import init_chat_model + from pydantic import BaseModel - class ResponseSchema(BaseModel): - response: str + class ResponseSchema(BaseModel): + response: str - def get_weather(location: str) -> str: - \"\"\"Get weather at a location.\"\"\" - pass + def get_weather(location: str) -> str: + \"\"\"Get weather at a location.\"\"\" + pass - llm = init_chat_model("openai:gpt-4o-mini") + llm = init_chat_model("openai:gpt-4o-mini") - structured_llm = llm.with_structured_output( - ResponseSchema, - tools=[get_weather], - strict=True, - include_raw=True, - ) + structured_llm = llm.with_structured_output( + ResponseSchema, + tools=[get_weather], + strict=True, + include_raw=True, + ) - structured_llm.invoke("What's the weather in Boston?") + structured_llm.invoke("What's the weather in Boston?") + ``` - .. code-block:: python - - { - "raw": AIMessage(content="", tool_calls=[...], ...), - "parsing_error": None, - "parsed": None, - } + ```python + { + "raw": AIMessage(content="", tool_calls=[...], ...), + "parsing_error": None, + "parsed": None, + } + ``` kwargs: Additional keyword args are passed through to the model. @@ -974,152 +970,142 @@ class AzureChatOpenAI(BaseChatOpenAI): See all constraints [here](https://platform.openai.com/docs/guides/structured-outputs/supported-schemas). - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_openai import AzureChatOpenAI - from pydantic import BaseModel, Field + from langchain_openai import AzureChatOpenAI + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str | None = Field( - default=..., description="A justification for the answer." - ) - - - llm = AzureChatOpenAI( - azure_deployment="...", model="gpt-4o", 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: str + justification: str | None = Field( + default=..., description="A justification for the answer." ) - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + + llm = AzureChatOpenAI(azure_deployment="...", model="gpt-4o", 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='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` ??? note "Example: `schema=Pydantic` class, `method='function_calling'`, `include_raw=False`, `strict=False`" - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from langchain_openai import AzureChatOpenAI - from pydantic import BaseModel, Field + from langchain_openai import AzureChatOpenAI + from pydantic import BaseModel, Field - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str | None = Field( - default=..., description="A justification for the answer." - ) - - - llm = AzureChatOpenAI( - azure_deployment="...", model="gpt-4o", temperature=0 - ) - structured_llm = llm.with_structured_output( - AnswerWithJustification, method="function_calling" + answer: str + justification: str | None = Field( + default=..., description="A justification for the answer." ) - structured_llm.invoke( - "What weighs more a pound of bricks or a pound of feathers" - ) - # -> AnswerWithJustification( - # answer='They weigh the same', - # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' - # ) + llm = AzureChatOpenAI(azure_deployment="...", model="gpt-4o", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, method="function_calling" + ) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + ``` ??? note "Example: `schema=Pydantic` class, `method='json_schema'`, `include_raw=True`" - .. code-block:: python - - from langchain_openai import AzureChatOpenAI - from pydantic import BaseModel + ```python + from langchain_openai import AzureChatOpenAI + from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: str + answer: str + justification: str - llm = AzureChatOpenAI( - azure_deployment="...", model="gpt-4o", temperature=0 - ) - structured_llm = llm.with_structured_output( - AnswerWithJustification, include_raw=True - ) + llm = AzureChatOpenAI(azure_deployment="...", model="gpt-4o", 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': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), - # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), - # 'parsing_error': None - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + ``` ??? note "Example: `schema=TypedDict` class, `method='json_schema'`, `include_raw=False`, `strict=False`" - .. code-block:: python + ```python + from typing_extensions import Annotated, TypedDict - from typing_extensions import Annotated, TypedDict - - from langchain_openai import AzureChatOpenAI + from langchain_openai import AzureChatOpenAI - class AnswerWithJustification(TypedDict): - '''An answer to the user question along with justification for the answer.''' + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' - answer: str - justification: Annotated[ - str | None, None, "A justification for the answer." - ] + answer: str + justification: Annotated[ + str | None, None, "A justification for the answer." + ] - llm = AzureChatOpenAI( - azure_deployment="...", model="gpt-4o", temperature=0 - ) - structured_llm = llm.with_structured_output(AnswerWithJustification) + llm = AzureChatOpenAI(azure_deployment="...", model="gpt-4o", 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': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' - # } + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + ``` ??? note "Example: `schema=OpenAI` function schema, `method='json_schema'`, `include_raw=False`" - .. code-block:: python + ```python + from langchain_openai import AzureChatOpenAI - from langchain_openai import AzureChatOpenAI - - 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'] - } - } + 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 = AzureChatOpenAI( azure_deployment="...", @@ -1135,59 +1121,62 @@ class AzureChatOpenAI(BaseChatOpenAI): # 'answer': 'They weigh the same', # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' # } + ``` ??? note "Example: `schema=Pydantic` class, `method='json_mode'`, `include_raw=True`" - .. code-block:: + ```python + from langchain_openai import AzureChatOpenAI + from pydantic import BaseModel - from langchain_openai import AzureChatOpenAI - from pydantic import BaseModel - class AnswerWithJustification(BaseModel): - answer: str - justification: str + class AnswerWithJustification(BaseModel): + answer: str + justification: str - llm = AzureChatOpenAI( - azure_deployment="...", - model="gpt-4o", - 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), - # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), - # 'parsing_error': None - # } + llm = AzureChatOpenAI( + azure_deployment="...", + model="gpt-4o", + 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + ``` ??? note "Example: `schema=None`, `method='json_mode'`, `include_raw=True`" - .. code-block:: + ```python + structured_llm = llm.with_structured_output( + method="json_mode", include_raw=True + ) - 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), - # 'parsed': { - # 'answer': 'They are both the same weight.', - # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' - # }, - # 'parsing_error': None - # } + 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 both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': { + # 'answer': 'They are both the same weight.', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.' + # }, + # 'parsing_error': None + # } + ``` """ # noqa: E501 return super().with_structured_output( diff --git a/libs/partners/openai/langchain_openai/embeddings/azure.py b/libs/partners/openai/langchain_openai/embeddings/azure.py index 9d3cbd16400..3b1d03d8797 100644 --- a/libs/partners/openai/langchain_openai/embeddings/azure.py +++ b/libs/partners/openai/langchain_openai/embeddings/azure.py @@ -28,14 +28,14 @@ class AzureOpenAIEmbeddings(OpenAIEmbeddings): # type: ignore[override] instance and key. You can find the key in the Azure Portal, under the “Keys and Endpoint” section of your instance. - .. code-block:: bash + ```bash + pip install -U langchain_openai - pip install -U langchain_openai - - # Set up your environment variables (or pass them directly to the model) - export AZURE_OPENAI_API_KEY="your-api-key" - export AZURE_OPENAI_ENDPOINT="https://.openai.azure.com/" - export AZURE_OPENAI_API_VERSION="2024-02-01" + # Set up your environment variables (or pass them directly to the model) + export AZURE_OPENAI_API_KEY="your-api-key" + export AZURE_OPENAI_ENDPOINT="https://.openai.azure.com/" + export AZURE_OPENAI_API_VERSION="2024-02-01" + ``` Key init args — completion params: model: str @@ -45,61 +45,58 @@ class AzureOpenAIEmbeddings(OpenAIEmbeddings): # type: ignore[override] if the underlying model supports it. Key init args — client params: - api_key: SecretStr | None + api_key: SecretStr | None See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_openai import AzureOpenAIEmbeddings - from langchain_openai import AzureOpenAIEmbeddings - - embeddings = AzureOpenAIEmbeddings( - model="text-embedding-3-large" - # dimensions: int | None = None, # Can specify dimensions with new text-embedding-3 models - # azure_endpoint="https://.openai.azure.com/", If not provided, will read env variable AZURE_OPENAI_ENDPOINT - # api_key=... # Can provide an API key directly. If missing read env variable AZURE_OPENAI_API_KEY - # openai_api_version=..., # If not provided, will read env variable AZURE_OPENAI_API_VERSION - ) + embeddings = AzureOpenAIEmbeddings( + model="text-embedding-3-large" + # dimensions: int | None = None, # Can specify dimensions with new text-embedding-3 models + # azure_endpoint="https://.openai.azure.com/", If not provided, will read env variable AZURE_OPENAI_ENDPOINT + # api_key=... # Can provide an API key directly. If missing read env variable AZURE_OPENAI_API_KEY + # openai_api_version=..., # If not provided, will read env variable AZURE_OPENAI_API_VERSION + ) + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embed.embed_query(input_text) - print(vector[:3]) - - .. code-block:: python - - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + input_text = "The meaning of life is 42" + vector = embed.embed_query(input_text) + print(vector[:3]) + ``` + ```python + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Embed multiple texts: - .. code-block:: python - - input_texts = ["Document 1...", "Document 2..."] + ```python + input_texts = ["Document 1...", "Document 2..."] + ``` vectors = embed.embed_documents(input_texts) print(len(vectors)) # The first 3 coordinates for the first vector print(vectors[0][:3]) - .. code-block:: python - - 2 - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + 2 + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Async: - .. code-block:: python - - vector = await embed.aembed_query(input_text) - print(vector[:3]) - - # multiple: - # await embed.aembed_documents(input_texts) - - .. code-block:: python - - [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ```python + vector = await embed.aembed_query(input_text) + print(vector[:3]) + # multiple: + # await embed.aembed_documents(input_texts) + ``` + ```python + [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ``` """ # noqa: E501 azure_endpoint: str | None = Field( diff --git a/libs/partners/openai/langchain_openai/embeddings/base.py b/libs/partners/openai/langchain_openai/embeddings/base.py index 27817a39d5c..087bfd71b99 100644 --- a/libs/partners/openai/langchain_openai/embeddings/base.py +++ b/libs/partners/openai/langchain_openai/embeddings/base.py @@ -84,10 +84,10 @@ class OpenAIEmbeddings(BaseModel, Embeddings): Setup: Install `langchain_openai` and set environment variable `OPENAI_API_KEY`. - .. code-block:: bash - - pip install -U langchain_openai - export OPENAI_API_KEY="your-api-key" + ```bash + pip install -U langchain_openai + export OPENAI_API_KEY="your-api-key" + ``` Key init args — embedding params: model: str @@ -110,55 +110,51 @@ class OpenAIEmbeddings(BaseModel, Embeddings): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_openai import OpenAIEmbeddings - from langchain_openai import OpenAIEmbeddings - - embed = OpenAIEmbeddings( - model="text-embedding-3-large" - # With the `text-embedding-3` class - # of models, you can specify the size - # of the embeddings you want returned. - # dimensions=1024 - ) + embed = OpenAIEmbeddings( + model="text-embedding-3-large" + # With the `text-embedding-3` class + # of models, you can specify the size + # of the embeddings you want returned. + # dimensions=1024 + ) + ``` Embed single text: - .. code-block:: python - - input_text = "The meaning of life is 42" - vector = embeddings.embed_query("hello") - print(vector[:3]) - - .. code-block:: python - - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + input_text = "The meaning of life is 42" + vector = embeddings.embed_query("hello") + print(vector[:3]) + ``` + ```python + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Embed multiple texts: - .. code-block:: python - - vectors = embeddings.embed_documents(["hello", "goodbye"]) - # Showing only the first 3 coordinates - print(len(vectors)) - print(vectors[0][:3]) - - .. code-block:: python - - 2 - [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ```python + vectors = embeddings.embed_documents(["hello", "goodbye"]) + # Showing only the first 3 coordinates + print(len(vectors)) + print(vectors[0][:3]) + ``` + ```python + 2 + [-0.024603435769677162, -0.007543657906353474, 0.0039630369283258915] + ``` Async: - .. code-block:: python - - await embed.aembed_query(input_text) - print(vector[:3]) - - # multiple: - # await embed.aembed_documents(input_texts) - - .. code-block:: python - - [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ```python + await embed.aembed_query(input_text) + print(vector[:3]) + # multiple: + # await embed.aembed_documents(input_texts) + ``` + ```python + [-0.009100092574954033, 0.005071679595857859, -0.0029193938244134188] + ``` """ client: Any = Field(default=None, exclude=True) #: :meta private: diff --git a/libs/partners/openai/langchain_openai/llms/azure.py b/libs/partners/openai/langchain_openai/llms/azure.py index 97cc219dbcc..dc63d32f8e7 100644 --- a/libs/partners/openai/langchain_openai/llms/azure.py +++ b/libs/partners/openai/langchain_openai/llms/azure.py @@ -27,12 +27,11 @@ class AzureOpenAI(BaseOpenAI): in, even if not explicitly saved on this class. Example: - .. code-block:: python - - from langchain_openai import AzureOpenAI - - openai = AzureOpenAI(model_name="gpt-3.5-turbo-instruct") + ```python + from langchain_openai import AzureOpenAI + openai = AzureOpenAI(model_name="gpt-3.5-turbo-instruct") + ``` """ azure_endpoint: str | None = Field( diff --git a/libs/partners/openai/langchain_openai/llms/base.py b/libs/partners/openai/langchain_openai/llms/base.py index b7d92c10aed..1668854bba2 100644 --- a/libs/partners/openai/langchain_openai/llms/base.py +++ b/libs/partners/openai/langchain_openai/llms/base.py @@ -56,10 +56,10 @@ class BaseOpenAI(BaseLLM): Setup: Install `langchain-openai` and set environment variable `OPENAI_API_KEY`. - .. code-block:: bash - - pip install -U langchain-openai - export OPENAI_API_KEY="your-api-key" + ```bash + pip install -U langchain-openai + export OPENAI_API_KEY="your-api-key" + ``` Key init args — completion params: model_name: str @@ -107,62 +107,60 @@ class BaseOpenAI(BaseLLM): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_openai.llms.base import BaseOpenAI - from langchain_openai.llms.base import BaseOpenAI - - llm = BaseOpenAI( - model_name="gpt-3.5-turbo-instruct", - temperature=0.7, - max_tokens=256, - top_p=1, - frequency_penalty=0, - presence_penalty=0, - # openai_api_key="...", - # openai_api_base="...", - # openai_organization="...", - # other params... - ) + llm = BaseOpenAI( + model_name="gpt-3.5-turbo-instruct", + temperature=0.7, + max_tokens=256, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + # openai_api_key="...", + # openai_api_base="...", + # openai_organization="...", + # other params... + ) + ``` Invoke: - .. code-block:: python + ```python + input_text = "The meaning of life is " + response = llm.invoke(input_text) + print(response) + ``` - input_text = "The meaning of life is " - response = llm.invoke(input_text) - print(response) - - .. code-block:: - - "a philosophical question that has been debated by thinkers and - scholars for centuries." + ```txt + "a philosophical question that has been debated by thinkers and + scholars for centuries." + ``` Stream: - .. code-block:: python - - for chunk in llm.stream(input_text): - print(chunk, end="") - - .. code-block:: - - a philosophical question that has been debated by thinkers and - scholars for centuries. + ```python + for chunk in llm.stream(input_text): + print(chunk, end="") + ``` + ```txt + a philosophical question that has been debated by thinkers and + scholars for centuries. + ``` Async: - .. code-block:: python + ```python + response = await llm.ainvoke(input_text) - response = await llm.ainvoke(input_text) + # stream: + # async for chunk in llm.astream(input_text): + # print(chunk, end="") - # stream: - # async for chunk in llm.astream(input_text): - # print(chunk, end="") - - # batch: - # await llm.abatch([input_text]) - - .. code-block:: - - "a philosophical question that has been debated by thinkers and - scholars for centuries." + # batch: + # await llm.abatch([input_text]) + ``` + ``` + "a philosophical question that has been debated by thinkers and + scholars for centuries." + ``` """ @@ -404,10 +402,9 @@ class BaseOpenAI(BaseLLM): The full LLM output. Example: - .. code-block:: python - - response = openai.generate(["Tell me a joke."]) - + ```python + response = openai.generate(["Tell me a joke."]) + ``` """ # TODO: write a unit test for this params = self._invocation_params @@ -626,10 +623,9 @@ class BaseOpenAI(BaseLLM): The maximum context size Example: - .. code-block:: python - - max_tokens = openai.modelname_to_contextsize("gpt-3.5-turbo-instruct") - + ```python + max_tokens = openai.modelname_to_contextsize("gpt-3.5-turbo-instruct") + ``` """ model_token_mapping = { "gpt-4o-mini": 128_000, @@ -691,10 +687,9 @@ class BaseOpenAI(BaseLLM): The maximum number of tokens to generate for a prompt. Example: - .. code-block:: python - - max_tokens = openai.max_tokens_for_prompt("Tell me a joke.") - + ```python + max_tokens = openai.max_tokens_for_prompt("Tell me a joke.") + ``` """ num_tokens = self.get_num_tokens(prompt) return self.max_context_size - num_tokens @@ -706,10 +701,10 @@ class OpenAI(BaseOpenAI): Setup: Install `langchain-openai` and set environment variable `OPENAI_API_KEY`. - .. code-block:: bash - - pip install -U langchain-openai - export OPENAI_API_KEY="your-api-key" + ```bash + pip install -U langchain-openai + export OPENAI_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -741,64 +736,59 @@ class OpenAI(BaseOpenAI): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python + ```python + from langchain_openai import OpenAI - from langchain_openai import OpenAI - - llm = OpenAI( - model="gpt-3.5-turbo-instruct", - temperature=0, - max_retries=2, - # api_key="...", - # base_url="...", - # organization="...", - # other params... - ) + llm = OpenAI( + model="gpt-3.5-turbo-instruct", + temperature=0, + max_retries=2, + # api_key="...", + # base_url="...", + # organization="...", + # other params... + ) + ``` Invoke: - .. code-block:: python - - input_text = "The meaning of life is " - llm.invoke(input_text) - - .. code-block:: - - "a philosophical question that has been debated by thinkers and scholars for centuries." + ```python + input_text = "The meaning of life is " + llm.invoke(input_text) + ``` + ```txt + "a philosophical question that has been debated by thinkers and scholars for centuries." + ``` Stream: - .. code-block:: python + ```python + for chunk in llm.stream(input_text): + print(chunk, end="|") + ``` + ```txt + a| philosophical| question| that| has| been| debated| by| thinkers| and| scholars| for| centuries|. + ``` - for chunk in llm.stream(input_text): - print(chunk, end="|") - - .. code-block:: - - a| philosophical| question| that| has| been| debated| by| thinkers| and| scholars| for| centuries|. - - .. code-block:: python - - "".join(llm.stream(input_text)) - - .. code-block:: - - "a philosophical question that has been debated by thinkers and scholars for centuries." + ```python + "".join(llm.stream(input_text)) + ``` + ```txt + "a philosophical question that has been debated by thinkers and scholars for centuries." + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke(input_text) - await llm.ainvoke(input_text) - - # stream: - # async for chunk in (await llm.astream(input_text)): - # print(chunk) - - # batch: - # await llm.abatch([input_text]) - - .. code-block:: - - "a philosophical question that has been debated by thinkers and scholars for centuries." + # stream: + # async for chunk in (await llm.astream(input_text)): + # print(chunk) + # batch: + # await llm.abatch([input_text]) + ``` + ```txt + "a philosophical question that has been debated by thinkers and scholars for centuries." + ``` """ # noqa: E501 @classmethod diff --git a/libs/partners/openai/langchain_openai/tools/custom_tool.py b/libs/partners/openai/langchain_openai/tools/custom_tool.py index 02410a0e2ec..0724d56277c 100644 --- a/libs/partners/openai/langchain_openai/tools/custom_tool.py +++ b/libs/partners/openai/langchain_openai/tools/custom_tool.py @@ -31,65 +31,65 @@ def custom_tool(*args: Any, **kwargs: Any) -> Any: See below for an example using LangGraph: - .. code-block:: python - - @custom_tool - def execute_code(code: str) -> str: - \"\"\"Execute python code.\"\"\" - return "27" + ```python + @custom_tool + def execute_code(code: str) -> str: + \"\"\"Execute python code.\"\"\" + return "27" - llm = ChatOpenAI(model="gpt-5", output_version="responses/v1") + llm = ChatOpenAI(model="gpt-5", output_version="responses/v1") - agent = create_react_agent(llm, [execute_code]) + agent = create_react_agent(llm, [execute_code]) - input_message = {"role": "user", "content": "Use the tool to calculate 3^3."} - for step in agent.stream( - {"messages": [input_message]}, - stream_mode="values", - ): - step["messages"][-1].pretty_print() + input_message = {"role": "user", "content": "Use the tool to calculate 3^3."} + for step in agent.stream( + {"messages": [input_message]}, + stream_mode="values", + ): + step["messages"][-1].pretty_print() + ``` You can also specify a format for a corresponding context-free grammar using the `format` kwarg: - .. code-block:: python + ```python + from langchain_openai import ChatOpenAI, custom_tool + from langgraph.prebuilt import create_react_agent - from langchain_openai import ChatOpenAI, custom_tool - from langgraph.prebuilt import create_react_agent + grammar = \"\"\" + start: expr + expr: term (SP ADD SP term)* -> add + | term + term: factor (SP MUL SP factor)* -> mul + | factor + factor: INT + SP: " " + ADD: "+" + MUL: "*" + %import common.INT + \"\"\" - grammar = \"\"\" - start: expr - expr: term (SP ADD SP term)* -> add - | term - term: factor (SP MUL SP factor)* -> mul - | factor - factor: INT - SP: " " - ADD: "+" - MUL: "*" - %import common.INT - \"\"\" + format = {"type": "grammar", "syntax": "lark", "definition": grammar} - format = {"type": "grammar", "syntax": "lark", "definition": grammar} - - # highlight-next-line - @custom_tool(format=format) - def do_math(input_string: str) -> str: - \"\"\"Do a mathematical operation.\"\"\" - return "27" + # highlight-next-line + @custom_tool(format=format) + def do_math(input_string: str) -> str: + \"\"\"Do a mathematical operation.\"\"\" + return "27" - llm = ChatOpenAI(model="gpt-5", output_version="responses/v1") + llm = ChatOpenAI(model="gpt-5", output_version="responses/v1") - agent = create_react_agent(llm, [do_math]) + agent = create_react_agent(llm, [do_math]) - input_message = {"role": "user", "content": "Use the tool to calculate 3^3."} - for step in agent.stream( - {"messages": [input_message]}, - stream_mode="values", - ): - step["messages"][-1].pretty_print() + input_message = {"role": "user", "content": "Use the tool to calculate 3^3."} + for step in agent.stream( + {"messages": [input_message]}, + stream_mode="values", + ): + step["messages"][-1].pretty_print() + ``` """ def decorator(func: Callable[..., Any]) -> Any: diff --git a/libs/partners/perplexity/langchain_perplexity/chat_models.py b/libs/partners/perplexity/langchain_perplexity/chat_models.py index e8cefba3383..3cd1b1a76d7 100644 --- a/libs/partners/perplexity/langchain_perplexity/chat_models.py +++ b/libs/partners/perplexity/langchain_perplexity/chat_models.py @@ -67,9 +67,9 @@ class ChatPerplexity(BaseChatModel): Any parameters that are valid to be passed to the openai.create call can be passed in, even if not explicitly saved on this class. - .. code-block:: bash - - export PPLX_API_KEY=your_api_key + ```bash + export PPLX_API_KEY=your_api_key + ``` Key init args - completion params: model: str @@ -92,55 +92,58 @@ class ChatPerplexity(BaseChatModel): See full list of supported init args and their descriptions in the params section. Instantiate: - .. code-block:: python - from langchain_perplexity import ChatPerplexity + ```python + from langchain_perplexity import ChatPerplexity - llm = ChatPerplexity(model="sonar", temperature=0.7) + llm = ChatPerplexity(model="sonar", temperature=0.7) + ``` Invoke: - .. code-block:: python - messages = [("system", "You are a chatbot."), ("user", "Hello!")] - llm.invoke(messages) + ```python + messages = [("system", "You are a chatbot."), ("user", "Hello!")] + llm.invoke(messages) + ``` Invoke with structured output: - .. code-block:: python - from pydantic import BaseModel + ```python + from pydantic import BaseModel - class StructuredOutput(BaseModel): - role: str - content: str + class StructuredOutput(BaseModel): + role: str + content: str - llm.with_structured_output(StructuredOutput) - llm.invoke(messages) + llm.with_structured_output(StructuredOutput) + llm.invoke(messages) + ``` Invoke with perplexity-specific params: - .. code-block:: python - llm.invoke(messages, extra_body={"search_recency_filter": "week"}) + ```python + llm.invoke(messages, extra_body={"search_recency_filter": "week"}) + ``` Stream: - .. code-block:: python - - for chunk in llm.stream(messages): - print(chunk.content) + ```python + for chunk in llm.stream(messages): + print(chunk.content) + ``` Token usage: - .. code-block:: python - - response = llm.invoke(messages) - response.usage_metadata + ```python + response = llm.invoke(messages) + response.usage_metadata + ``` Response metadata: - .. code-block:: python - - response = llm.invoke(messages) - response.response_metadata - + ```python + response = llm.invoke(messages) + response.response_metadata + ``` """ # noqa: E501 client: Any = None #: :meta private: diff --git a/libs/partners/qdrant/langchain_qdrant/qdrant.py b/libs/partners/qdrant/langchain_qdrant/qdrant.py index 58ec8ad6ca5..922c4122658 100644 --- a/libs/partners/qdrant/langchain_qdrant/qdrant.py +++ b/libs/partners/qdrant/langchain_qdrant/qdrant.py @@ -42,9 +42,9 @@ class QdrantVectorStore(VectorStore): Setup: Install `langchain-qdrant` package. - .. code-block:: bash - - pip install -qU langchain-qdrant + ```bash + pip install -qU langchain-qdrant + ``` Key init args — indexing params: collection_name: str @@ -61,150 +61,148 @@ class QdrantVectorStore(VectorStore): Retrieval mode to use. Instantiate: - .. code-block:: python + ```python + from langchain_qdrant import QdrantVectorStore + from qdrant_client import QdrantClient + from qdrant_client.http.models import Distance, VectorParams + from langchain_openai import OpenAIEmbeddings - from langchain_qdrant import QdrantVectorStore - from qdrant_client import QdrantClient - from qdrant_client.http.models import Distance, VectorParams - from langchain_openai import OpenAIEmbeddings + client = QdrantClient(":memory:") - client = QdrantClient(":memory:") + client.create_collection( + collection_name="demo_collection", + vectors_config=VectorParams(size=1536, distance=Distance.COSINE), + ) - client.create_collection( - collection_name="demo_collection", - vectors_config=VectorParams(size=1536, distance=Distance.COSINE), - ) - - vector_store = QdrantVectorStore( - client=client, - collection_name="demo_collection", - embedding=OpenAIEmbeddings(), - ) + vector_store = QdrantVectorStore( + client=client, + collection_name="demo_collection", + embedding=OpenAIEmbeddings(), + ) + ``` Add Documents: - .. code-block:: python + ```python + from langchain_core.documents import Document + from uuid import uuid4 - from langchain_core.documents import Document - from uuid import uuid4 + document_1 = Document(page_content="foo", metadata={"baz": "bar"}) + document_2 = Document(page_content="thud", metadata={"bar": "baz"}) + document_3 = Document(page_content="i will be deleted :(") - document_1 = Document(page_content="foo", metadata={"baz": "bar"}) - document_2 = Document(page_content="thud", metadata={"bar": "baz"}) - document_3 = Document(page_content="i will be deleted :(") - - documents = [document_1, document_2, document_3] - ids = [str(uuid4()) for _ in range(len(documents))] - vector_store.add_documents(documents=documents, ids=ids) + documents = [document_1, document_2, document_3] + ids = [str(uuid4()) for _ in range(len(documents))] + vector_store.add_documents(documents=documents, ids=ids) + ``` Delete Documents: - .. code-block:: python - - vector_store.delete(ids=[ids[-1]]) + ```python + vector_store.delete(ids=[ids[-1]]) + ``` Search: - .. code-block:: python + ```python + results = vector_store.similarity_search( + query="thud", + k=1, + ) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search( - query="thud", - k=1, - ) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - *thud[ - { - "bar": "baz", - "_id": "0d706099-6dd9-412a-9df6-a71043e020de", - "_collection_name": "demo_collection", - } - ] + ```python + *thud[ + { + "bar": "baz", + "_id": "0d706099-6dd9-412a-9df6-a71043e020de", + "_collection_name": "demo_collection", + } + ] + ``` Search with filter: - .. code-block:: python + ```python + from qdrant_client.http import models - from qdrant_client.http import models + results = vector_store.similarity_search( + query="thud", + k=1, + filter=models.Filter( + must=[ + models.FieldCondition( + key="metadata.bar", + match=models.MatchValue(value="baz"), + ) + ] + ), + ) + for doc in results: + print(f"* {doc.page_content} [{doc.metadata}]") + ``` - results = vector_store.similarity_search( - query="thud", - k=1, - filter=models.Filter( - must=[ - models.FieldCondition( - key="metadata.bar", - match=models.MatchValue(value="baz"), - ) - ] - ), - ) - for doc in results: - print(f"* {doc.page_content} [{doc.metadata}]") + ```python + *thud[ + { + "bar": "baz", + "_id": "0d706099-6dd9-412a-9df6-a71043e020de", + "_collection_name": "demo_collection", + } + ] + ``` - .. code-block:: python + Search with score: + ```python + results = vector_store.similarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` - *thud[ - { + ```python + * [SIM=0.832268] foo [{'baz': 'bar', '_id': '44ec7094-b061-45ac-8fbf-014b0f18e8aa', '_collection_name': 'demo_collection'}] + ``` + + Async: + ```python + # add documents + # await vector_store.aadd_documents(documents=documents, ids=ids) + + # delete documents + # await vector_store.adelete(ids=["3"]) + + # search + # results = vector_store.asimilarity_search(query="thud",k=1) + + # search with score + results = await vector_store.asimilarity_search_with_score(query="qux", k=1) + for doc, score in results: + print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") + ``` + + ```python + * [SIM=0.832268] foo [{'baz': 'bar', '_id': '44ec7094-b061-45ac-8fbf-014b0f18e8aa', '_collection_name': 'demo_collection'}] + ``` + + Use as Retriever: + ```python + retriever = vector_store.as_retriever( + search_type="mmr", + search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, + ) + retriever.invoke("thud") + ``` + + ```python + [ + Document( + metadata={ "bar": "baz", "_id": "0d706099-6dd9-412a-9df6-a71043e020de", "_collection_name": "demo_collection", - } - ] - - - Search with score: - .. code-block:: python - - results = vector_store.similarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - * [SIM=0.832268] foo [{'baz': 'bar', '_id': '44ec7094-b061-45ac-8fbf-014b0f18e8aa', '_collection_name': 'demo_collection'}] - - Async: - .. code-block:: python - - # add documents - # await vector_store.aadd_documents(documents=documents, ids=ids) - - # delete documents - # await vector_store.adelete(ids=["3"]) - - # search - # results = vector_store.asimilarity_search(query="thud",k=1) - - # search with score - results = await vector_store.asimilarity_search_with_score(query="qux", k=1) - for doc, score in results: - print(f"* [SIM={score:3f}] {doc.page_content} [{doc.metadata}]") - - .. code-block:: python - - * [SIM=0.832268] foo [{'baz': 'bar', '_id': '44ec7094-b061-45ac-8fbf-014b0f18e8aa', '_collection_name': 'demo_collection'}] - - Use as Retriever: - .. code-block:: python - - retriever = vector_store.as_retriever( - search_type="mmr", - search_kwargs={"k": 1, "fetch_k": 2, "lambda_mult": 0.5}, + }, + page_content="thud", ) - retriever.invoke("thud") - - .. code-block:: python - - [ - Document( - metadata={ - "bar": "baz", - "_id": "0d706099-6dd9-412a-9df6-a71043e020de", - "_collection_name": "demo_collection", - }, - page_content="thud", - ) - ] - + ] + ``` """ # noqa: E501 CONTENT_KEY: str = "page_content" @@ -229,16 +227,15 @@ class QdrantVectorStore(VectorStore): ) -> None: """Initialize a new instance of `QdrantVectorStore`. - Example: - .. code-block:: python - qdrant = Qdrant( - client=client, - collection_name="my-collection", - embedding=OpenAIEmbeddings(), - retrieval_mode=RetrievalMode.HYBRID, - sparse_embedding=FastEmbedSparse(), - ) - + ```python + qdrant = Qdrant( + client=client, + collection_name="my-collection", + embedding=OpenAIEmbeddings(), + retrieval_mode=RetrievalMode.HYBRID, + sparse_embedding=FastEmbedSparse(), + ) + ``` """ if validate_embeddings: self._validate_embeddings(retrieval_mode, embedding, sparse_embedding) @@ -385,14 +382,13 @@ class QdrantVectorStore(VectorStore): This is intended to be a quick way to get started. - Example: - .. code-block:: python - - from langchain_qdrant import Qdrant - from langchain_openai import OpenAIEmbeddings - embeddings = OpenAIEmbeddings() - qdrant = Qdrant.from_texts(texts, embeddings, url="http://localhost:6333") + ```python + from langchain_qdrant import Qdrant + from langchain_openai import OpenAIEmbeddings + embeddings = OpenAIEmbeddings() + qdrant = Qdrant.from_texts(texts, embeddings, url="http://localhost:6333") + ``` """ if sparse_vector_params is None: sparse_vector_params = {} diff --git a/libs/partners/qdrant/langchain_qdrant/vectorstores.py b/libs/partners/qdrant/langchain_qdrant/vectorstores.py index 4bc465cf39d..47f6c61e06c 100644 --- a/libs/partners/qdrant/langchain_qdrant/vectorstores.py +++ b/libs/partners/qdrant/langchain_qdrant/vectorstores.py @@ -60,16 +60,14 @@ def sync_call_fallback(method: Callable) -> Callable: class Qdrant(VectorStore): """`Qdrant` vector store. - Example: - .. code-block:: python - - from qdrant_client import QdrantClient - from langchain_qdrant import Qdrant - - client = QdrantClient() - collection_name = "MyCollection" - qdrant = Qdrant(client, collection_name, embedding_function) + ```python + from qdrant_client import QdrantClient + from langchain_qdrant import Qdrant + client = QdrantClient() + collection_name = "MyCollection" + qdrant = Qdrant(client, collection_name, embedding_function) + ``` """ CONTENT_KEY: str = "page_content" @@ -398,9 +396,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to @@ -457,9 +455,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to QdrantClient.search() @@ -516,9 +514,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to @@ -575,9 +573,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to QdrantClient.search() @@ -664,9 +662,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to @@ -744,11 +742,9 @@ class Qdrant(VectorStore): query: Text to look up documents similar to. k: Number of Documents to return. Defaults to 4. fetch_k: Number of Documents to fetch to pass to MMR algorithm. - Defaults to 20. lambda_mult: Number between 0 and 1 that determines the degree - of diversity among the results with 0 corresponding - to maximum diversity and 1 to minimum diversity. - Defaults to 0.5. + of diversity among the results with 0 corresponding to maximum diversity + and 1 to minimum diversity. filter: Filter by metadata. Defaults to `None`. search_params: Additional search params score_threshold: @@ -764,9 +760,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to QdrantClient.search() @@ -810,7 +806,6 @@ class Qdrant(VectorStore): query: Text to look up documents similar to. k: Number of Documents to return. Defaults to 4. fetch_k: Number of Documents to fetch to pass to MMR algorithm. - Defaults to 20. lambda_mult: Number between 0 and 1 that determines the degree of diversity among the results with 0 corresponding to maximum diversity and 1 to minimum diversity. @@ -830,9 +825,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to @@ -895,9 +890,9 @@ class Qdrant(VectorStore): - int - number of replicas to query, values should present in all queried replicas - 'majority' - query all replicas, but return values present in the - majority of replicas + majority of replicas - 'quorum' - query the majority of replicas, return values present in - all of them + all of them - 'all' - query all replicas, and return values present in all replicas **kwargs: Any other named arguments to pass through to QdrantClient.search() @@ -941,7 +936,6 @@ class Qdrant(VectorStore): embedding: Embedding vector to look up documents similar to. k: Number of Documents to return. Defaults to 4. fetch_k: Number of Documents to fetch to pass to MMR algorithm. - Defaults to 20. lambda_mult: Number between 0 and 1 that determines the degree of diversity among the results with 0 corresponding to maximum diversity and 1 to minimum diversity. @@ -1337,15 +1331,13 @@ class Qdrant(VectorStore): This is intended to be a quick way to get started. - Example: - .. code-block:: python - - from langchain_qdrant import Qdrant - from langchain_openai import OpenAIEmbeddings - - embeddings = OpenAIEmbeddings() - qdrant = Qdrant.from_texts(texts, embeddings, "localhost") + ```python + from langchain_qdrant import Qdrant + from langchain_openai import OpenAIEmbeddings + embeddings = OpenAIEmbeddings() + qdrant = Qdrant.from_texts(texts, embeddings, "localhost") + ``` """ qdrant = cls.construct_instance( texts, @@ -1574,20 +1566,18 @@ class Qdrant(VectorStore): This is a user-friendly interface that: 1. Creates embeddings, one for each text 2. Initializes the Qdrant database as an in-memory docstore by default - (and overridable to a remote docstore) + (and overridable to a remote docstore) 3. Adds the text embeddings to the Qdrant database This is intended to be a quick way to get started. - Example: - .. code-block:: python - - from langchain_qdrant import Qdrant - from langchain_openai import OpenAIEmbeddings - - embeddings = OpenAIEmbeddings() - qdrant = await Qdrant.afrom_texts(texts, embeddings, "localhost") + ```python + from langchain_qdrant import Qdrant + from langchain_openai import OpenAIEmbeddings + embeddings = OpenAIEmbeddings() + qdrant = await Qdrant.afrom_texts(texts, embeddings, "localhost") + ``` """ qdrant = await cls.aconstruct_instance( texts, diff --git a/libs/partners/xai/langchain_xai/chat_models.py b/libs/partners/xai/langchain_xai/chat_models.py index 2bf53caedc0..2f0224bfa21 100644 --- a/libs/partners/xai/langchain_xai/chat_models.py +++ b/libs/partners/xai/langchain_xai/chat_models.py @@ -32,11 +32,10 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] Setup: Install `langchain-xai` and set environment variable `XAI_API_KEY`. - .. code-block:: bash - - pip install -U langchain-xai - export XAI_API_KEY="your-api-key" - + ```bash + pip install -U langchain-xai + export XAI_API_KEY="your-api-key" + ``` Key init args — completion params: model: str @@ -60,107 +59,107 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] xAI API key. If not passed in will be read from env var `XAI_API_KEY`. Instantiate: - .. code-block:: python + ```python + from langchain_xai import ChatXAI - from langchain_xai import ChatXAI - - llm = ChatXAI( - model="grok-4", - temperature=0, - max_tokens=None, - timeout=None, - max_retries=2, - # api_key="...", - # other params... - ) + llm = ChatXAI( + model="grok-4", + temperature=0, + max_tokens=None, + timeout=None, + max_retries=2, + # api_key="...", + # other params... + ) + ``` Invoke: - .. code-block:: python + ```python + messages = [ + ( + "system", + "You are a helpful translator. Translate the user sentence to French.", + ), + ("human", "I love programming."), + ] + llm.invoke(messages) + ``` - messages = [ - ( - "system", - "You are a helpful translator. Translate the user sentence to French.", - ), - ("human", "I love programming."), - ] - llm.invoke(messages) - - .. code-block:: python - - AIMessage( - content="J'adore la programmation.", - response_metadata={ - "token_usage": { - "completion_tokens": 9, - "prompt_tokens": 32, - "total_tokens": 41, - }, - "model_name": "grok-4", - "system_fingerprint": None, - "finish_reason": "stop", - "logprobs": None, - }, - id="run-168dceca-3b8b-4283-94e3-4c739dbc1525-0", - usage_metadata={ - "input_tokens": 32, - "output_tokens": 9, + ```python + AIMessage( + content="J'adore la programmation.", + response_metadata={ + "token_usage": { + "completion_tokens": 9, + "prompt_tokens": 32, "total_tokens": 41, }, - ) + "model_name": "grok-4", + "system_fingerprint": None, + "finish_reason": "stop", + "logprobs": None, + }, + id="run-168dceca-3b8b-4283-94e3-4c739dbc1525-0", + usage_metadata={ + "input_tokens": 32, + "output_tokens": 9, + "total_tokens": 41, + }, + ) + ``` Stream: - .. code-block:: python + ```python + for chunk in llm.stream(messages): + print(chunk.text, end="") + ``` - for chunk in llm.stream(messages): - print(chunk.text, end="") - - .. code-block:: python - - content='J' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content="'" id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content='ad' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content='ore' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content=' la' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content=' programm' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content='ation' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content='.' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' - content='' response_metadata={'finish_reason': 'stop', 'model_name': 'grok-4'} id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + ```python + content='J' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content="'" id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content='ad' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content='ore' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content=' la' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content=' programm' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content='ation' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content='.' id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + content='' response_metadata={'finish_reason': 'stop', 'model_name': 'grok-4'} id='run-1bc996b5-293f-4114-96a1-e0f755c05eb9' + ``` Async: - .. code-block:: python + ```python + await llm.ainvoke(messages) - await llm.ainvoke(messages) + # stream: + # async for chunk in (await llm.astream(messages)) - # stream: - # async for chunk in (await llm.astream(messages)) + # batch: + # await llm.abatch([messages]) + ``` - # batch: - # await llm.abatch([messages]) - - .. code-block:: python - - AIMessage( - content="J'adore la programmation.", - response_metadata={ - "token_usage": { - "completion_tokens": 9, - "prompt_tokens": 32, - "total_tokens": 41, - }, - "model_name": "grok-4", - "system_fingerprint": None, - "finish_reason": "stop", - "logprobs": None, - }, - id="run-09371a11-7f72-4c53-8e7c-9de5c238b34c-0", - usage_metadata={ - "input_tokens": 32, - "output_tokens": 9, + ```python + AIMessage( + content="J'adore la programmation.", + response_metadata={ + "token_usage": { + "completion_tokens": 9, + "prompt_tokens": 32, "total_tokens": 41, }, - ) + "model_name": "grok-4", + "system_fingerprint": None, + "finish_reason": "stop", + "logprobs": None, + }, + id="run-09371a11-7f72-4c53-8e7c-9de5c238b34c-0", + usage_metadata={ + "input_tokens": 32, + "output_tokens": 9, + "total_tokens": 41, + }, + ) + ``` Reasoning: [Certain xAI models](https://docs.x.ai/docs/models#model-pricing) support reasoning, @@ -173,12 +172,12 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] argument, which will control the amount of reasoning the model does. The value can be one of `'low'` or `'high'`. - .. code-block:: python - - model = ChatXAI( - model="grok-3-mini", - extra_body={"reasoning_effort": "high"}, - ) + ```python + model = ChatXAI( + model="grok-3-mini", + extra_body={"reasoning_effort": "high"}, + ) + ``` !!! note As of 2025-07-10, `reasoning_content` is only returned in Grok 3 models, such as @@ -190,45 +189,45 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] reasoning cannot be disabled, and the `reasoning_effort` cannot be specified. Tool calling / function calling: - .. code-block:: python + ```python + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field - - llm = ChatXAI(model="grok-4") + llm = ChatXAI(model="grok-4") - class GetWeather(BaseModel): - '''Get the current weather in a given location''' + class GetWeather(BaseModel): + '''Get the current weather in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - class GetPopulation(BaseModel): - '''Get the current population in a given location''' + class GetPopulation(BaseModel): + '''Get the current population in a given location''' - location: str = Field(..., description="The city and state, e.g. San Francisco, CA") + location: str = Field(..., description="The city and state, e.g. San Francisco, CA") - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke("Which city is bigger: LA or NY?") - ai_msg.tool_calls + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke("Which city is bigger: LA or NY?") + ai_msg.tool_calls + ``` - .. code-block:: python - - [ - { - "name": "GetPopulation", - "args": {"location": "NY"}, - "id": "call_m5tstyn2004pre9bfuxvom8x", - "type": "tool_call", - }, - { - "name": "GetPopulation", - "args": {"location": "LA"}, - "id": "call_0vjgq455gq1av5sp9eb1pw6a", - "type": "tool_call", - }, - ] + ```python + [ + { + "name": "GetPopulation", + "args": {"location": "NY"}, + "id": "call_m5tstyn2004pre9bfuxvom8x", + "type": "tool_call", + }, + { + "name": "GetPopulation", + "args": {"location": "LA"}, + "id": "call_0vjgq455gq1av5sp9eb1pw6a", + "type": "tool_call", + }, + ] + ``` !!! note With stream response, the tool / function call will be returned in whole in a @@ -236,59 +235,59 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] Tool choice can be controlled by setting the `tool_choice` parameter in the model constructor's `extra_body` argument. For example, to disable tool / function calling: - .. code-block:: python - - llm = ChatXAI(model="grok-4", extra_body={"tool_choice": "none"}) + ```python + llm = ChatXAI(model="grok-4", extra_body={"tool_choice": "none"}) + ``` To require that the model always calls a tool / function, set `tool_choice` to `'required'`: - .. code-block:: python - - llm = ChatXAI(model="grok-4", extra_body={"tool_choice": "required"}) + ```python + llm = ChatXAI(model="grok-4", extra_body={"tool_choice": "required"}) + ``` To specify a tool / function to call, set `tool_choice` to the name of the tool / function: - .. code-block:: python + ```python + from pydantic import BaseModel, Field - from pydantic import BaseModel, Field + llm = ChatXAI( + model="grok-4", + extra_body={ + "tool_choice": {"type": "function", "function": {"name": "GetWeather"}} + }, + ) - llm = ChatXAI( - model="grok-4", - extra_body={ - "tool_choice": {"type": "function", "function": {"name": "GetWeather"}} - }, - ) + class GetWeather(BaseModel): + \"\"\"Get the current weather in a given location\"\"\" - class GetWeather(BaseModel): - \"\"\"Get the current weather in a given location\"\"\" - - location: str = Field(..., description='The city and state, e.g. San Francisco, CA') + location: str = Field(..., description='The city and state, e.g. San Francisco, CA') - class GetPopulation(BaseModel): - \"\"\"Get the current population in a given location\"\"\" + class GetPopulation(BaseModel): + \"\"\"Get the current population in a given location\"\"\" - location: str = Field(..., description='The city and state, e.g. San Francisco, CA') + location: str = Field(..., description='The city and state, e.g. San Francisco, CA') - llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) - ai_msg = llm_with_tools.invoke( - "Which city is bigger: LA or NY?", - ) - ai_msg.tool_calls + llm_with_tools = llm.bind_tools([GetWeather, GetPopulation]) + ai_msg = llm_with_tools.invoke( + "Which city is bigger: LA or NY?", + ) + ai_msg.tool_calls + ``` The resulting tool call would be: - .. code-block:: python - - [ - { - "name": "GetWeather", - "args": {"location": "Los Angeles, CA"}, - "id": "call_81668711", - "type": "tool_call", - } - ] + ```python + [ + { + "name": "GetWeather", + "args": {"location": "Los Angeles, CA"}, + "id": "call_81668711", + "type": "tool_call", + } + ] + ``` Parallel tool calling / parallel function calling: By default, parallel tool / function calling is enabled, so you can process @@ -296,104 +295,103 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] are required, all of the tool call requests will be included in the response body. Structured output: - .. code-block:: python + ```python + from typing import Optional - from typing import Optional - - from pydantic import BaseModel, Field + from pydantic import BaseModel, Field - class Joke(BaseModel): - '''Joke to tell user.''' + 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") - rating: int | None = Field(description="How funny the joke is, from 1 to 10") + setup: str = Field(description="The setup of the joke") + punchline: str = Field(description="The punchline to the joke") + rating: int | None = Field(description="How funny the joke is, from 1 to 10") - structured_llm = llm.with_structured_output(Joke) - structured_llm.invoke("Tell me a joke about cats") + structured_llm = llm.with_structured_output(Joke) + structured_llm.invoke("Tell me a joke about cats") + ``` - .. code-block:: python - - Joke( - setup="Why was the cat sitting on the computer?", - punchline="To keep an eye on the mouse!", - rating=7, - ) + ```python + Joke( + setup="Why was the cat sitting on the computer?", + punchline="To keep an eye on the mouse!", + rating=7, + ) + ``` Live Search: xAI supports a [Live Search](https://docs.x.ai/docs/guides/live-search) feature that enables Grok to ground its answers using results from web searches. - .. code-block:: python + ```python + from langchain_xai import ChatXAI - from langchain_xai import ChatXAI + llm = ChatXAI( + model="grok-4", + search_parameters={ + "mode": "auto", + # Example optional parameters below: + "max_search_results": 3, + "from_date": "2025-05-26", + "to_date": "2025-05-27", + }, + ) - llm = ChatXAI( - model="grok-4", - search_parameters={ - "mode": "auto", - # Example optional parameters below: - "max_search_results": 3, - "from_date": "2025-05-26", - "to_date": "2025-05-27", - }, - ) - - llm.invoke("Provide me a digest of world news in the last 24 hours.") + llm.invoke("Provide me a digest of world news in the last 24 hours.") + ``` !!! note [Citations](https://docs.x.ai/docs/guides/live-search#returning-citations) are only available in [Grok 3](https://docs.x.ai/docs/models/grok-3). Token usage: - .. code-block:: python + ```python + ai_msg = llm.invoke(messages) + ai_msg.usage_metadata + ``` - ai_msg = llm.invoke(messages) - ai_msg.usage_metadata - - .. code-block:: python - - {"input_tokens": 37, "output_tokens": 6, "total_tokens": 43} + ```python + {"input_tokens": 37, "output_tokens": 6, "total_tokens": 43} + ``` Logprobs: - .. code-block:: python + ```python + logprobs_llm = llm.bind(logprobs=True) + messages = [("human", "Say Hello World! Do not return anything else.")] + ai_msg = logprobs_llm.invoke(messages) + ai_msg.response_metadata["logprobs"] + ``` - logprobs_llm = llm.bind(logprobs=True) - messages = [("human", "Say Hello World! Do not return anything else.")] - ai_msg = logprobs_llm.invoke(messages) - ai_msg.response_metadata["logprobs"] - - .. code-block:: python - - { - "content": None, - "token_ids": [22557, 3304, 28808, 2], - "tokens": [" Hello", " World", "!", ""], - "token_logprobs": [-4.7683716e-06, -5.9604645e-07, 0, -0.057373047], - } + ```python + { + "content": None, + "token_ids": [22557, 3304, 28808, 2], + "tokens": [" Hello", " World", "!", ""], + "token_logprobs": [-4.7683716e-06, -5.9604645e-07, 0, -0.057373047], + } + ``` Response metadata - .. code-block:: python - - ai_msg = llm.invoke(messages) - ai_msg.response_metadata - - .. code-block:: python - - { - "token_usage": { - "completion_tokens": 4, - "prompt_tokens": 19, - "total_tokens": 23, - }, - "model_name": "grok-4", - "system_fingerprint": None, - "finish_reason": "stop", - "logprobs": None, - } + ```python + ai_msg = llm.invoke(messages) + ai_msg.response_metadata + ``` + ```python + { + "token_usage": { + "completion_tokens": 4, + "prompt_tokens": 19, + "total_tokens": 23, + }, + "model_name": "grok-4", + "system_fingerprint": None, + "finish_reason": "stop", + "logprobs": None, + } + ``` """ # noqa: E501 model_name: str = Field(default="grok-4", alias="model") diff --git a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py index 7497653f7af..e20085e1306 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py @@ -147,29 +147,28 @@ class ChatModelIntegrationTests(ChatModelTests): `chat_model_params` properties to specify what model to test and its initialization parameters. - Example: - .. code-block:: python + ```python + from typing import Type - from typing import Type - - from langchain_tests.integration_tests import ChatModelIntegrationTests - from my_package.chat_models import MyChatModel + from langchain_tests.integration_tests import ChatModelIntegrationTests + from my_package.chat_models import MyChatModel - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def chat_model_class(self) -> Type[MyChatModel]: - # Return the chat model class to test here - return MyChatModel + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def chat_model_class(self) -> Type[MyChatModel]: + # Return the chat model class to test here + return MyChatModel - @property - def chat_model_params(self) -> dict: - # Return initialization parameters for the model. - return {"model": "model-001", "temperature": 0} + @property + def chat_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001", "temperature": 0} + ``` !!! note - API references for individual test methods include troubleshooting tips. + API references for individual test methods include troubleshooting tips. Test subclasses **must** implement the following two properties: @@ -177,24 +176,20 @@ class ChatModelIntegrationTests(ChatModelTests): chat_model_class The chat model class to test, e.g., `ChatParrotLink`. - Example: - - .. code-block:: python - - @property - def chat_model_class(self) -> Type[ChatParrotLink]: - return ChatParrotLink + ```python + @property + def chat_model_class(self) -> Type[ChatParrotLink]: + return ChatParrotLink + ``` chat_model_params Initialization parameters for the chat model. - Example: - - .. code-block:: python - - @property - def chat_model_params(self) -> dict: - return {"model": "bird-brain-001", "temperature": 0} + ```python + @property + def chat_model_params(self) -> dict: + return {"model": "bird-brain-001", "temperature": 0} + ``` In addition, test subclasses can control what features are tested (such as tool calling or multi-modality) by selectively overriding the following properties. @@ -209,11 +204,11 @@ class ChatModelIntegrationTests(ChatModelTests): Example override: - .. code-block:: python - - @property - def has_tool_calling(self) -> bool: - return True + ```python + @property + def has_tool_calling(self) -> bool: + return True + ``` ??? note "`tool_choice_value`" @@ -227,13 +222,11 @@ class ChatModelIntegrationTests(ChatModelTests): support forcing tool calling, override the `has_tool_choice` property to return `False`. - Example: - - .. code-block:: python - - @property - def tool_choice_value(self) -> str | None: - return "any" + ```python + @property + def tool_choice_value(self) -> str | None: + return "any" + ``` ??? note "`has_tool_choice`" @@ -249,11 +242,11 @@ class ChatModelIntegrationTests(ChatModelTests): Example override: - .. code-block:: python - - @property - def has_tool_choice(self) -> bool: - return False + ```python + @property + def has_tool_choice(self) -> bool: + return False + ``` ??? note "`has_structured_output`" @@ -266,26 +259,22 @@ class ChatModelIntegrationTests(ChatModelTests): See: https://python.langchain.com/docs/concepts/structured_outputs/ - Example: - - .. code-block:: python - - @property - def has_structured_output(self) -> bool: - return True + ```python + @property + def has_structured_output(self) -> bool: + return True + ``` ??? note "`structured_output_kwargs`" Dict property that can be used to specify additional kwargs for `with_structured_output`. Useful for testing different models. - Example: - - .. code-block:: python - - @property - def structured_output_kwargs(self) -> dict: - return {"method": "function_calling"} + ```python + @property + def structured_output_kwargs(self) -> dict: + return {"method": "function_calling"} + ``` ??? note "`supports_json_mode`" @@ -294,13 +283,11 @@ class ChatModelIntegrationTests(ChatModelTests): See: https://python.langchain.com/docs/concepts/structured_outputs/#json-mode - Example: - - .. code-block:: python - - @property - def supports_json_mode(self) -> bool: - return True + ```python + @property + def supports_json_mode(self) -> bool: + return True + ``` ??? note "`supports_image_inputs`" @@ -310,32 +297,30 @@ class ChatModelIntegrationTests(ChatModelTests): If set to `True`, the chat model will be tested by inputting an `ImageContentBlock` with the shape: - .. code-block:: python - - { - "type": "image", - "base64": "", - "mime_type": "image/jpeg", # or appropriate mime-type - } + ```python + { + "type": "image", + "base64": "", + "mime_type": "image/jpeg", # or appropriate mime-type + } + ``` In addition to OpenAI-style content blocks: - .. code-block:: python - - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - } + ```python + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - - .. code-block:: python - - @property - def supports_image_inputs(self) -> bool: - return True + ```python + @property + def supports_image_inputs(self) -> bool: + return True + ``` ??? note "`supports_image_urls`" @@ -345,22 +330,20 @@ class ChatModelIntegrationTests(ChatModelTests): If set to `True`, the chat model will be tested using content blocks of the form - .. code-block:: python - - { - "type": "image", - "url": "https://...", - } + ```python + { + "type": "image", + "url": "https://...", + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - - .. code-block:: python - - @property - def supports_image_urls(self) -> bool: - return True + ```python + @property + def supports_image_urls(self) -> bool: + return True + ``` ??? note "`supports_pdf_inputs`" @@ -370,23 +353,21 @@ class ChatModelIntegrationTests(ChatModelTests): If set to `True`, the chat model will be tested by inputting a `FileContentBlock` with the shape: - .. code-block:: python - - { - "type": "file", - "base64": "", - "mime_type": "application/pdf", - } + ```python + { + "type": "file", + "base64": "", + "mime_type": "application/pdf", + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - - .. code-block:: python - - @property - def supports_pdf_inputs(self) -> bool: - return True + ```python + @property + def supports_pdf_inputs(self) -> bool: + return True + ``` ??? note "`supports_audio_inputs`" @@ -396,23 +377,21 @@ class ChatModelIntegrationTests(ChatModelTests): If set to `True`, the chat model will be tested by inputting an `AudioContentBlock` with the shape: - .. code-block:: python - - { - "type": "audio", - "base64": "", - "mime_type": "audio/wav", # or appropriate mime-type - } + ```python + { + "type": "audio", + "base64": "", + "mime_type": "audio/wav", # or appropriate mime-type + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - - .. code-block:: python - - @property - def supports_audio_inputs(self) -> bool: - return True + ```python + @property + def supports_audio_inputs(self) -> bool: + return True + ``` ??? note "`supports_video_inputs`" @@ -428,13 +407,11 @@ class ChatModelIntegrationTests(ChatModelTests): and output tokens. [See more](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html). - Example: - - .. code-block:: python - - @property - def returns_usage_metadata(self) -> bool: - return False + ```python + @property + def returns_usage_metadata(self) -> bool: + return False + ``` Models supporting `usage_metadata` should also return the name of the underlying model in the `response_metadata` of the `AIMessage`. @@ -446,104 +423,97 @@ class ChatModelIntegrationTests(ChatModelTests): These inputs might feature "tool use" and "tool result" content blocks, e.g., - .. code-block:: python - - [ - {"type": "text", "text": "Hmm let me think about that"}, - { - "type": "tool_use", - "input": {"fav_color": "green"}, - "id": "foo", - "name": "color_picker", - }, - ] + ```python + [ + {"type": "text", "text": "Hmm let me think about that"}, + { + "type": "tool_use", + "input": {"fav_color": "green"}, + "id": "foo", + "name": "color_picker", + }, + ] + ``` If set to `True`, the chat model will be tested using content blocks of this form. - Example: - - .. code-block:: python - - @property - def supports_anthropic_inputs(self) -> bool: - return False + ```python + @property + def supports_anthropic_inputs(self) -> bool: + return False + ``` ??? note "`supports_image_tool_message`" Boolean property indicating whether the chat model supports a `ToolMessage` that includes image content, e.g. in the OpenAI Chat Completions format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ], - tool_call_id="1", - name="random_image", - ) + ```python + ToolMessage( + content=[ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` as well as the LangChain `ImageContentBlock` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image", - "base64": image_data, - "mime_type": "image/jpeg", - }, - ], - tool_call_id="1", - name="random_image", - ) - + ```python + ToolMessage( + content=[ + { + "type": "image", + "base64": image_data, + "mime_type": "image/jpeg", + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` If set to `True`, the chat model will be tested with message sequences that include `ToolMessage` objects of this form. - Example: - - .. code-block:: python - - @property - def supports_image_tool_message(self) -> bool: - return False + ```python + @property + def supports_image_tool_message(self) -> bool: + return False + ``` ??? note "`supports_pdf_tool_message`" Boolean property indicating whether the chat model supports a `ToolMessage that include PDF content using the LangChain `FileContentBlock` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "file", - "base64": pdf_data, - "mime_type": "application/pdf", - }, - ], - tool_call_id="1", - name="random_pdf", - ) + ```python + ToolMessage( + content=[ + { + "type": "file", + "base64": pdf_data, + "mime_type": "application/pdf", + }, + ], + tool_call_id="1", + name="random_pdf", + ) + ``` If set to `True`, the chat model will be tested with message sequences that include `ToolMessage` objects of this form. - Example: - - .. code-block:: python - - @property - def supports_pdf_tool_message(self) -> bool: - return False + ```python + @property + def supports_pdf_tool_message(self) -> bool: + return False + ``` ??? note "`supported_usage_metadata_details`" @@ -570,14 +540,14 @@ class ChatModelIntegrationTests(ChatModelTests): 1. Override the `enable_vcr_tests` property to return `True`: - .. code-block:: python - - @property - def enable_vcr_tests(self) -> bool: - return True + ```python + @property + def enable_vcr_tests(self) -> bool: + return True + ``` 2. Configure VCR to exclude sensitive headers and other information from - cassettes. + cassettes. !!! warning VCR will by default record authentication headers and other sensitive @@ -593,13 +563,55 @@ class ChatModelIntegrationTests(ChatModelTests): also exclude additional headers, override the default exclusions, or apply other customizations to the VCR configuration. See example below: - .. code-block:: python + ```python + :caption: tests/conftest.py + + import pytest + from langchain_tests.conftest import ( + _base_vcr_config as _base_vcr_config, + ) + + _EXTRA_HEADERS = [ + # Specify additional headers to redact + ("user-agent", "PLACEHOLDER"), + ] + + + def remove_response_headers(response: dict) -> dict: + # If desired, remove or modify headers in the response. + response["headers"] = {} + return response + + + @pytest.fixture(scope="session") + def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 + """Extend the default configuration from langchain_tests.""" + config = _base_vcr_config.copy() + config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) + config["before_record_response"] = remove_response_headers + + return config + ``` + + ??? note "Compressing cassettes" + + `langchain-tests` includes a custom VCR serializer that compresses + cassettes using gzip. To use it, register the `yaml.gz` serializer + to your VCR fixture and enable this serializer in the config. See + example below: + + ```python :caption: tests/conftest.py import pytest + from langchain_tests.conftest import ( + CustomPersister, + CustomSerializer, + ) from langchain_tests.conftest import ( _base_vcr_config as _base_vcr_config, ) + from vcr import VCR _EXTRA_HEADERS = [ # Specify additional headers to redact @@ -619,87 +631,46 @@ class ChatModelIntegrationTests(ChatModelTests): config = _base_vcr_config.copy() config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) config["before_record_response"] = remove_response_headers + # New: enable serializer and set file extension + config["serializer"] = "yaml.gz" + config["path_transformer"] = VCR.ensure_suffix(".yaml.gz") return config - ??? note "Compressing cassettes" - `langchain-tests` includes a custom VCR serializer that compresses - cassettes using gzip. To use it, register the `yaml.gz` serializer - to your VCR fixture and enable this serializer in the config. See - example below: - - .. code-block:: python - :caption: tests/conftest.py - - import pytest - from langchain_tests.conftest import ( - CustomPersister, - CustomSerializer, - ) - from langchain_tests.conftest import ( - _base_vcr_config as _base_vcr_config, - ) - from vcr import VCR - - _EXTRA_HEADERS = [ - # Specify additional headers to redact - ("user-agent", "PLACEHOLDER"), - ] - - - def remove_response_headers(response: dict) -> dict: - # If desired, remove or modify headers in the response. - response["headers"] = {} - return response - - - @pytest.fixture(scope="session") - def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 - """Extend the default configuration from langchain_tests.""" - config = _base_vcr_config.copy() - config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) - config["before_record_response"] = remove_response_headers - # New: enable serializer and set file extension - config["serializer"] = "yaml.gz" - config["path_transformer"] = VCR.ensure_suffix(".yaml.gz") - - return config - - - def pytest_recording_configure(config: dict, vcr: VCR) -> None: - vcr.register_persister(CustomPersister()) - vcr.register_serializer("yaml.gz", CustomSerializer()) + def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) + ``` You can inspect the contents of the compressed cassettes (e.g., to ensure no sensitive information is recorded) using - .. code-block:: bash - - gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + ```bash + gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + ``` or by using the serializer: - .. code-block:: python + ```python + from langchain_tests.conftest import ( + CustomPersister, + CustomSerializer, + ) - from langchain_tests.conftest import ( - CustomPersister, - CustomSerializer, - ) - - cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" - requests, responses = CustomPersister().load_cassette( - path, CustomSerializer() - ) + cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" + requests, responses = CustomPersister().load_cassette( + path, CustomSerializer() + ) + ``` 3. Run tests to generate VCR cassettes. Example: - - .. code-block:: bash - - uv run python -m pytest tests/integration_tests/test_chat_models.py::TestMyModel::test_stream_time + ```bash + uv run python -m pytest tests/integration_tests/test_chat_models.py::TestMyModel::test_stream_time + ``` This will generate a VCR cassette for the test in `tests/integration_tests/cassettes/`. @@ -731,13 +702,11 @@ class ChatModelIntegrationTests(ChatModelTests): does not raise any exceptions, and that it returns a valid `langchain_core.outputs.chat_result.ChatResult` like so: - .. code-block:: python - - return ChatResult( - generations=[ - ChatGeneration(message=AIMessage(content="Output text")) - ] - ) + ```python + return ChatResult( + generations=[ChatGeneration(message=AIMessage(content="Output text"))] + ) + ``` """ result = model.invoke("Hello") @@ -764,14 +733,11 @@ class ChatModelIntegrationTests(ChatModelTests): method does not raise any exceptions, and that it returns a valid `langchain_core.outputs.chat_result.ChatResult` like so: - .. code-block:: python - - return ChatResult( - generations=[ - ChatGeneration(message=AIMessage(content="Output text")) - ] - ) - + ```python + return ChatResult( + generations=[ChatGeneration(message=AIMessage(content="Output text"))] + ) + ``` """ result = await model.ainvoke("Hello") assert result is not None @@ -799,10 +765,9 @@ class ChatModelIntegrationTests(ChatModelTests): `langchain_core.outputs.chat_generation.ChatGenerationChunk` objects like so: - .. code-block:: python - - yield ChatGenerationChunk(message=AIMessageChunk(content="chunk text")) - + ```python + yield ChatGenerationChunk(message=AIMessageChunk(content="chunk text")) + ``` """ num_chunks = 0 full: AIMessageChunk | None = None @@ -841,10 +806,9 @@ class ChatModelIntegrationTests(ChatModelTests): `langchain_core.outputs.chat_generation.ChatGenerationChunk` objects like so: - .. code-block:: python - - yield ChatGenerationChunk(message=AIMessageChunk(content="chunk text")) - + ```python + yield ChatGenerationChunk(message=AIMessageChunk(content="chunk text")) + ``` """ num_chunks = 0 full: AIMessageChunk | None = None @@ -1005,40 +969,39 @@ class ChatModelIntegrationTests(ChatModelTests): To disable this feature, set `returns_usage_metadata` to `False` in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def returns_usage_metadata(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def returns_usage_metadata(self) -> bool: + return False + ``` This test can also check the format of specific kinds of usage metadata based on the `supported_usage_metadata_details` property. This property should be configured as follows with the types of tokens that the model supports tracking: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supported_usage_metadata_details(self) -> dict: - return { - "invoke": [ - "audio_input", - "audio_output", - "reasoning_output", - "cache_read_input", - "cache_creation_input", - ], - "stream": [ - "audio_input", - "audio_output", - "reasoning_output", - "cache_read_input", - "cache_creation_input", - ], - } - + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supported_usage_metadata_details(self) -> dict: + return { + "invoke": [ + "audio_input", + "audio_output", + "reasoning_output", + "cache_read_input", + "cache_creation_input", + ], + "stream": [ + "audio_input", + "audio_output", + "reasoning_output", + "cache_read_input", + "cache_creation_input", + ], + } + ``` ??? note "Troubleshooting" @@ -1046,31 +1009,31 @@ class ChatModelIntegrationTests(ChatModelTests): `langchain_core.messages.ai.UsageMetadata` dicts attached to the returned AIMessage object in `_generate`: - .. code-block:: python - - return ChatResult( - generations=[ - ChatGeneration( - message=AIMessage( - content="Output text", - usage_metadata={ - "input_tokens": 350, - "output_tokens": 240, - "total_tokens": 590, - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, + ```python + return ChatResult( + generations=[ + ChatGeneration( + message=AIMessage( + content="Output text", + usage_metadata={ + "input_tokens": 350, + "output_tokens": 240, + "total_tokens": 590, + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, }, - ) + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + }, ) - ] - ) + ) + ] + ) + ``` Check also that the response includes a `model_name` key in its `usage_metadata`. @@ -1178,39 +1141,39 @@ class ChatModelIntegrationTests(ChatModelTests): To disable this feature, set `returns_usage_metadata` to `False` in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def returns_usage_metadata(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def returns_usage_metadata(self) -> bool: + return False + ``` This test can also check the format of specific kinds of usage metadata based on the `supported_usage_metadata_details` property. This property should be configured as follows with the types of tokens that the model supports tracking: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supported_usage_metadata_details(self) -> dict: - return { - "invoke": [ - "audio_input", - "audio_output", - "reasoning_output", - "cache_read_input", - "cache_creation_input", - ], - "stream": [ - "audio_input", - "audio_output", - "reasoning_output", - "cache_read_input", - "cache_creation_input", - ], - } + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supported_usage_metadata_details(self) -> dict: + return { + "invoke": [ + "audio_input", + "audio_output", + "reasoning_output", + "cache_read_input", + "cache_creation_input", + ], + "stream": [ + "audio_input", + "audio_output", + "reasoning_output", + "cache_read_input", + "cache_creation_input", + ], + } + ``` ??? note "Troubleshooting" @@ -1227,35 +1190,35 @@ class ChatModelIntegrationTests(ChatModelTests): the sum. This test will pass as long as the sum of `output_tokens` across all chunks is not `0`. - .. code-block:: python - - yield ChatResult( - generations=[ - ChatGeneration( - message=AIMessage( - content="Output text", - usage_metadata={ - "input_tokens": ( - num_input_tokens if is_first_chunk else 0 - ), - "output_tokens": 11, - "total_tokens": ( - 11 + num_input_tokens if is_first_chunk else 11 - ), - "input_token_details": { - "audio": 10, - "cache_creation": 200, - "cache_read": 100, - }, - "output_token_details": { - "audio": 10, - "reasoning": 200, - }, + ```python + yield ChatResult( + generations=[ + ChatGeneration( + message=AIMessage( + content="Output text", + usage_metadata={ + "input_tokens": ( + num_input_tokens if is_first_chunk else 0 + ), + "output_tokens": 11, + "total_tokens": ( + 11 + num_input_tokens if is_first_chunk else 11 + ), + "input_token_details": { + "audio": 10, + "cache_creation": 200, + "cache_read": 100, }, - ) + "output_token_details": { + "audio": 10, + "reasoning": 200, + }, + }, ) - ] - ) + ) + ] + ) + ``` Check also that the aggregated response includes a `model_name` key in its `usage_metadata`. @@ -1339,16 +1302,16 @@ class ChatModelIntegrationTests(ChatModelTests): If this test fails, check that the function signature for `_generate` (as well as `_stream` and async variants) accepts the `stop` parameter: - .. code-block:: python - - def _generate( - self, - messages: List[BaseMessage], - stop: list[str] | None = None, - run_manager: CallbackManagerForLLMRun | None = None, - **kwargs: Any, - ) -> ChatResult: + ```python + def _generate( + self, + messages: List[BaseMessage], + stop: list[str] | None = None, + run_manager: CallbackManagerForLLMRun | None = None, + **kwargs: Any, + ) -> ChatResult: + ``` """ result = model.invoke("hi", stop=["you"]) assert isinstance(result, AIMessage) @@ -1376,12 +1339,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1394,11 +1357,11 @@ class ChatModelIntegrationTests(ChatModelTests): `tool_choice` is not supported and the model consistently fails this test, you can `xfail` the test: - .. code-block:: python - - @pytest.mark.xfail(reason=("Does not support tool_choice.")) - def test_tool_calling(self, model: BaseChatModel) -> None: - super().test_tool_calling(model) + ```python + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + def test_tool_calling(self, model: BaseChatModel) -> None: + super().test_tool_calling(model) + ``` Otherwise, in the case that only one tool is bound, ensure that `tool_choice` supports the string `'any'` to force calling that tool. @@ -1438,12 +1401,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1456,11 +1419,11 @@ class ChatModelIntegrationTests(ChatModelTests): `tool_choice` is not supported and the model consistently fails this test, you can `xfail` the test: - .. code-block:: python - - @pytest.mark.xfail(reason=("Does not support tool_choice.")) - async def test_tool_calling_async(self, model: BaseChatModel) -> None: - await super().test_tool_calling_async(model) + ```python + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + async def test_tool_calling_async(self, model: BaseChatModel) -> None: + await super().test_tool_calling_async(model) + ``` Otherwise, in the case that only one tool is bound, ensure that `tool_choice` supports the string `'any'` to force calling that tool. @@ -1501,12 +1464,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1519,11 +1482,11 @@ class ChatModelIntegrationTests(ChatModelTests): `tool_choice` is not supported and the model consistently fails this test, you can `xfail` the test: - .. code-block:: python - - @pytest.mark.xfail(reason=("Does not support tool_choice.")) - def test_bind_runnables_as_tools(self, model: BaseChatModel) -> None: - super().test_bind_runnables_as_tools(model) + ```python + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + def test_bind_runnables_as_tools(self, model: BaseChatModel) -> None: + super().test_bind_runnables_as_tools(model) + ``` Otherwise, ensure that the `tool_choice_value` property is correctly specified on the test class. @@ -1571,35 +1534,32 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" If this test fails, check that: 1. The model can correctly handle message histories that include - `AIMessage` objects with `""` content. + `AIMessage` objects with `""` content. 2. The `tool_calls` attribute on `AIMessage` objects is correctly - handled and passed to the model in an appropriate format. + handled and passed to the model in an appropriate format. 3. The model can correctly handle `ToolMessage` objects with string - content and arbitrary string values for `tool_call_id`. + content and arbitrary string values for `tool_call_id`. You can `xfail` the test if tool calling is implemented but this format is not supported. - .. code-block:: python - - @pytest.mark.xfail(reason=("Not implemented.")) - def test_tool_message_histories_string_content( - self, *args: Any - ) -> None: - super().test_tool_message_histories_string_content(*args) - + ```python + @pytest.mark.xfail(reason=("Not implemented.")) + def test_tool_message_histories_string_content(self, *args: Any) -> None: + super().test_tool_message_histories_string_content(*args) + ``` """ if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1643,17 +1603,17 @@ class ChatModelIntegrationTests(ChatModelTests): These message histories will include `AIMessage` objects with "tool use" and content blocks, e.g., - .. code-block:: python - - [ - {"type": "text", "text": "Hmm let me think about that"}, - { - "type": "tool_use", - "input": {"fav_color": "green"}, - "id": "foo", - "name": "color_picker", - }, - ] + ```python + [ + {"type": "text", "text": "Hmm let me think about that"}, + { + "type": "tool_use", + "input": {"fav_color": "green"}, + "id": "foo", + "name": "color_picker", + }, + ] + ``` This test should be skipped if the model does not support tool calling (see Configuration below). @@ -1663,33 +1623,32 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" If this test fails, check that: 1. The model can correctly handle message histories that include - `AIMessage` objects with list content. + `AIMessage` objects with list content. 2. The `tool_calls` attribute on `AIMessage` objects is correctly - handled and passed to the model in an appropriate format. + handled and passed to the model in an appropriate format. 3. The model can correctly handle ToolMessage objects with string content - and arbitrary string values for `tool_call_id`. + and arbitrary string values for `tool_call_id`. You can `xfail` the test if tool calling is implemented but this format is not supported. - .. code-block:: python - - @pytest.mark.xfail(reason=("Not implemented.")) - def test_tool_message_histories_list_content(self, *args: Any) -> None: - super().test_tool_message_histories_list_content(*args) - + ```python + @pytest.mark.xfail(reason=("Not implemented.")) + def test_tool_message_histories_list_content(self, *args: Any) -> None: + super().test_tool_message_histories_list_content(*args) + ``` """ if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1744,12 +1703,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_choice` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_choice(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_choice(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1796,12 +1755,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1815,13 +1774,11 @@ class ChatModelIntegrationTests(ChatModelTests): fail if a provider does not support this form of tool. In these cases, you can `xfail` the test: - .. code-block:: python - - @pytest.mark.xfail(reason=("Does not support tool_choice.")) - def test_tool_calling_with_no_arguments( - self, model: BaseChatModel - ) -> None: - super().test_tool_calling_with_no_arguments(model) + ```python + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + def test_tool_calling_with_no_arguments(self, model: BaseChatModel) -> None: + super().test_tool_calling_with_no_arguments(model) + ``` Otherwise, in the case that only one tool is bound, ensure that `tool_choice` supports the string `'any'` to force calling that tool. @@ -1851,14 +1808,14 @@ class ChatModelIntegrationTests(ChatModelTests): These messages may take the form: - .. code-block:: python - - ToolMessage( - "Error: Missing required argument 'b'.", - name="my_adder_tool", - tool_call_id="abc123", - status="error", - ) + ```python + ToolMessage( + "Error: Missing required argument 'b'.", + name="my_adder_tool", + tool_call_id="abc123", + status="error", + ) + ``` If possible, the `status` field should be parsed and passed appropriately to the model. @@ -1871,12 +1828,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1932,12 +1889,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -1951,12 +1908,11 @@ class ChatModelIntegrationTests(ChatModelTests): You can `xfail` the test if tool calling is implemented but this format is not supported. - .. code-block:: python - - @pytest.mark.xfail(reason=("Not implemented.")) - def test_structured_few_shot_examples(self, *args: Any) -> None: - super().test_structured_few_shot_examples(*args) - + ```python + @pytest.mark.xfail(reason=("Not implemented.")) + def test_structured_few_shot_examples(self, *args: Any) -> None: + super().test_structured_few_shot_examples(*args) + ``` """ if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1994,12 +1950,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable structured output tests, set `has_structured_output` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_structured_output(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_structured_output(self) -> bool: + return False + ``` By default, `has_structured_output` is True if a model overrides the `with_structured_output` or `bind_tools` methods. @@ -2077,12 +2033,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable structured output tests, set `has_structured_output` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_structured_output(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_structured_output(self) -> bool: + return False + ``` By default, `has_structured_output` is True if a model overrides the `with_structured_output` or `bind_tools` methods. @@ -2159,12 +2115,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable structured output tests, set `has_structured_output` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_structured_output(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_structured_output(self) -> bool: + return False + ``` By default, `has_structured_output` is True if a model overrides the `with_structured_output` or `bind_tools` methods. @@ -2225,12 +2181,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable structured output tests, set `has_structured_output` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_structured_output(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_structured_output(self) -> bool: + return False + ``` By default, `has_structured_output` is True if a model overrides the `with_structured_output` or `bind_tools` methods. @@ -2297,12 +2253,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable this test, set `supports_json_mode` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_json_mode(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_json_mode(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -2353,40 +2309,40 @@ class ChatModelIntegrationTests(ChatModelTests): support PDF inputs. These will take the shape of the LangChain `FileContentBlock`: - .. code-block:: python - - { - "type": "image", - "base64": "", - "mime_type": "application/pdf", - } + ```python + { + "type": "image", + "base64": "", + "mime_type": "application/pdf", + } + ``` Furthermore, for backward-compatibility, we must also support OpenAI chat completions file content blocks: - .. code-block:: python - - ( - { - "type": "file", - "file": { - "filename": "test_file.pdf", - "file_data": f"data:application/pdf;base64,{pdf_data}", - }, + ```python + ( + { + "type": "file", + "file": { + "filename": "test_file.pdf", + "file_data": f"data:application/pdf;base64,{pdf_data}", }, - ) + }, + ) + ``` ??? note "Configuration" To disable this test, set `supports_pdf_inputs` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_pdf_inputs(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_pdf_inputs(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -2441,38 +2397,38 @@ class ChatModelIntegrationTests(ChatModelTests): support audio inputs. These will take the shape of the LangChain `AudioContentBlock`: - .. code-block:: python - - { - "type": "audio", - "base64": "", - "mime_type": "audio/wav", # or appropriate mime-type - } + ```python + { + "type": "audio", + "base64": "", + "mime_type": "audio/wav", # or appropriate mime-type + } + ``` Furthermore, for backward-compatibility, we must also support OpenAI chat completions audio content blocks: - .. code-block:: python - - { - "type": "input_audio", - "input_audio": { - "data": "", - "format": "wav", # or appropriate format - }, - } + ```python + { + "type": "input_audio", + "input_audio": { + "data": "", + "format": "wav", # or appropriate format + }, + } + ``` ??? note "Configuration" To disable this test, set `supports_audio_inputs` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_audio_inputs(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_audio_inputs(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -2524,55 +2480,55 @@ class ChatModelIntegrationTests(ChatModelTests): support image inputs. These will take the shape of the LangChain `ImageContentBlock`: - .. code-block:: python - - { - "type": "image", - "base64": "", - "mime_type": "image/jpeg", # or appropriate mime-type - } + ```python + { + "type": "image", + "base64": "", + "mime_type": "image/jpeg", # or appropriate mime-type + } + ``` For backward-compatibility, we must also support OpenAI chat completions image content blocks containing base64-encoded images: - .. code-block:: python - - [ - {"type": "text", "text": "describe the weather in this image"}, - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ] + ```python + [ + {"type": "text", "text": "describe the weather in this image"}, + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ] + ``` See https://python.langchain.com/docs/concepts/multimodality/ If the property `supports_image_urls` is set to True, the test will also check that we can process content blocks of the form: - .. code-block:: python - - { - "type": "image", - "url": "", - } + ```python + { + "type": "image", + "url": "", + } + ``` ??? note "Configuration" To disable this test, set `supports_image_inputs` to False in your test class: - .. code-block:: python + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_image_inputs(self) -> bool: + return False - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_image_inputs(self) -> bool: - return False - - # Can also explicitly disable testing image URLs: - @property - def supports_image_urls(self) -> bool: - return False + # Can also explicitly disable testing image URLs: + @property + def supports_image_urls(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -2631,35 +2587,35 @@ class ChatModelIntegrationTests(ChatModelTests): This test should be skipped if the model does not support messages of the Chat Completions `image_url` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ], - tool_call_id="1", - name="random_image", - ) + ```python + ToolMessage( + content=[ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` In addition, models should support the standard LangChain `ImageContentBlock` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image", - "base64": image_data, - "mime_type": "image/jpeg", - }, - ], - tool_call_id="1", - name="random_image", - ) + ```python + ToolMessage( + content=[ + { + "type": "image", + "base64": image_data, + "mime_type": "image/jpeg", + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` This test can be skipped by setting the `supports_image_tool_message` property to False (see Configuration below). @@ -2669,12 +2625,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable this test, set `supports_image_tool_message` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_image_tool_message(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_image_tool_message(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -2746,36 +2702,36 @@ class ChatModelIntegrationTests(ChatModelTests): This test should be skipped if the model does not support messages of the LangChain `FileContentBlock` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "file", - "base64": pdf_data, - "mime_type": "application/pdf", - }, - ], - tool_call_id="1", - name="random_pdf", - ) + ```python + ToolMessage( + content=[ + { + "type": "file", + "base64": pdf_data, + "mime_type": "application/pdf", + }, + ], + tool_call_id="1", + name="random_pdf", + ) + ``` This test can be skipped by setting the `supports_pdf_tool_message` property to False (see Configuration below). - .. dropdown:: Configuration + ??? note "Configuration" To disable this test, set `supports_pdf_tool_message` to False in your test class: - .. code-block:: python + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_pdf_tool_message(self) -> bool: + return False + ``` - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_pdf_tool_message(self) -> bool: - return False - - .. dropdown:: Troubleshooting + ??? note "Troubleshooting" If this test fails, check that the model can correctly handle messages with PDF content blocks in `ToolMessage` objects, specifically @@ -2830,41 +2786,41 @@ class ChatModelIntegrationTests(ChatModelTests): These message histories will include `AIMessage` objects with `tool_use` content blocks, e.g., - .. code-block:: python - - AIMessage( - [ - {"type": "text", "text": "Hmm let me think about that"}, - { - "type": "tool_use", - "input": {"fav_color": "green"}, - "id": "foo", - "name": "color_picker", - }, - ] - ) + ```python + AIMessage( + [ + {"type": "text", "text": "Hmm let me think about that"}, + { + "type": "tool_use", + "input": {"fav_color": "green"}, + "id": "foo", + "name": "color_picker", + }, + ] + ) + ``` as well as `HumanMessage` objects containing `tool_result` content blocks: - .. code-block:: python - - HumanMessage( - [ - { - "type": "tool_result", - "tool_use_id": "foo", - "content": [ - { - "type": "text", - "text": "green is a great pick! " - "that's my sister's favorite color", - } - ], - "is_error": False, - }, - {"type": "text", "text": "what's my sister's favorite color"}, - ] - ) + ```python + HumanMessage( + [ + { + "type": "tool_result", + "tool_use_id": "foo", + "content": [ + { + "type": "text", + "text": "green is a great pick! " + "that's my sister's favorite color", + } + ], + "is_error": False, + }, + {"type": "text", "text": "what's my sister's favorite color"}, + ] + ) + ``` This test should be skipped if the model does not support messages of this form (or doesn't support tool calling generally). See Configuration below. @@ -2874,23 +2830,23 @@ class ChatModelIntegrationTests(ChatModelTests): To disable this test, set `supports_anthropic_inputs` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def supports_anthropic_inputs(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def supports_anthropic_inputs(self) -> bool: + return False + ``` ??? note "Troubleshooting" If this test fails, check that: 1. The model can correctly handle message histories that include message - objects with list content. + objects with list content. 2. The `tool_calls` attribute on AIMessage objects is correctly handled - and passed to the model in an appropriate format. + and passed to the model in an appropriate format. 3. `HumanMessage`s with "tool_result" content blocks are correctly - handled. + handled. Otherwise, if Anthropic tool call and result formats are not supported, set the `supports_anthropic_inputs` property to False. @@ -3010,9 +2966,9 @@ class ChatModelIntegrationTests(ChatModelTests): These messages may take the form: - .. code-block:: python - - HumanMessage("hello", name="example_user") + ```python + HumanMessage("hello", name="example_user") + ``` If possible, the `name` field should be parsed and passed appropriately to the model. Otherwise, it should be ignored. @@ -3044,12 +3000,12 @@ class ChatModelIntegrationTests(ChatModelTests): To disable tool calling tests, set `has_tool_calling` to False in your test class: - .. code-block:: python - - class TestMyChatModelIntegration(ChatModelIntegrationTests): - @property - def has_tool_calling(self) -> bool: - return False + ```python + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def has_tool_calling(self) -> bool: + return False + ``` ??? note "Troubleshooting" @@ -3064,11 +3020,11 @@ class ChatModelIntegrationTests(ChatModelTests): calls in response to an appropriate query. In these cases you can `xfail` the test: - .. code-block:: python - - @pytest.mark.xfail(reason=("Does not support tool_choice.")) - def test_agent_loop(self, model: BaseChatModel) -> None: - super().test_agent_loop(model) + ```python + @pytest.mark.xfail(reason=("Does not support tool_choice.")) + def test_agent_loop(self, model: BaseChatModel) -> None: + super().test_agent_loop(model) + ``` """ if not self.has_tool_calling: @@ -3114,11 +3070,11 @@ class ChatModelIntegrationTests(ChatModelTests): This test can be enabled or disabled using the `enable_vcr_tests` property. For example, to disable the test, set this property to `False`: - .. code-block:: python - - @property - def enable_vcr_tests(self) -> bool: - return False + ```python + @property + def enable_vcr_tests(self) -> bool: + return False + ``` !!! warning VCR will by default record authentication headers and other sensitive diff --git a/libs/standard-tests/langchain_tests/integration_tests/embeddings.py b/libs/standard-tests/langchain_tests/integration_tests/embeddings.py index b4cbe39826e..9d556a21581 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/embeddings.py +++ b/libs/standard-tests/langchain_tests/integration_tests/embeddings.py @@ -12,26 +12,24 @@ class EmbeddingsIntegrationTests(EmbeddingsTests): embeddings model to be tested. You can also override the `embedding_model_params` property to specify initialization parameters. - Example: + ```python + from typing import Type - .. code-block:: python - - from typing import Type - - from langchain_tests.integration_tests import EmbeddingsIntegrationTests - from my_package.embeddings import MyEmbeddingsModel + from langchain_tests.integration_tests import EmbeddingsIntegrationTests + from my_package.embeddings import MyEmbeddingsModel - class TestMyEmbeddingsModelIntegration(EmbeddingsIntegrationTests): - @property - def embeddings_class(self) -> Type[MyEmbeddingsModel]: - # Return the embeddings model class to test here - return MyEmbeddingsModel + class TestMyEmbeddingsModelIntegration(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[MyEmbeddingsModel]: + # Return the embeddings model class to test here + return MyEmbeddingsModel - @property - def embedding_model_params(self) -> dict: - # Return initialization parameters for the model. - return {"model": "model-001"} + @property + def embedding_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001"} + ``` !!! note API references for individual test methods include troubleshooting tips. diff --git a/libs/standard-tests/langchain_tests/integration_tests/retrievers.py b/libs/standard-tests/langchain_tests/integration_tests/retrievers.py index 7f0470b4c9d..1736045cca0 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/retrievers.py +++ b/libs/standard-tests/langchain_tests/integration_tests/retrievers.py @@ -56,14 +56,14 @@ class RetrieversIntegrationTests(BaseStandardTests): via the constructor, this test can be skipped using a pytest `xfail` on the test class: - .. code-block:: python - - @pytest.mark.xfail( - reason="This retriever doesn't support setting " - "the number of results via the constructor." - ) - def test_k_constructor_param(self) -> None: - raise NotImplementedError + ```python + @pytest.mark.xfail( + reason="This retriever doesn't support setting " + "the number of results via the constructor." + ) + def test_k_constructor_param(self) -> None: + raise NotImplementedError + ``` ??? note "Troubleshooting" @@ -74,9 +74,9 @@ class RetrieversIntegrationTests(BaseStandardTests): For example, a retriever like - .. code-block:: python - - MyRetriever(k=3).invoke("query") + ```python + MyRetriever(k=3).invoke("query") + ``` should return 3 documents when invoked with a query. @@ -112,14 +112,14 @@ class RetrieversIntegrationTests(BaseStandardTests): via the invoke method, this test can be skipped using a pytest `xfail` on the test class: - .. code-block:: python - - @pytest.mark.xfail( - reason="This retriever doesn't support setting " - "the number of results in the invoke method." - ) - def test_invoke_with_k_kwarg(self) -> None: - raise NotImplementedError + ```python + @pytest.mark.xfail( + reason="This retriever doesn't support setting " + "the number of results in the invoke method." + ) + def test_invoke_with_k_kwarg(self) -> None: + raise NotImplementedError + ``` ??? note "Troubleshooting" @@ -130,9 +130,9 @@ class RetrieversIntegrationTests(BaseStandardTests): For example, a retriever like - .. code-block:: python - - MyRetriever().invoke("query", k=3) + ```python + MyRetriever().invoke("query", k=3) + ``` should return 3 documents when invoked with a query. diff --git a/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py b/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py index 21f634a7507..066d1d30a5b 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py +++ b/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py @@ -29,74 +29,72 @@ class VectorStoreIntegrationTests(BaseStandardTests): Here is a template: - .. code-block:: python + ```python + from typing import Generator - from typing import Generator - - import pytest - from langchain_core.vectorstores import VectorStore - from langchain_parrot_link.vectorstores import ParrotVectorStore - from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests + import pytest + from langchain_core.vectorstores import VectorStore + from langchain_parrot_link.vectorstores import ParrotVectorStore + from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests - class TestParrotVectorStore(VectorStoreIntegrationTests): - @pytest.fixture() - def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore - \"\"\"Get an empty vectorstore.\"\"\" - store = ParrotVectorStore(self.get_embeddings()) - # note: store should be EMPTY at this point - # if you need to delete data, you may do so here - try: - yield store - finally: - # cleanup operations, or deleting data - pass + class TestParrotVectorStore(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + \"\"\"Get an empty vectorstore.\"\"\" + store = ParrotVectorStore(self.get_embeddings()) + # note: store should be EMPTY at this point + # if you need to delete data, you may do so here + try: + yield store + finally: + # cleanup operations, or deleting data + pass + ``` In the fixture, before the `yield` we instantiate an empty vector store. In the `finally` block, we call whatever logic is necessary to bring the vector store to a clean state. - Example: + ```python + from typing import Generator - .. code-block:: python + import pytest + from langchain_core.vectorstores import VectorStore + from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests - from typing import Generator - - import pytest - from langchain_core.vectorstores import VectorStore - from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests - - from langchain_chroma import Chroma + from langchain_chroma import Chroma - class TestChromaStandard(VectorStoreIntegrationTests): - @pytest.fixture() - def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore - \"\"\"Get an empty vectorstore for unit tests.\"\"\" - store = Chroma(embedding_function=self.get_embeddings()) - try: - yield store - finally: - store.delete_collection() - pass + class TestChromaStandard(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + \"\"\"Get an empty vectorstore for unit tests.\"\"\" + store = Chroma(embedding_function=self.get_embeddings()) + try: + yield store + finally: + store.delete_collection() + pass + ``` Note that by default we enable both sync and async tests. To disable either, override the `has_sync` or `has_async` properties to `False` in the subclass. For example: - .. code-block:: python + ```python + class TestParrotVectorStore(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + ... - class TestParrotVectorStore(VectorStoreIntegrationTests): - @pytest.fixture() - def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore - ... - - @property - def has_async(self) -> bool: - return False + @property + def has_async(self) -> bool: + return False + ``` !!! note - API references for individual test methods include troubleshooting tips. + API references for individual test methods include troubleshooting tips. """ # noqa: E501 @@ -160,11 +158,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): If this test fails, check that: 1. We correctly initialize an empty vector store in the `vectorestore` - fixture. + fixture. 2. Calling `.similarity_search` for the top `k` similar documents does - not threshold by score. + not threshold by score. 3. We do not mutate the original document object when adding it to the - vector store (e.g., by adding an ID). + vector store (e.g., by adding an ID). """ if not self.has_sync: pytest.skip("Sync tests not supported.") @@ -344,12 +342,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_sync: pytest.skip("Sync tests not supported.") @@ -384,12 +381,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_sync: pytest.skip("Sync tests not supported.") @@ -418,12 +414,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_sync: pytest.skip("Sync tests not supported.") @@ -454,9 +449,9 @@ class VectorStoreIntegrationTests(BaseStandardTests): This test also verifies that: 1. IDs specified in the `Document.id` field are assigned when adding - documents. + documents. 2. If some documents include IDs and others don't string IDs are generated - for the latter. + for the latter. !!! note `get_by_ids` was added to the `VectorStore` interface in @@ -464,12 +459,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_sync: pytest.skip("Sync tests not supported.") @@ -512,11 +506,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): If this test fails, check that: 1. We correctly initialize an empty vector store in the `vectorestore` - fixture. + fixture. 2. Calling `.asimilarity_search` for the top `k` similar documents does - not threshold by score. + not threshold by score. 3. We do not mutate the original document object when adding it to the - vector store (e.g., by adding an ID). + vector store (e.g., by adding an ID). """ if not self.has_async: pytest.skip("Async tests not supported.") @@ -703,12 +697,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_async: pytest.skip("Async tests not supported.") @@ -743,12 +736,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_async: pytest.skip("Async tests not supported.") @@ -778,12 +770,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_async: pytest.skip("Async tests not supported.") @@ -816,9 +807,9 @@ class VectorStoreIntegrationTests(BaseStandardTests): This test also verifies that: 1. IDs specified in the `Document.id` field are assigned when adding - documents. + documents. 2. If some documents include IDs and others don't string IDs are generated - for the latter. + for the latter. !!! note `get_by_ids` was added to the `VectorStore` interface in @@ -826,12 +817,11 @@ class VectorStoreIntegrationTests(BaseStandardTests): test can be skipped by setting the `has_get_by_ids` property to `False`. - .. code-block:: python - - @property - def has_get_by_ids(self) -> bool: - return False - + ```python + @property + def has_get_by_ids(self) -> bool: + return False + ``` """ if not self.has_async: pytest.skip("Async tests not supported.") diff --git a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py index 339f4eab203..1b99be089b6 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py @@ -265,29 +265,27 @@ class ChatModelUnitTests(ChatModelTests): `chat_model_params` properties to specify what model to test and its initialization parameters. - Example: + ```python + from typing import Type - .. code-block:: python - - from typing import Type - - from langchain_tests.unit_tests import ChatModelUnitTests - from my_package.chat_models import MyChatModel + from langchain_tests.unit_tests import ChatModelUnitTests + from my_package.chat_models import MyChatModel - class TestMyChatModelUnit(ChatModelUnitTests): - @property - def chat_model_class(self) -> Type[MyChatModel]: - # Return the chat model class to test here - return MyChatModel + class TestMyChatModelUnit(ChatModelUnitTests): + @property + def chat_model_class(self) -> Type[MyChatModel]: + # Return the chat model class to test here + return MyChatModel - @property - def chat_model_params(self) -> dict: - # Return initialization parameters for the model. - return {"model": "model-001", "temperature": 0} + @property + def chat_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001", "temperature": 0} + ``` !!! note - API references for individual test methods include troubleshooting tips. + API references for individual test methods include troubleshooting tips. Test subclasses **must** implement the following two properties: @@ -295,24 +293,19 @@ class ChatModelUnitTests(ChatModelTests): chat_model_class The chat model class to test, e.g., `ChatParrotLink`. - Example: - - .. code-block:: python - - @property - def chat_model_class(self) -> Type[ChatParrotLink]: - return ChatParrotLink - + ```python + @property + def chat_model_class(self) -> Type[ChatParrotLink]: + return ChatParrotLink + ``` chat_model_params Initialization parameters for the chat model. - Example: - - .. code-block:: python - - @property - def chat_model_params(self) -> dict: - return {"model": "bird-brain-001", "temperature": 0} + ```python + @property + def chat_model_params(self) -> dict: + return {"model": "bird-brain-001", "temperature": 0} + ``` In addition, test subclasses can control what features are tested (such as tool calling or multi-modality) by selectively overriding the following properties. @@ -327,11 +320,11 @@ class ChatModelUnitTests(ChatModelTests): Example override: - .. code-block:: python - - @property - def has_tool_calling(self) -> bool: - return True + ```python + @property + def has_tool_calling(self) -> bool: + return True + ``` ??? note "`tool_choice_value`" @@ -344,13 +337,11 @@ class ChatModelUnitTests(ChatModelTests): return `False`. Otherwise, models should accept values of `'any'` or the name of a tool in `tool_choice`. - Example: - - .. code-block:: python - - @property - def tool_choice_value(self) -> str | None: - return "any" + ```python + @property + def tool_choice_value(self) -> str | None: + return "any" + ``` ??? note "`has_tool_choice`" @@ -366,11 +357,11 @@ class ChatModelUnitTests(ChatModelTests): Example override: - .. code-block:: python - - @property - def has_tool_choice(self) -> bool: - return False + ```python + @property + def has_tool_choice(self) -> bool: + return False + ``` ??? note "`has_structured_output`" @@ -383,26 +374,22 @@ class ChatModelUnitTests(ChatModelTests): See: https://python.langchain.com/docs/concepts/structured_outputs/ - Example: - - .. code-block:: python - - @property - def has_structured_output(self) -> bool: - return True + ```python + @property + def has_structured_output(self) -> bool: + return True + ``` ??? note "`structured_output_kwargs`" Dict property that can be used to specify additional kwargs for `with_structured_output`. Useful for testing different models. - Example: - - .. code-block:: python - - @property - def structured_output_kwargs(self) -> dict: - return {"method": "function_calling"} + ```python + @property + def structured_output_kwargs(self) -> dict: + return {"method": "function_calling"} + ``` ??? note "`supports_json_mode`" @@ -411,13 +398,11 @@ class ChatModelUnitTests(ChatModelTests): See: https://python.langchain.com/docs/concepts/structured_outputs/#json-mode - Example: - - .. code-block:: python - - @property - def supports_json_mode(self) -> bool: - return True + ```python + @property + def supports_json_mode(self) -> bool: + return True + ``` ??? note "`supports_image_inputs`" @@ -427,32 +412,31 @@ class ChatModelUnitTests(ChatModelTests): If set to `True`, the chat model will be tested using the LangChain `ImageContentBlock` format: - .. code-block:: python - - { - "type": "image", - "base64": "", - "mime_type": "image/jpeg", # or appropriate mime-type - } + ```python + { + "type": "image", + "base64": "", + "mime_type": "image/jpeg", # or appropriate mime-type + } + ``` In addition to OpenAI Chat Completions `image_url` blocks: - .. code-block:: python - - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - } + ```python + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - .. code-block:: python - - @property - def supports_image_inputs(self) -> bool: - return True + ```python + @property + def supports_image_inputs(self) -> bool: + return True + ``` ??? note "`supports_image_urls`" @@ -462,22 +446,21 @@ class ChatModelUnitTests(ChatModelTests): If set to `True`, the chat model will be tested using content blocks of the form. - .. code-block:: python - - { - "type": "image", - "url": "https://...", - } + ```python + { + "type": "image", + "url": "https://...", + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - .. code-block:: python - - @property - def supports_image_urls(self) -> bool: - return True + ```python + @property + def supports_image_urls(self) -> bool: + return True + ``` ??? note "`supports_pdf_inputs`" @@ -487,23 +470,22 @@ class ChatModelUnitTests(ChatModelTests): If set to `True`, the chat model will be tested using the LangChain `FileContentBlock` format: - .. code-block:: python - - { - "type": "file", - "base64": "", - "mime_type": "application/pdf", - } + ```python + { + "type": "file", + "base64": "", + "mime_type": "application/pdf", + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - .. code-block:: python - - @property - def supports_pdf_inputs(self) -> bool: - return True + ```python + @property + def supports_pdf_inputs(self) -> bool: + return True + ``` ??? note "`supports_audio_inputs`" @@ -513,23 +495,21 @@ class ChatModelUnitTests(ChatModelTests): If set to `True`, the chat model will be tested using the LangChain `AudioContentBlock` format: - .. code-block:: python - - { - "type": "audio", - "base64": "", - "mime_type": "audio/wav", # or appropriate mime-type - } + ```python + { + "type": "audio", + "base64": "", + "mime_type": "audio/wav", # or appropriate mime-type + } + ``` See https://python.langchain.com/docs/concepts/multimodality/ - Example: - - .. code-block:: python - - @property - def supports_audio_inputs(self) -> bool: - return True + ```python + @property + def supports_audio_inputs(self) -> bool: + return True + ``` ??? note "`supports_video_inputs`" @@ -545,13 +525,11 @@ class ChatModelUnitTests(ChatModelTests): input and output tokens. [See more](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html). - Example: - - .. code-block:: python - - @property - def returns_usage_metadata(self) -> bool: - return False + ```python + @property + def returns_usage_metadata(self) -> bool: + return False + ``` Models supporting `usage_metadata` should also return the name of the underlying model in the `response_metadata` of the `AIMessage`. @@ -563,108 +541,102 @@ class ChatModelUnitTests(ChatModelTests): These inputs might feature "tool use" and "tool result" content blocks, e.g., - .. code-block:: python - - [ - {"type": "text", "text": "Hmm let me think about that"}, - { - "type": "tool_use", - "input": {"fav_color": "green"}, - "id": "foo", - "name": "color_picker", - }, - ] + ```python + [ + {"type": "text", "text": "Hmm let me think about that"}, + { + "type": "tool_use", + "input": {"fav_color": "green"}, + "id": "foo", + "name": "color_picker", + }, + ] + ``` If set to `True`, the chat model will be tested using content blocks of this form. - Example: - - .. code-block:: python - - @property - def supports_anthropic_inputs(self) -> bool: - return False + ```python + @property + def supports_anthropic_inputs(self) -> bool: + return False + ``` ??? note "`supports_image_tool_message`" Boolean property indicating whether the chat model supports `ToolMessage` objects that include image content, e.g., - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, - }, - ], - tool_call_id="1", - name="random_image", - ) + ```python + ToolMessage( + content=[ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` (OpenAI Chat Completions format), as well as LangChain's `ImageContentBlock` format: - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "image", - "base64": image_data, - "mime_type": "image/jpeg", - }, - ], - tool_call_id="1", - name="random_image", - ) + ```python + ToolMessage( + content=[ + { + "type": "image", + "base64": image_data, + "mime_type": "image/jpeg", + }, + ], + tool_call_id="1", + name="random_image", + ) + ``` (standard format). If set to `True`, the chat model will be tested with message sequences that include `ToolMessage` objects of this form. - Example: - - .. code-block:: python - - @property - def supports_image_tool_message(self) -> bool: - return False + ```python + @property + def supports_image_tool_message(self) -> bool: + return False + ``` ??? note "`supports_pdf_tool_message`" Boolean property indicating whether the chat model supports `ToolMessage` objects that include PDF content, i.e., - .. code-block:: python - - ToolMessage( - content=[ - { - "type": "file", - "base64": pdf_data, - "mime_type": "application/pdf", - }, - ], - tool_call_id="1", - name="random_pdf", - ) + ```python + ToolMessage( + content=[ + { + "type": "file", + "base64": pdf_data, + "mime_type": "application/pdf", + }, + ], + tool_call_id="1", + name="random_pdf", + ) + ``` using LangChain's `FileContentBlock` format. If set to `True`, the chat model will be tested with message sequences that include `ToolMessage` objects of this form. - Example: - - .. code-block:: python - - @property - def supports_pdf_tool_message(self) -> bool: - return False + ```python + @property + def supports_pdf_tool_message(self) -> bool: + return False + ``` ??? note "`supported_usage_metadata_details`" @@ -691,14 +663,14 @@ class ChatModelUnitTests(ChatModelTests): 1. Override the `enable_vcr_tests` property to return `True`: - .. code-block:: python - - @property - def enable_vcr_tests(self) -> bool: - return True + ```python + @property + def enable_vcr_tests(self) -> bool: + return True + ``` 2. Configure VCR to exclude sensitive headers and other information from - cassettes. + cassettes. !!! warning VCR will by default record authentication headers and other sensitive @@ -714,13 +686,55 @@ class ChatModelUnitTests(ChatModelTests): also exclude additional headers, override the default exclusions, or apply other customizations to the VCR configuration. See example below: - .. code-block:: python + ```python + :caption: tests/conftest.py + + import pytest + from langchain_tests.conftest import ( + _base_vcr_config as _base_vcr_config, + ) + + _EXTRA_HEADERS = [ + # Specify additional headers to redact + ("user-agent", "PLACEHOLDER"), + ] + + + def remove_response_headers(response: dict) -> dict: + # If desired, remove or modify headers in the response. + response["headers"] = {} + return response + + + @pytest.fixture(scope="session") + def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 + """Extend the default configuration from langchain_tests.""" + config = _base_vcr_config.copy() + config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) + config["before_record_response"] = remove_response_headers + + return config + ``` + + ??? note "Compressing cassettes" + + `langchain-tests` includes a custom VCR serializer that compresses + cassettes using gzip. To use it, register the `yaml.gz` serializer + to your VCR fixture and enable this serializer in the config. See + example below: + + ```python :caption: tests/conftest.py import pytest + from langchain_tests.conftest import ( + CustomPersister, + CustomSerializer, + ) from langchain_tests.conftest import ( _base_vcr_config as _base_vcr_config, ) + from vcr import VCR _EXTRA_HEADERS = [ # Specify additional headers to redact @@ -740,87 +754,45 @@ class ChatModelUnitTests(ChatModelTests): config = _base_vcr_config.copy() config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) config["before_record_response"] = remove_response_headers + # New: enable serializer and set file extension + config["serializer"] = "yaml.gz" + config["path_transformer"] = VCR.ensure_suffix(".yaml.gz") return config - ??? note "Compressing cassettes" - - `langchain-tests` includes a custom VCR serializer that compresses - cassettes using gzip. To use it, register the `yaml.gz` serializer - to your VCR fixture and enable this serializer in the config. See - example below: - - .. code-block:: python - :caption: tests/conftest.py - - import pytest - from langchain_tests.conftest import ( - CustomPersister, - CustomSerializer, - ) - from langchain_tests.conftest import ( - _base_vcr_config as _base_vcr_config, - ) - from vcr import VCR - - _EXTRA_HEADERS = [ - # Specify additional headers to redact - ("user-agent", "PLACEHOLDER"), - ] - - - def remove_response_headers(response: dict) -> dict: - # If desired, remove or modify headers in the response. - response["headers"] = {} - return response - - - @pytest.fixture(scope="session") - def vcr_config(_base_vcr_config: dict) -> dict: # noqa: F811 - """Extend the default configuration from langchain_tests.""" - config = _base_vcr_config.copy() - config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS) - config["before_record_response"] = remove_response_headers - # New: enable serializer and set file extension - config["serializer"] = "yaml.gz" - config["path_transformer"] = VCR.ensure_suffix(".yaml.gz") - - return config - - - def pytest_recording_configure(config: dict, vcr: VCR) -> None: - vcr.register_persister(CustomPersister()) - vcr.register_serializer("yaml.gz", CustomSerializer()) + def pytest_recording_configure(config: dict, vcr: VCR) -> None: + vcr.register_persister(CustomPersister()) + vcr.register_serializer("yaml.gz", CustomSerializer()) + ``` You can inspect the contents of the compressed cassettes (e.g., to ensure no sensitive information is recorded) using - .. code-block:: bash - - gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + ```bash + gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz + ``` or by using the serializer: - .. code-block:: python + ```python + from langchain_tests.conftest import ( + CustomPersister, + CustomSerializer, + ) - from langchain_tests.conftest import ( - CustomPersister, - CustomSerializer, - ) - - cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" - requests, responses = CustomPersister().load_cassette( - path, CustomSerializer() - ) + cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz" + requests, responses = CustomPersister().load_cassette( + path, CustomSerializer() + ) + ``` 3. Run tests to generate VCR cassettes. Example: - - .. code-block:: bash - - uv run python -m pytest tests/integration_tests/test_chat_models.py::TestMyModel::test_stream_time + ```bash + uv run python -m pytest tests/integration_tests/test_chat_models.py::TestMyModel::test_stream_time + ``` This will generate a VCR cassette for the test in `tests/integration_tests/cassettes/`. @@ -849,22 +821,21 @@ class ChatModelUnitTests(ChatModelTests): Defaults to empty dicts. If not overridden, the test is skipped. Example: - - .. code-block:: python - - @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: - return ( - { - "MY_API_KEY": "api_key", - }, - { - "model": "bird-brain-001", - }, - { - "my_api_key": "api_key", - }, - ) + ```python + @property + def init_from_env_params(self) -> Tuple[dict, dict, dict]: + return ( + { + "MY_API_KEY": "api_key", + }, + { + "model": "bird-brain-001", + }, + { + "my_api_key": "api_key", + }, + ) + ``` ''' # noqa: E501,D214 @@ -893,9 +864,9 @@ class ChatModelUnitTests(ChatModelTests): If this test fails, ensure that: 1. `chat_model_params` is specified and the model can be initialized - from those params; + from those params; 2. The model accommodates - [standard parameters](https://python.langchain.com/docs/concepts/chat_models/#standard-parameters). + [standard parameters](https://python.langchain.com/docs/concepts/chat_models/#standard-parameters). """ model = self.chat_model_class( diff --git a/libs/standard-tests/langchain_tests/unit_tests/embeddings.py b/libs/standard-tests/langchain_tests/unit_tests/embeddings.py index 50e95f428c9..c8901ab55b7 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/embeddings.py +++ b/libs/standard-tests/langchain_tests/unit_tests/embeddings.py @@ -37,27 +37,24 @@ class EmbeddingsUnitTests(EmbeddingsTests): embeddings model to be tested. You can also override the `embedding_model_params` property to specify initialization parameters. - Example: + ```python + from typing import Type - .. code-block:: python - - from typing import Type - - from langchain_tests.unit_tests import EmbeddingsUnitTests - from my_package.embeddings import MyEmbeddingsModel + from langchain_tests.unit_tests import EmbeddingsUnitTests + from my_package.embeddings import MyEmbeddingsModel - class TestMyEmbeddingsModelUnit(EmbeddingsUnitTests): - @property - def embeddings_class(self) -> Type[MyEmbeddingsModel]: - # Return the embeddings model class to test here - return MyEmbeddingsModel - - @property - def embedding_model_params(self) -> dict: - # Return initialization parameters for the model. - return {"model": "model-001"} + class TestMyEmbeddingsModelUnit(EmbeddingsUnitTests): + @property + def embeddings_class(self) -> Type[MyEmbeddingsModel]: + # Return the embeddings model class to test here + return MyEmbeddingsModel + @property + def embedding_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001"} + ``` !!! note API references for individual test methods include troubleshooting tips. @@ -74,25 +71,22 @@ class EmbeddingsUnitTests(EmbeddingsTests): Defaults to empty dicts. If not overridden, the test is skipped. - Example: - - .. code-block:: python - - @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: - return ( - { - "MY_API_KEY": "api_key", - }, - { - "model": "model-001", - }, - { - "my_api_key": "api_key", - }, - ) - - """ # noqa: D214 + ```python + @property + def init_from_env_params(self) -> Tuple[dict, dict, dict]: + return ( + { + "MY_API_KEY": "api_key", + }, + { + "model": "model-001", + }, + { + "my_api_key": "api_key", + }, + ) + ``` + """ def test_init(self) -> None: """Test model initialization. diff --git a/libs/standard-tests/tests/unit_tests/custom_chat_model.py b/libs/standard-tests/tests/unit_tests/custom_chat_model.py index 880cc226416..5a7d262c4a5 100644 --- a/libs/standard-tests/tests/unit_tests/custom_chat_model.py +++ b/libs/standard-tests/tests/unit_tests/custom_chat_model.py @@ -24,18 +24,16 @@ class ChatParrotLink(BaseChatModel): links to the underlying models documentation or API. Example: - - .. code-block:: python - - model = ChatParrotLink(parrot_buffer_length=2, model="bird-brain-001") - result = model.invoke([HumanMessage(content="hello")]) - result = model.batch( - [ - [HumanMessage(content="hello")], - [HumanMessage(content="world")], - ] - ) - + ```python + model = ChatParrotLink(parrot_buffer_length=2, model="bird-brain-001") + result = model.invoke([HumanMessage(content="hello")]) + result = model.batch( + [ + [HumanMessage(content="hello")], + [HumanMessage(content="world")], + ] + ) + ``` """ model_name: str = Field(alias="model") diff --git a/libs/text-splitters/langchain_text_splitters/__init__.py b/libs/text-splitters/langchain_text_splitters/__init__.py index d61c0e4792f..828319c852d 100644 --- a/libs/text-splitters/langchain_text_splitters/__init__.py +++ b/libs/text-splitters/langchain_text_splitters/__init__.py @@ -1,24 +1,9 @@ """**Text Splitters** are classes for splitting text. -**Class hierarchy:** - -.. code-block:: - - BaseDocumentTransformer --> TextSplitter --> TextSplitter # Example: CharacterTextSplitter - RecursiveCharacterTextSplitter --> TextSplitter - - !!! note - **MarkdownHeaderTextSplitter** and **HTMLHeaderTextSplitter do not derive from TextSplitter. - - -**Main helpers:** - -.. code-block:: - - Document, Tokenizer, Language, LineType, HeaderType - -""" # noqa: E501 + **MarkdownHeaderTextSplitter** and **HTMLHeaderTextSplitter do not derive from + TextSplitter. +""" from langchain_text_splitters.base import ( Language, diff --git a/libs/text-splitters/langchain_text_splitters/html.py b/libs/text-splitters/langchain_text_splitters/html.py index 44835f4864a..179b110ab9c 100644 --- a/libs/text-splitters/langchain_text_splitters/html.py +++ b/libs/text-splitters/langchain_text_splitters/html.py @@ -99,50 +99,48 @@ class HTMLHeaderTextSplitter: hierarchical representation of the content. Example: + ```python + from langchain_text_splitters.html_header_text_splitter import ( + HTMLHeaderTextSplitter, + ) - .. code-block:: python + # Define headers for splitting on h1 and h2 tags. + headers_to_split_on = [("h1", "Main Topic"), ("h2", "Sub Topic")] - from langchain_text_splitters.html_header_text_splitter import ( - HTMLHeaderTextSplitter, - ) + splitter = HTMLHeaderTextSplitter( + headers_to_split_on=headers_to_split_on, + return_each_element=False + ) - # Define headers for splitting on h1 and h2 tags. - headers_to_split_on = [("h1", "Main Topic"), ("h2", "Sub Topic")] - - splitter = HTMLHeaderTextSplitter( - headers_to_split_on=headers_to_split_on, - return_each_element=False - ) - - html_content = \"\"\" - - + html_content = \"\"\" + +

Introduction

Welcome to the introduction section.

Background

Some background details here.

Conclusion

Final thoughts.

- - - \"\"\" + + + \"\"\" - documents = splitter.split_text(html_content) - - # 'documents' now contains Document objects reflecting the hierarchy: - # - Document with metadata={"Main Topic": "Introduction"} and - # content="Introduction" - # - Document with metadata={"Main Topic": "Introduction"} and - # content="Welcome to the introduction section." - # - Document with metadata={"Main Topic": "Introduction", - # "Sub Topic": "Background"} and content="Background" - # - Document with metadata={"Main Topic": "Introduction", - # "Sub Topic": "Background"} and content="Some background details here." - # - Document with metadata={"Main Topic": "Conclusion"} and - # content="Conclusion" - # - Document with metadata={"Main Topic": "Conclusion"} and - # content="Final thoughts." + documents = splitter.split_text(html_content) + # 'documents' now contains Document objects reflecting the hierarchy: + # - Document with metadata={"Main Topic": "Introduction"} and + # content="Introduction" + # - Document with metadata={"Main Topic": "Introduction"} and + # content="Welcome to the introduction section." + # - Document with metadata={"Main Topic": "Introduction", + # "Sub Topic": "Background"} and content="Background" + # - Document with metadata={"Main Topic": "Introduction", + # "Sub Topic": "Background"} and content="Some background details here." + # - Document with metadata={"Main Topic": "Conclusion"} and + # content="Conclusion" + # - Document with metadata={"Main Topic": "Conclusion"} and + # content="Final thoughts." + ``` """ def __init__( @@ -534,33 +532,32 @@ class HTMLSemanticPreservingSplitter(BaseDocumentTransformer): !!! version-added "Added in version 0.3.5" Example: - .. code-block:: python + ```python + from langchain_text_splitters.html import HTMLSemanticPreservingSplitter - from langchain_text_splitters.html import HTMLSemanticPreservingSplitter + def custom_iframe_extractor(iframe_tag): + ``` + Custom handler function to extract the 'src' attribute from an