cohere: move package to external repo (#20081)

This commit is contained in:
Erick Friis
2024-04-05 14:29:15 -07:00
committed by GitHub
parent 58a2123ca0
commit 28dfde2cb2
64 changed files with 2 additions and 6346 deletions

View File

@@ -1 +0,0 @@
__pycache__

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 LangChain, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,57 +0,0 @@
.PHONY: all format lint test tests integration_tests docker_tests help extended_tests
# Default target executed when no arguments are given to make.
all: help
# Define a variable for the test file path.
TEST_FILE ?= tests/unit_tests/
integration_test integration_tests: TEST_FILE=tests/integration_tests/
test tests integration_test integration_tests:
poetry run pytest $(TEST_FILE)
######################
# LINTING AND FORMATTING
######################
# Define a variable for Python and notebook files.
PYTHON_FILES=.
MYPY_CACHE=.mypy_cache
lint format: PYTHON_FILES=.
lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/partners/cohere --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$')
lint_package: PYTHON_FILES=langchain_cohere
lint_tests: PYTHON_FILES=tests
lint_tests: MYPY_CACHE=.mypy_cache_test
lint lint_diff lint_package lint_tests:
poetry run ruff .
poetry run ruff format $(PYTHON_FILES) --diff
poetry run ruff --select I $(PYTHON_FILES)
mkdir $(MYPY_CACHE); poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
format format_diff:
poetry run ruff format $(PYTHON_FILES)
poetry run ruff --select I --fix $(PYTHON_FILES)
spell_check:
poetry run codespell --toml pyproject.toml
spell_fix:
poetry run codespell --toml pyproject.toml -w
check_imports: $(shell find langchain_cohere -name '*.py')
poetry run python ./scripts/check_imports.py $^
######################
# HELP
######################
help:
@echo '----'
@echo 'check_imports - check imports'
@echo 'format - run code formatters'
@echo 'lint - run linters'
@echo 'test - run unit tests'
@echo 'tests - run unit tests'
@echo 'test TEST_FILE=<test_file> - run all tests in file'

View File

@@ -1,93 +1,3 @@
# Cohere
This package has moved!
>[Cohere](https://cohere.ai/about) is a Canadian startup that provides natural language processing models
> that help companies improve human-machine interactions.
## Installation and Setup
- Install the Python SDK :
```bash
pip install langchain-cohere
```
Get a [Cohere api key](https://dashboard.cohere.ai/) and set it as an environment variable (`COHERE_API_KEY`)
## Cohere langchain integrations
| API | description | Endpoint docs | Import | Example usage |
| ---------------- | -------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------- |
| Chat | Build chat bots | [chat](https://docs.cohere.com/reference/chat) | `from langchain_cohere import ChatCohere` | [cohere.ipynb](/docs/integrations/chat/cohere) |
| LLM | Generate text | [generate](https://docs.cohere.com/reference/generate) | `from langchain_cohere import Cohere` | [cohere.ipynb](/docs/integrations/llms/cohere) |
| RAG Retriever | Connect to external data sources | [chat + rag](https://docs.cohere.com/reference/chat) | `from langchain.retrievers import CohereRagRetriever` | [cohere.ipynb](/docs/integrations/retrievers/cohere) |
| Text Embedding | Embed strings to vectors | [embed](https://docs.cohere.com/reference/embed) | `from langchain_cohere import CohereEmbeddings` | [cohere.ipynb](/docs/integrations/text_embedding/cohere) |
| Rerank Retriever | Rank strings based on relevance | [rerank](https://docs.cohere.com/reference/rerank) | `from langchain.retrievers.document_compressors import CohereRerank` | [cohere.ipynb](/docs/integrations/retrievers/cohere-reranker) |
## Quick copy examples
### Chat
```python
from langchain_cohere import ChatCohere
from langchain_core.messages import HumanMessage
chat = ChatCohere()
messages = [HumanMessage(content="knock knock")]
print(chat(messages))
```
### LLM
```python
from langchain_cohere import Cohere
llm = Cohere(model="command")
print(llm.invoke("Come up with a pet name"))
```
### ReAct Agent
```python
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_cohere import ChatCohere, create_cohere_react_agent
from langchain.prompts import ChatPromptTemplate
from langchain.agents import AgentExecutor
llm = ChatCohere()
internet_search = TavilySearchResults(max_results=4)
internet_search.name = "internet_search"
internet_search.description = "Route a user query to the internet"
prompt = ChatPromptTemplate.from_template("{input}")
agent = create_cohere_react_agent(
llm,
[internet_search],
prompt
)
agent_executor = AgentExecutor(agent=agent, tools=[internet_search], verbose=True)```
agent_executor.invoke({
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?",
})
```
### RAG Retriever
```python
from langchain_cohere import ChatCohere
from langchain.retrievers import CohereRagRetriever
from langchain_core.documents import Document
rag = CohereRagRetriever(llm=ChatCohere())
print(rag.get_relevant_documents("What is cohere ai?"))
```
### Text Embedding
```python
from langchain_cohere import CohereEmbeddings
embeddings = CohereEmbeddings(model="embed-english-light-v3.0")
print(embeddings.embed_documents(["This is a test document."]))
```
https://github.com/langchain-ai/langchain-cohere/tree/main/libs/cohere

View File

@@ -1,236 +0,0 @@
{
"cells": [
{
"cell_type": "raw",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 0\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cohere Tools\n",
"\n",
"The following notebook goes over how to use the Cohere tools agent:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Prerequisites for this notebook:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: langchain in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (0.1.13)\n",
"Requirement already satisfied: langchain-cohere in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (0.1.0rc2)\n",
"Requirement already satisfied: PyYAML>=5.3 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (6.0.1)\n",
"Requirement already satisfied: SQLAlchemy<3,>=1.4 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (2.0.27)\n",
"Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (3.9.3)\n",
"Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (0.6.4)\n",
"Requirement already satisfied: jsonpatch<2.0,>=1.33 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (1.33)\n",
"Requirement already satisfied: langchain-community<0.1,>=0.0.29 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (0.0.29)\n",
"Requirement already satisfied: langchain-core<0.2.0,>=0.1.33 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (0.1.35)\n",
"Requirement already satisfied: langchain-text-splitters<0.1,>=0.0.1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (0.0.1)\n",
"Requirement already satisfied: langsmith<0.2.0,>=0.1.17 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (0.1.31)\n",
"Requirement already satisfied: numpy<2,>=1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (1.24.4)\n",
"Requirement already satisfied: pydantic<3,>=1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (2.6.4)\n",
"Requirement already satisfied: requests<3,>=2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (2.31.0)\n",
"Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain) (8.2.3)\n",
"Requirement already satisfied: cohere<6.0.0,>=5.1.4 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain-cohere) (5.1.4)\n",
"Requirement already satisfied: aiosignal>=1.1.2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
"Requirement already satisfied: attrs>=17.3.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.2.0)\n",
"Requirement already satisfied: frozenlist>=1.1.1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.4.1)\n",
"Requirement already satisfied: multidict<7.0,>=4.5 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.5)\n",
"Requirement already satisfied: yarl<2.0,>=1.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.4)\n",
"Requirement already satisfied: httpx>=0.21.2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from cohere<6.0.0,>=5.1.4->langchain-cohere) (0.27.0)\n",
"Requirement already satisfied: typing_extensions>=4.0.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from cohere<6.0.0,>=5.1.4->langchain-cohere) (4.10.0)\n",
"Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (3.20.2)\n",
"Requirement already satisfied: typing-inspect<1,>=0.4.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (0.9.0)\n",
"Requirement already satisfied: jsonpointer>=1.9 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from jsonpatch<2.0,>=1.33->langchain) (2.4)\n",
"Requirement already satisfied: packaging<24.0,>=23.2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langchain-core<0.2.0,>=0.1.33->langchain) (23.2)\n",
"Requirement already satisfied: orjson<4.0.0,>=3.9.14 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from langsmith<0.2.0,>=0.1.17->langchain) (3.9.15)\n",
"Requirement already satisfied: annotated-types>=0.4.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from pydantic<3,>=1->langchain) (0.6.0)\n",
"Requirement already satisfied: pydantic-core==2.16.3 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from pydantic<3,>=1->langchain) (2.16.3)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain) (3.6)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain) (2.2.1)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3,>=2->langchain) (2024.2.2)\n",
"Requirement already satisfied: anyio in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from httpx>=0.21.2->cohere<6.0.0,>=5.1.4->langchain-cohere) (4.3.0)\n",
"Requirement already satisfied: httpcore==1.* in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from httpx>=0.21.2->cohere<6.0.0,>=5.1.4->langchain-cohere) (1.0.4)\n",
"Requirement already satisfied: sniffio in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from httpx>=0.21.2->cohere<6.0.0,>=5.1.4->langchain-cohere) (1.3.1)\n",
"Requirement already satisfied: h11<0.15,>=0.13 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from httpcore==1.*->httpx>=0.21.2->cohere<6.0.0,>=5.1.4->langchain-cohere) (0.14.0)\n",
"Requirement already satisfied: mypy-extensions>=0.3.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from typing-inspect<1,>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain) (1.0.0)\n",
"Requirement already satisfied: wikipedia in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (1.4.0)\n",
"Requirement already satisfied: beautifulsoup4 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from wikipedia) (4.12.3)\n",
"Requirement already satisfied: requests<3.0.0,>=2.0.0 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from wikipedia) (2.31.0)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->wikipedia) (3.3.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->wikipedia) (3.6)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->wikipedia) (2.2.1)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.0.0->wikipedia) (2024.2.2)\n",
"Requirement already satisfied: soupsieve>1.2 in /Users/beatrix/Repos/langchain-1/.venv/lib/python3.11/site-packages (from beautifulsoup4->wikipedia) (2.5)\n"
]
}
],
"source": [
"# install package\n",
"!pip install langchain langchain-cohere\n",
"!pip install wikipedia"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import AgentExecutor\n",
"from langchain.retrievers import WikipediaRetriever\n",
"from langchain.tools.retriever import create_retriever_tool\n",
"from langchain_cohere import create_cohere_tools_agent, ChatCohere\n",
"from langchain_core.prompts import ChatPromptTemplate"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we create the prompt template and cohere model"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"# Create the prompt\n",
"prompt = ChatPromptTemplate.from_template(\n",
" \"Write all output in capital letters. {input}\"\n",
")\n",
"\n",
"# Create the Cohere chat model\n",
"chat = ChatCohere(cohere_api_key=\"API_KEY\", model=\"command-r\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example we use a Wikipedia retrieval tool "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"retriever = WikipediaRetriever()\n",
"retriever_tool = create_retriever_tool(\n",
" retriever,\n",
" \"wikipedia\",\n",
" \"Search for information on Wikipedia\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, create the cohere tool agent and call with the input"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mwikipedia\u001b[0m\u001b[36;1m\u001b[1;3m\u001b[0m\u001b[32;1m\u001b[1;3m\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"{'input': 'Who founded Cohere?',\n",
" 'text': 'COHERE WAS FOUNDED BY AIDAN GOMEZ, IVAN ZAPATA, AND ALON GELLA.',\n",
" 'additional_info': {'documents': [{'answer': '',\n",
" 'id': 'wikipedia:0:0',\n",
" 'tool_name': 'wikipedia'}],\n",
" 'citations': [ChatCitation(start=22, end=63, text='AIDAN GOMEZ, IVAN ZAPATA, AND ALON GELLA.', document_ids=['wikipedia:0:0'])],\n",
" 'search_results': None,\n",
" 'search_queries': None,\n",
" 'is_search_required': None,\n",
" 'generation_id': '3b7e96be-8aad-4fa0-9ae3-7a38e800c289',\n",
" 'token_count': {'prompt_tokens': 740,\n",
" 'response_tokens': 27,\n",
" 'total_tokens': 767,\n",
" 'billed_tokens': 48}}}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent = create_cohere_tools_agent(\n",
" llm=chat,\n",
" tools=[retriever_tool],\n",
" prompt=prompt,\n",
")\n",
"agent_executor = AgentExecutor(agent=agent, tools=[retriever_tool], verbose=True)\n",
"agent_executor.invoke({\"input\": \"Who founded Cohere?\"})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -1,318 +0,0 @@
{
"cells": [
{
"cell_type": "raw",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 0\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cohere Tools\n",
"\n",
"The following notebook goes over how to use the Cohere tools agent:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Prerequisites for this notebook:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# install package\n",
"!pip install -U langchain-cohere\n",
"%pip install wikipedia"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import AgentExecutor\n",
"from langchain_cohere.chat_models import ChatCohere\n",
"from langchain_cohere.react_multi_hop.agent import create_cohere_react_agent\n",
"from langchain.retrievers import WikipediaRetriever\n",
"from langchain.tools.retriever import create_retriever_tool\n",
"from langchain_core.prompts import ChatPromptTemplate"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we create the prompt template and cohere model"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# Create the prompt\n",
"prompt = ChatPromptTemplate.from_template(\n",
" \"Write all output in capital letters. {input}\"\n",
")\n",
"\n",
"# Create the Cohere chat model\n",
"chat = ChatCohere(cohere_api_key=\"API_KEY\", model=\"command-r\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example we use a Wikipedia retrieval tool "
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"retriever = WikipediaRetriever()\n",
"retriever_tool = create_retriever_tool(\n",
" retriever,\n",
" \"wikipedia\",\n",
" \"Search for information on Wikipedia\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, create the cohere tool agent and call with the input"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mPlan: First I will search for who the second person to walk on the moon was. Then I will search for that person's mother's hometown and write the answer in capital letters.\n",
"Action: ```json\n",
"[\n",
" {\n",
" \"tool_name\": \"wikipedia\",\n",
" \"parameters\": {\n",
" \"query\": \"second man to walk on the moon\"\n",
" }\n",
" }\n",
"]\n",
"```\u001b[0m\u001b[36;1m\u001b[1;3mWalk the Moon (stylized as WALK THE MOON) is an American pop rock band based in Cincinnati, Ohio. Lead singer Nicholas Petricca started the band in 2006, while a student at Kenyon College, deriving the band's name from the song \"Walking on the Moon\" by The Police. Although the band is best known for their most successful hit single to date \"Shut Up and Dance\", other notable songs include \"Anna Sun\" and \"One Foot\".\n",
"Walk the Moon has cited Talking Heads as influences. The band's use of 1980s musical mainstays, such as keyboard and synthesizer, is also notable.\n",
"\n",
"\n",
"== History ==\n",
"\n",
"\n",
"=== 20102011: Beginning, Anna Sun and i Want! i Want! ===\n",
"\n",
"The group independently released their debut studio album, I Want! I Want!, in November 2010, receiving airplay for the track \"Anna Sun\" on multiple alternative radio stations. Along with the success of \"Anna Sun\", Alt Nation named them a band you need to know for the summer of 2012. Influential music blog Neon Gold helped to break the band in January 2011, calling \"Anna Sun\", \"the kind of stuff British A&R dreams, and major label bidding wars, are made of.\" In February 2011, Walk the Moon signed to RCA Records.\n",
"In 2011, the band members began to paint their faces for live performances and they would bring enough paint to share with audience members. They have claimed it has become a “live tradition”. Bonnaroos camera crew documented the painting process in a short video from the 2011 festival. The band played at the Sasquatch Music Festival and Firefly Music Festival. In these years, they were known for their energetic performances and tireless touring schedule.\n",
"Before the release of their self-titled album, Walk the Moon joined many other performers at the Music Midtown festival and performed on the Great Southeast Music Hall Stage in Atlanta, Georgia in September 2011. In spring 2011, the band went on a short tour with the west coast band, Grouplove, as well as supporting Panic! at the Disco and Weezer on select dates. The band played on the main stage at the 20th Anniversary of Lollapalooza and also supported Local Natives in an Official Lollaplooza Aftershow at Lincoln Hall.\n",
"The band recorded i Want! i Want! with Chris Schmidt and Ben Cochran at Soap Floats Recording Studio in their hometown of Cincinnati, Ohio and then self-released it. The lead single from the album, \"Anna Sun\", became a surprise hit in the summer of 2011 following an endorsement by the Esquire article \"30 Summer Songs Every Man Should Listen To\". The song was written by Petricca and New York songwriter Nick Lerangis as their time at Kenyon College came to an end. \"It's about college, about maintaining that little bit of being a kid,\" Petricca said. \"Don't be afraid to play.\" The song was named after one of their favorite teachers. It was named song of the summer by MTV and Seventeen Mag, and one of the top songs of the year by Amazon. It has been officially remixed by Fool's Gold and received a Trouble Productions remix by Albert Hammond Jr. Anna Sun rose to the number one spot on Alt. Nation on Sirius XM Radio. \"Anna Sun\" was added to the video rotation of American Eagle Outfitters stores in May 2011. It was featured on the hit TV show Vampire Diaries in the first episode of season three. It was also the free single of the week on iTunes for the week of May 15.\n",
"Filmed in 2011 in Cincinnati's Over-the-Rhine neighborhood, the \"Anna Sun\" music video was released to coincide with the album. The video was shot on-location at the Cincinnati Mockbee building, as well as at a city park. It was directed and produced by Patrick Meier of the Cincinnati company, Contrast Productions, and features original choreography from Kim Popa of PONES Inc., as well as a cast full of the band's friends and locals from Cincinnati. MTV Hive calls the video a \"hilariously choreographed, neon-colored and awesomely shot in one take\" production.\n",
"\n",
"\n",
"=== 20122013: Walk the Moon and Tightrope EP ===\n",
"\n",
"The band's self-titled major label debut Walk t\n",
"\n",
"As part of the Apollo program by NASA, 24 astronauts flew nine missions to the Moon between December 1968 and December 1972. During six successful two-man landing missions, twelve men walked on the lunar surface, six of whom drove Lunar Roving Vehicles as part of the last three missions. Three men have been to the Moon twice, one orbited once and took a circumlunar trajectory the second time, while the other two landed once apiece. Apart from these 24 men, no human being has gone beyond low Earth orbit. No woman has been to the Moon, but a number of non-human animals have circled or orbited it, including two tortoises, several turtles, and five mice.\n",
"Apollo missions 8 and 1017 were the nine crewed missions to the Moon. Apollo 46 and AS-201 and AS-202 were uncrewed, while AS-203 is considered a test flight. The Apollo program included three other crewed missions: Apollo 1 (AS-204) did not launch and its crew died in a ground-based capsule fire, while Apollo 7 and Apollo 9 were low Earth orbit missions that only tested spacecraft components and docking maneuvers. Apollo missions 18, 19, and 20 were canceled. Twelve astronauts later flew unused Apollo command modules in the Apollo Applications Program's Skylab and ApolloSoyuz Test Project. Of the 24 astronauts who flew to the Moon, two went on to command a Skylab mission, one commanded ApolloSoyuz, one flew as commander for Approach and Landing Tests of the Space Shuttle, and two commanded orbital Space Shuttle missions.\n",
"\n",
"\n",
"== Prime crew members ==\n",
"NASA's Director of Flight Crew Operations during the Gemini and Apollo programs was Donald K. \"Deke\" Slayton, one of the original Mercury Seven astronauts, who was medically grounded in September 1962 due to a minor cardiac arrhythmia paroxysmal atrial fibrillation. Slayton was responsible for making all Gemini and Apollo crew assignments. In March 1972, Slayton was restored to flight status, and flew on the 1975 ApolloSoyuz Test Project mission.\n",
"The prime crew members selected for actual missions are here grouped by their NASA astronaut selection groups, and within each group in the order selected for flight. Two versions of the Apollo Command/Service Module (CSM) spacecraft were developed: Block I, intended for preliminary low Earth orbit testing; and Block II, redesigned for the lunar landing. The Block I crew position titles were Command Pilot, Senior Pilot (second seat), and Pilot (third seat). The corresponding Block II titles were: Commander, Command Module Pilot, and Lunar Module Pilot. The second seat pilot was given secondary responsibility for celestial navigation to keep the CSM's guidance computer accurately calibrated with the spacecraft's true position, and the third seat pilot served as a flight engineer, monitoring the health of the spacecraft systems.\n",
"\n",
"\n",
"== Apollo astronauts by their dates of selection by NASA ==\n",
"\n",
"\n",
"=== 1959 ===\n",
"Virgil I. \"Gus\" Grissom began his career at NASA in 1959. In 1966, he was selected as Command Pilot for the first crewed Apollo mission, a low Earth orbit test. This mission ended a month before its scheduled launch, when a cabin fire on the launch pad killed Grissom, Ed White and Roger Chaffee on January 27, 1967.\n",
"Walter M. Schirra Jr. also began his NASA career in 1959. He was selected in October 1968 as Command Pilot for Apollo 7, an 11-day, low Earth orbit shakedown test of the three-man Apollo Command/Service Module and the first crewed launch for the Apollo project.\n",
"Alan B. Shepard Jr. America's first man in space on Freedom 7 was originally selected to command Gemini 3, but was medically grounded for the duration of Gemini due to Ménière's disease and assisted Slayton in Flight Operations. After corrective surgery, Shepard was restored to flight status and commanded Apollo 14, the third successful Moon landing mission.\n",
"\n",
"\n",
"=== 1962 ===\n",
"\n",
"All of these astronauts flew on Gemini, and except for White, each commanded one Gemini and one Apollo mission:\n",
"\n",
"Edward H. White II Second-seat \n",
"\n",
"\"Man on the Moon\" is a song by American alternative rock band R.E.M., released in November 1992 as the second single from their eighth album, Automatic for the People (1992). The lyrics were written by lead singer Michael Stipe, and the music by drummer Bill Berry and guitarist Peter Buck. The song was well received by critics and reached number 30 on the US Billboard Hot 100, number 17 on the US Cash Box Top 100, number 18 on the UK Singles Chart, and number one in Iceland. It remains one of R.E.M.'s most popular songs and was included on the compilations In Time: The Best of R.E.M. 19882003 and Part Lies, Part Heart, Part Truth, Part Garbage 19822011.\n",
"\"Man on the Moon\" is a tribute to comedian Andy Kaufman, with numerous references to his career including his Elvis impersonation, wrestling, and the film My Breakfast with Blassie. The song's title and chorus refer to Moon landing conspiracy theories, as an oblique allusion to rumors that Kaufman's death in 1984 was faked. The song gave its name to Miloš Forman's Kaufman biopic Man on the Moon (1999), and features prominently in the film's soundtrack.\n",
"\n",
"\n",
"== Composition ==\n",
"\"Man on the Moon\" is a mid-tempo country-rock song following a verse-chorus structure with an added pre-chorus and an instrumental bridge following the second and third choruses. The song has six lines in the first verse but only four in the second and third verses.An early instrumental demo of the song was known to the band as \"C to D Slide\". Guitarist Peter Buck has explained how the music came together: \"'Man on the Moon' was something that Bill [Berry] had, this one chord change that he came in with, which was C to D like the verse of the song, and he said: 'I don't know what to do with that.' I used to finish some of Bill's things ... he would come up with the riffs, but I would be the finish guy for that. I sat down and came up with the chorus, the bridges, and so forth. I remember we showed it to Mike and Michael when they came in later; definitely we had the song finished. I think Bill played bass and I played guitar; we kept going around with it. I think we might have played some mandolin on it in the rehearsal studio.\"Michael Stipe explained in an interview with Charlie Rose how the lyric was written independently of the music, which had no prior association with the song's eventual lyrical content regarding Kaufman. Stipe recounted the other R.E.M. members had written and performed the music of the song and recorded it along with the rest of the Automatic for the People album during studio sessions in Seattle. As of the final week of the recording sessions, Stipe was still struggling to write the lyric, and the others continued to plead with him to finish it. Stipe attempted to argue the track should be an instrumental, but his bandmates were insistent. Stipe listened to the track on a walk around Seattle on his Walkman cassette player and was inspired to write about Andy Kaufman. After Stipe went back to the studio to complete the vocal track, the track was mixed that night and sent out the following day to be mastered.\n",
"\n",
"\n",
"== Lyric ==\n",
"The song's lyric does not tell a conventional story and instead forms a collection of cultural references, images and ideas. There are repeated mentions of Andy Kaufman, including references to his Elvis impersonation and work with wrestlers Fred Blassie and Jerry Lawler. The song also invokes the conspiracy theories surrounding the Moon landing and Elvis Presley's death as an indirect nod to the persistent rumors that Kaufman faked his own death. Speaking in 2017 to the NME, Mike Mills explained that the perceived ambiguity of Kaufman's legacy, including questions of whether he was a comedian or a performance artist, and whether his work was funny or irritating, was a way to frame other questions about life within the song:\n",
"\n",
"He's the perfect ghost to lead you through this tour of questioning things. Did the moon landing really happen? Is Elvis really dead? He was ki\u001b[0m\u001b[32;1m\u001b[1;3mReflection: I have found that the second person to walk on the moon was Buzz Aldrin. Now I will search for the hometown of his mother and write the answer in capital letters.\n",
"Action: ```json\n",
"[\n",
" {\n",
" \"tool_name\": \"wikipedia\",\n",
" \"parameters\": {\n",
" \"query\": \"Buzz Aldrin mother's hometown\"\n",
" }\n",
" }\n",
"]\n",
"```\u001b[0m\u001b[36;1m\u001b[1;3mBuzz Aldrin (; born Edwin Eugene Aldrin Jr.; January 20, 1930) is an American former astronaut, engineer and fighter pilot. He made three spacewalks as pilot of the 1966 Gemini 12 mission, and was the Lunar Module Eagle pilot on the 1969 Apollo 11 mission. He was the second person to walk on the Moon after mission commander Neil Armstrong.\n",
"Born in Glen Ridge, New Jersey, Aldrin graduated third in the class of 1951 from the United States Military Academy at West Point with a degree in mechanical engineering. He was commissioned into the United States Air Force and served as a jet fighter pilot during the Korean War. He flew 66 combat missions and shot down two MiG-15 aircraft.\n",
"After earning a Doctor of Science degree in astronautics from the Massachusetts Institute of Technology (MIT), Aldrin was selected as a member of NASA's Astronaut Group 3, making him the first astronaut with a doctoral degree. His doctoral thesis, Line-of-Sight Guidance Techniques for Manned Orbital Rendezvous, earned him the nickname \"Dr. Rendezvous\" from fellow astronauts. His first space flight was in 1966 on Gemini 12, during which he spent over five hours on extravehicular activity. Three years later, Aldrin set foot on the Moon at 03:15:16 on July 21, 1969 (UTC), nineteen minutes after Armstrong first touched the surface, while command module pilot Michael Collins remained in lunar orbit. A Presbyterian elder, Aldrin became the first person to hold a religious ceremony on the Moon when he privately took communion.\n",
"After leaving NASA in 1971, Aldrin became Commandant of the U.S. Air Force Test Pilot School. He retired from the Air Force in 1972 after 21 years of service. His autobiographies Return to Earth (1973) and Magnificent Desolation (2009) recount his struggles with clinical depression and alcoholism in the years after leaving NASA. Aldrin continues to advocate for space exploration, particularly a human mission to Mars. He developed the Aldrin cycler, a special spacecraft trajectory that makes travel to Mars more efficient in terms of time and propellant. He has been accorded numerous honors, including the Presidential Medal of Freedom in 1969.\n",
"\n",
"\n",
"== Early life and education ==\n",
"Aldrin was born Edwin Eugene Aldrin Jr. on January 20, 1930, at Mountainside Hospital in Glen Ridge, New Jersey. His parents, Edwin Eugene Aldrin Sr. and Marion Aldrin (née Moon), lived in neighboring Montclair. His father was an Army aviator during World War I and the assistant commandant of the Army's test pilot school at McCook Field, Ohio, from 1919 to 1922, but left the Army in 1928 and became an executive at Standard Oil. Aldrin had two sisters: Madeleine, who was four years older, and Fay Ann, who was a year and a half older. His nickname, which became his legal first name in 1988, arose as a result of Fay's mispronouncing \"brother\" as \"buzzer\", which was then shortened to \"Buzz\". He was a Boy Scout, achieving the rank of Tenderfoot Scout.Aldrin did well in school, maintaining an A average. He played football and was the starting center for Montclair High School's undefeated 1946 state champion team. His father wanted him to go to the United States Naval Academy in Annapolis, Maryland, and enrolled him at nearby Severn School, a preparatory school for Annapolis, and even secured him a Naval Academy appointment from Albert W. Hawkes, one of the United States senators from New Jersey. Aldrin attended Severn School in 1946, but had other ideas about his future career. He suffered from seasickness and considered ships a distraction from flying airplanes. He faced down his father and told him to ask Hawkes to change the nomination to the United States Military Academy at West Point, New York.Aldrin entered West Point in 1947. He did well academically, finishing first in his class his plebe (first) year. Aldrin was also an excellent athlete, competing in pole vault for the academy track and field team. In 1950, he traveled with a group of West Point cadets to Japan and\n",
"\n",
"Neil Alden Armstrong (August 5, 1930 August 25, 2012) was an American astronaut and aeronautical engineer who in 1969 became the first person to walk on the Moon. He was also a naval aviator, test pilot, and university professor.\n",
"Armstrong was born and raised in Wapakoneta, Ohio. He entered Purdue University, studying aeronautical engineering, with the U.S. Navy paying his tuition under the Holloway Plan. He became a midshipman in 1949 and a naval aviator the following year. He saw action in the Korean War, flying the Grumman F9F Panther from the aircraft carrier USS Essex. After the war, he completed his bachelor's degree at Purdue and became a test pilot at the National Advisory Committee for Aeronautics (NACA) High-Speed Flight Station at Edwards Air Force Base in California. He was the project pilot on Century Series fighters and flew the North American X-15 seven times. He was also a participant in the U.S. Air Force's Man in Space Soonest and X-20 Dyna-Soar human spaceflight programs.\n",
"Armstrong joined the NASA Astronaut Corps in the second group, which was selected in 1962. He made his first spaceflight as command pilot of Gemini 8 in March 1966, becoming NASA's first civilian astronaut to fly in space. During this mission with pilot David Scott, he performed the first docking of two spacecraft; the mission was aborted after Armstrong used some of his re-entry control fuel to stabilize a dangerous roll caused by a stuck thruster. During training for Armstrong's second and last spaceflight as commander of Apollo 11, he had to eject from the Lunar Landing Research Vehicle moments before a crash.\n",
"On July 20, 1969, Armstrong and Apollo 11 Lunar Module (LM) pilot Buzz Aldrin became the first people to land on the Moon, and the next day they spent two and a half hours outside the Lunar Module Eagle spacecraft while Michael Collins remained in lunar orbit in the Apollo Command Module Columbia. When Armstrong first stepped onto the lunar surface, he famously said: \"That's one small step for [a] man, one giant leap for mankind.\" It was broadcast live to an estimated 530 million viewers worldwide. Apollo 11 was a major U.S. victory in the Space Race, by fulfilling a national goal proposed in 1961 by President John F. Kennedy \"of landing a man on the Moon and returning him safely to the Earth\" before the end of the decade. Along with Collins and Aldrin, Armstrong was awarded the Presidential Medal of Freedom by President Richard Nixon and received the 1969 Collier Trophy. President Jimmy Carter presented him with the Congressional Space Medal of Honor in 1978, he was inducted into the National Aviation Hall of Fame in 1979, and with his former crewmates received the Congressional Gold Medal in 2009.\n",
"After he resigned from NASA in 1971, Armstrong taught in the Department of Aerospace Engineering at the University of Cincinnati until 1979. He served on the Apollo 13 accident investigation and on the Rogers Commission, which investigated the Space Shuttle Challenger disaster. In 2012, Armstrong died due to complications resulting from coronary bypass surgery, at the age of 82.\n",
"\n",
"\n",
"== Early life ==\n",
"Armstrong was born near Wapakoneta, Ohio, on August 5, 1930, the son of Viola Louise (née Engel) and Stephen Koenig Armstrong. He was of German, English, Scots-Irish, and Scottish descent. He is a descendant of Clan Armstrong. He had a younger sister, June, and a younger brother, Dean. His father was an auditor for the Ohio state government, and the family moved around the state repeatedly, living in 16 towns over the next 14 years. Armstrong's love for flying grew during this time, having started at the age of two when his father took him to the Cleveland Air Races. When he was five or six, he experienced his first airplane flight in Warren, Ohio, when he and his father took a ride in a Ford Trimotor (also known as the \"Tin Goose\").The family's last move was in 1944 and took them back to Wapakoneta, where Armstrong attended Blume High Scho\n",
"\n",
"David Kaufman (born July 23, 1961) is an American actor. He is best known for his voice roles of Dexter Douglas in Freakazoid!, Jimmy Olsen in various DC projects beginning with Superman: The Animated Series, the titular protagonist in Danny Phantom, Aldrin in The Buzz on Maggie, Marty McFly in Back to the Future, and Stuart Little in the animated series of the same name. He often is a voice double for Michael J. Fox.\n",
"\n",
"\n",
"== Early life ==\n",
"Kaufman was born and raised in St. Louis, Missouri. His father is Jewish, while his mother is Catholic. Kaufman began acting at a young age in his hometown when his kindergarten teacher handed him the plum lead role of Santa Claus in the class Christmas play.\n",
"At the age of 18, Kaufman moved from St. Louis to attend UCLA; he was a student in the Department of Theater Arts.\n",
"\n",
"\n",
"== Career ==\n",
"Since his college years, Kaufman has studied and worked extensively as a professional actor in films and television.\n",
"He has worked with the Daly family of actors on several projects: He worked with Tim Daly on Superman: The Animated Series and its subsequent spin-offs, playing Jimmy Olsen. He also appeared with Daly in the series Wings in 1995 with his wife Lisa; the two portrayed a couple whose wedding Daly's character Joe and his fiancée Helen crash. He worked with Tim's sister, Tyne Daly on Kids Like These and joined Tim again in Justice League: Doom, reprising his role of Jimmy Olsen.\n",
"Kaufman has also worked as a commercial actor. One of his most prominent roles was as a dancing stockboy in a 1989 commercial for Hi-C Ecto Cooler.\n",
"\n",
"\n",
"== Personal life ==\n",
"On June 30, 1990, Kaufman married actress Lisa Picotte; together, they have two children who are also actors, including Grace Kaufman and Henry Kaufman. He came out as bisexual on Celebrate Bisexuality Day in 2021.\n",
"\n",
"\n",
"== Filmography ==\n",
"\n",
"\n",
"=== Television series ===\n",
"\n",
"\n",
"=== Films ===\n",
"\n",
"\n",
"=== Video games ===\n",
"\n",
"\n",
"=== Commercials ===\n",
"AT&T (starring with Ray Walston)\n",
"Pepsi (starring with Cindy Crawford)\n",
"Blockbuster (starring with Magic Johnson)\n",
"Kid Cuisine (voice of K. C. Penguin)as well as spots for Honda, Hi-C Ecto-Cooler, Maxwell House, Dentyne, Chili's, Midas, Wendy's, Twiglets and British Petroleum, among others.\n",
"\n",
"\n",
"=== Stage work ===\n",
"Kaufman has earned several Los Angeles area critics' awards and nominations.\n",
"He has been a member of the West Coast Ensemble in Los Angeles for over ten years, performing such varied roles as:\n",
"\n",
"Skeets Miller in Floyd Collins\n",
"Prosecutor Gilmer in To Kill a Mockingbird\n",
"George Lewis in Kaufman and Hart's Once in a Lifetime\n",
"Orestes in Electra\n",
"Tyler in Sondheim and Furth's Merrily We Roll Along\n",
"Ronnie Shaughnessy in The House of Blue Leaves\n",
"Callimaco in Machiavelli's The Mandrake\n",
"Eugene Jerome in Neil Simon's Biloxi Blues\n",
"Paul Palmer in James Duff's A Quarrel of Sparrows at The Court Theatre in Los Angeles\n",
"Stewie in the premiere of Richard Greenberg's Night and Her Stars at South Coast Repertory.\n",
"\n",
"\n",
"== References ==\n",
"\n",
"\n",
"== External links ==\n",
"David Kaufman at IMDb\u001b[0m\u001b[32;1m\u001b[1;3mRelevant Documents: 1\n",
"Cited Documents: 1\n",
"Answer: MONTclair, NEW JERSEY.\n",
"Grounded answer: <co: 1>Montclair</co: 1>, <co: 1>New Jersey</co: 1>.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"{'input': 'What was the hometown of the mother of the second person to walk on the moon?',\n",
" 'output': 'MONTclair, NEW JERSEY.'}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent = create_cohere_react_agent(\n",
" llm=chat,\n",
" tools=[retriever_tool],\n",
" prompt=prompt,\n",
")\n",
"agent_executor = AgentExecutor(agent=agent, tools=[retriever_tool], verbose=True)\n",
"agent_executor.invoke({\"input\": \"What was the hometown of the mother of the second person to walk on the moon?\"})"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -1,17 +0,0 @@
from langchain_cohere.chat_models import ChatCohere
from langchain_cohere.cohere_agent import create_cohere_tools_agent
from langchain_cohere.common import CohereCitation
from langchain_cohere.embeddings import CohereEmbeddings
from langchain_cohere.rag_retrievers import CohereRagRetriever
from langchain_cohere.react_multi_hop.agent import create_cohere_react_agent
from langchain_cohere.rerank import CohereRerank
__all__ = [
"CohereCitation",
"ChatCohere",
"CohereEmbeddings",
"CohereRagRetriever",
"CohereRerank",
"create_cohere_tools_agent",
"create_cohere_react_agent",
]

View File

@@ -1,390 +0,0 @@
import json
from typing import (
Any,
AsyncIterator,
Dict,
Iterator,
List,
Optional,
Sequence,
Type,
Union,
)
from cohere.types import NonStreamedChatResponse, ToolCall
from langchain_core._api import beta
from langchain_core.callbacks import (
AsyncCallbackManagerForLLMRun,
CallbackManagerForLLMRun,
)
from langchain_core.documents import Document
from langchain_core.language_models import LanguageModelInput
from langchain_core.language_models.chat_models import (
BaseChatModel,
agenerate_from_stream,
generate_from_stream,
)
from langchain_core.messages import (
AIMessage,
AIMessageChunk,
BaseMessage,
ChatMessage,
HumanMessage,
SystemMessage,
)
from langchain_core.output_parsers.base import OutputParserLike
from langchain_core.output_parsers.openai_tools import (
JsonOutputKeyToolsParser,
PydanticToolsParser,
)
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
from langchain_core.pydantic_v1 import BaseModel
from langchain_core.runnables import Runnable
from langchain_core.tools import BaseTool
from langchain_cohere.cohere_agent import (
_convert_to_cohere_tool,
_format_to_cohere_tools,
)
from langchain_cohere.llms import BaseCohere
def get_role(message: BaseMessage) -> str:
"""Get the role of the message.
Args:
message: The message.
Returns:
The role of the message.
Raises:
ValueError: If the message is of an unknown type.
"""
if isinstance(message, ChatMessage) or isinstance(message, HumanMessage):
return "User"
elif isinstance(message, AIMessage):
return "Chatbot"
elif isinstance(message, SystemMessage):
return "System"
else:
raise ValueError(f"Got unknown type {message}")
def get_cohere_chat_request(
messages: List[BaseMessage],
*,
documents: Optional[List[Document]] = None,
connectors: Optional[List[Dict[str, str]]] = None,
stop_sequences: Optional[List[str]] = None,
**kwargs: Any,
) -> Dict[str, Any]:
"""Get the request for the Cohere chat API.
Args:
messages: The messages.
connectors: The connectors.
**kwargs: The keyword arguments.
Returns:
The request for the Cohere chat API.
"""
additional_kwargs = messages[-1].additional_kwargs
# cohere SDK will fail loudly if both connectors and documents are provided
if additional_kwargs.get("documents", []) and documents and len(documents) > 0:
raise ValueError(
"Received documents both as a keyword argument and as an prompt additional keyword argument. Please choose only one option." # noqa: E501
)
parsed_docs: Optional[Union[List[Document], List[Dict]]] = None
if "documents" in additional_kwargs:
parsed_docs = (
additional_kwargs["documents"]
if len(additional_kwargs["documents"]) > 0
else None
)
elif documents is not None and len(documents) > 0:
parsed_docs = documents
formatted_docs: Optional[List[Dict[str, Any]]] = None
if parsed_docs:
formatted_docs = []
for i, parsed_doc in enumerate(parsed_docs):
if isinstance(parsed_doc, Document):
formatted_docs.append(
{
"text": parsed_doc.page_content,
"id": parsed_doc.metadata.get("id") or f"doc-{str(i)}",
}
)
elif isinstance(parsed_doc, dict):
formatted_docs.append(parsed_doc)
# by enabling automatic prompt truncation, the probability of request failure is
# reduced with minimal impact on response quality
prompt_truncation = (
"AUTO" if formatted_docs is not None or connectors is not None else None
)
req = {
"message": messages[-1].content,
"chat_history": [
{"role": get_role(x), "message": x.content} for x in messages[:-1]
],
"documents": formatted_docs,
"connectors": connectors,
"prompt_truncation": prompt_truncation,
"stop_sequences": stop_sequences,
**kwargs,
}
return {k: v for k, v in req.items() if v is not None}
class ChatCohere(BaseChatModel, BaseCohere):
"""`Cohere` chat large language models.
To use, you should have the ``cohere`` python package installed, and the
environment variable ``COHERE_API_KEY`` set with your API key, or pass
it as a named parameter to the constructor.
Example:
.. code-block:: python
from langchain_cohere import ChatCohere
from langchain_core.messages import HumanMessage
chat = ChatCohere(cohere_api_key="my-api-key")
messages = [HumanMessage(content="knock knock")]
chat.invoke(messages)
"""
class Config:
"""Configuration for this pydantic object."""
allow_population_by_field_name = True
arbitrary_types_allowed = True
@property
def _llm_type(self) -> str:
"""Return type of chat model."""
return "cohere-chat"
@property
def _default_params(self) -> Dict[str, Any]:
"""Get the default parameters for calling Cohere API."""
base_params = {
"model": self.model,
"temperature": self.temperature,
}
return {k: v for k, v in base_params.items() if v is not None}
def bind_tools(
self,
tools: Sequence[Union[Dict[str, Any], BaseTool, Type[BaseModel]]],
**kwargs: Any,
) -> Runnable[LanguageModelInput, BaseMessage]:
formatted_tools = _format_to_cohere_tools(tools)
return super().bind(tools=formatted_tools, **kwargs)
@beta()
def with_structured_output(
self,
schema: Union[Dict, Type[BaseModel]],
**kwargs: Any,
) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]:
"""Model wrapper that returns outputs formatted to match the given schema.
Args:
schema: The output schema as a dict or a Pydantic class. If a Pydantic class
then the model output will be an object of that class. If a dict then
the model output will be a dict.
Returns:
A Runnable that takes any ChatModel input and returns either a dict or
Pydantic class as output.
"""
is_pydantic_schema = isinstance(schema, type) and issubclass(schema, BaseModel)
llm = self.bind_tools([schema], **kwargs)
if is_pydantic_schema:
output_parser: OutputParserLike = PydanticToolsParser(
tools=[schema], first_tool_only=True
)
else:
key_name = _convert_to_cohere_tool(schema)["name"]
output_parser = JsonOutputKeyToolsParser(
key_name=key_name, first_tool_only=True
)
return llm | output_parser
@property
def _identifying_params(self) -> Dict[str, Any]:
"""Get the identifying parameters."""
return self._default_params
def _stream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> Iterator[ChatGenerationChunk]:
request = get_cohere_chat_request(
messages, stop_sequences=stop, **self._default_params, **kwargs
)
if hasattr(self.client, "chat_stream"): # detect and support sdk v5
stream = self.client.chat_stream(**request)
else:
stream = self.client.chat(**request, stream=True)
for data in stream:
if data.event_type == "text-generation":
delta = data.text
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
if run_manager:
run_manager.on_llm_new_token(delta, chunk=chunk)
yield chunk
elif data.event_type == "stream-end":
generation_info = self._get_generation_info(data.response)
yield ChatGenerationChunk(
message=AIMessageChunk(
content="", additional_kwargs=generation_info
),
generation_info=generation_info,
)
async def _astream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> AsyncIterator[ChatGenerationChunk]:
request = get_cohere_chat_request(
messages, stop_sequences=stop, **self._default_params, **kwargs
)
if hasattr(self.async_client, "chat_stream"): # detect and support sdk v5
stream = self.async_client.chat_stream(**request)
else:
stream = self.async_client.chat(**request, stream=True)
async for data in stream:
if data.event_type == "text-generation":
delta = data.text
chunk = ChatGenerationChunk(message=AIMessageChunk(content=delta))
if run_manager:
await run_manager.on_llm_new_token(delta, chunk=chunk)
yield chunk
elif data.event_type == "stream-end":
generation_info = self._get_generation_info(data.response)
yield ChatGenerationChunk(
message=AIMessageChunk(
content="", additional_kwargs=generation_info
),
generation_info=generation_info,
)
def _get_generation_info(self, response: NonStreamedChatResponse) -> Dict[str, Any]:
"""Get the generation info from cohere API response."""
generation_info = {
"documents": response.documents,
"citations": response.citations,
"search_results": response.search_results,
"search_queries": response.search_queries,
"is_search_required": response.is_search_required,
"generation_id": response.generation_id,
}
if response.tool_calls:
# Only populate tool_calls when 1) present on the response and
# 2) has one or more calls.
generation_info["tool_calls"] = _format_cohere_tool_calls(
response.generation_id or "", response.tool_calls
)
if hasattr(response, "token_count"):
generation_info["token_count"] = response.token_count
return generation_info
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
if self.streaming:
stream_iter = self._stream(
messages, stop=stop, run_manager=run_manager, **kwargs
)
return generate_from_stream(stream_iter)
request = get_cohere_chat_request(
messages, stop_sequences=stop, **self._default_params, **kwargs
)
response = self.client.chat(**request)
generation_info = self._get_generation_info(response)
message = AIMessage(content=response.text, additional_kwargs=generation_info)
return ChatResult(
generations=[
ChatGeneration(message=message, generation_info=generation_info)
]
)
async def _agenerate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
if self.streaming:
stream_iter = self._astream(
messages, stop=stop, run_manager=run_manager, **kwargs
)
return await agenerate_from_stream(stream_iter)
request = get_cohere_chat_request(
messages, stop_sequences=stop, **self._default_params, **kwargs
)
response = self.client.chat(**request)
generation_info = self._get_generation_info(response)
message = AIMessage(content=response.text, additional_kwargs=generation_info)
return ChatResult(
generations=[
ChatGeneration(message=message, generation_info=generation_info)
]
)
def get_num_tokens(self, text: str) -> int:
"""Calculate number of tokens."""
return len(self.client.tokenize(text=text).tokens)
def _format_cohere_tool_calls(
generation_id: str, tool_calls: Optional[List[ToolCall]] = None
) -> List[Dict]:
"""
Formats a Cohere API response into the tool call format used elsewhere in Langchain.
"""
if not tool_calls:
return []
formatted_tool_calls = []
for tool_call in tool_calls:
formatted_tool_calls.append(
{
"id": generation_id,
"function": {
"name": tool_call.name,
"arguments": json.dumps(tool_call.parameters),
},
"type": "function",
}
)
return formatted_tool_calls

View File

@@ -1,204 +0,0 @@
import json
from typing import Any, Dict, List, Sequence, Tuple, Type, Union
from cohere.types import (
ChatRequestToolResultsItem,
Tool,
ToolCall,
ToolParameterDefinitionsValue,
)
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.language_models import BaseLanguageModel
from langchain_core.output_parsers import BaseOutputParser
from langchain_core.outputs import Generation
from langchain_core.outputs.chat_generation import ChatGeneration
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel
from langchain_core.runnables import Runnable, RunnablePassthrough
from langchain_core.runnables.base import RunnableLambda
from langchain_core.tools import BaseTool
from langchain_core.utils.function_calling import (
convert_to_openai_function,
)
from langchain_cohere.utils import (
JSON_TO_PYTHON_TYPES,
_remove_signature_from_tool_description,
)
def create_cohere_tools_agent(
llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: ChatPromptTemplate
) -> Runnable:
def llm_with_tools(input_: Dict) -> Runnable:
tool_results = (
input_["tool_results"] if len(input_["tool_results"]) > 0 else None
)
tools_ = input_["tools"] if len(input_["tools"]) > 0 else None
return RunnableLambda(lambda x: x["input"]) | llm.bind(
tools=tools_, tool_results=tool_results
)
agent = (
RunnablePassthrough.assign(
# Intermediate steps are in tool results.
# Edit below to change the prompt parameters.
input=lambda x: prompt.format_messages(**x, agent_scratchpad=[]),
tools=lambda x: _format_to_cohere_tools(tools),
tool_results=lambda x: _format_to_cohere_tools_messages(
x["intermediate_steps"]
),
)
| llm_with_tools
| _CohereToolsAgentOutputParser()
)
return agent
def _format_to_cohere_tools(
tools: Sequence[Union[Dict[str, Any], BaseTool, Type[BaseModel]]],
) -> List[Dict[str, Any]]:
return [_convert_to_cohere_tool(tool) for tool in tools]
def _format_to_cohere_tools_messages(
intermediate_steps: Sequence[Tuple[AgentAction, str]],
) -> List[Dict[str, Any]]:
"""Convert (AgentAction, tool output) tuples into tool messages."""
if len(intermediate_steps) == 0:
return []
tool_results = []
for agent_action, observation in intermediate_steps:
# agent_action.tool_input can be a dict, serialised dict, or string.
# Cohere API only accepts a dict.
tool_call_parameters: Dict[str, Any]
if isinstance(agent_action.tool_input, dict):
# tool_input is a dict, use as-is.
tool_call_parameters = agent_action.tool_input
else:
try:
# tool_input is serialised dict.
tool_call_parameters = json.loads(agent_action.tool_input)
if not isinstance(tool_call_parameters, dict):
raise ValueError()
except ValueError:
# tool_input is a string, last ditch attempt at having something useful.
tool_call_parameters = {"input": agent_action.tool_input}
tool_results.append(
ChatRequestToolResultsItem(
call=ToolCall(
name=agent_action.tool,
parameters=tool_call_parameters,
),
outputs=[{"answer": observation}],
).dict()
)
return tool_results
def _convert_to_cohere_tool(
tool: Union[Dict[str, Any], BaseTool, Type[BaseModel]],
) -> Dict[str, Any]:
"""
Convert a BaseTool instance, JSON schema dict, or BaseModel type to a Cohere tool.
"""
if isinstance(tool, BaseTool):
return Tool(
name=tool.name,
description=_remove_signature_from_tool_description(
tool.name, tool.description
),
parameter_definitions={
param_name: ToolParameterDefinitionsValue(
description=param_definition.get("description")
if "description" in param_definition
else "",
type=JSON_TO_PYTHON_TYPES.get(
param_definition.get("type"), param_definition.get("type")
),
required="default" not in param_definition,
)
for param_name, param_definition in tool.args.items()
},
).dict()
elif isinstance(tool, dict):
if not all(k in tool for k in ("title", "description", "properties")):
raise ValueError(
"Unsupported dict type. Tool must be passed in as a BaseTool instance, JSON schema dict, or BaseModel type." # noqa: E501
)
return Tool(
name=tool.get("title"),
description=tool.get("description"),
parameter_definitions={
param_name: ToolParameterDefinitionsValue(
description=param_definition.get("description"),
type=JSON_TO_PYTHON_TYPES.get(
param_definition.get("type"), param_definition.get("type")
),
required="default" not in param_definition,
)
for param_name, param_definition in tool.get("properties", {}).items()
},
).dict()
elif issubclass(tool, BaseModel):
as_json_schema_function = convert_to_openai_function(tool)
parameters = as_json_schema_function.get("parameters", {})
properties = parameters.get("properties", {})
return Tool(
name=as_json_schema_function.get("name"),
description=as_json_schema_function.get(
# The Cohere API requires the description field.
"description",
as_json_schema_function.get("name"),
),
parameter_definitions={
param_name: ToolParameterDefinitionsValue(
description=param_definition.get("description"),
type=JSON_TO_PYTHON_TYPES.get(
param_definition.get("type"), param_definition.get("type")
),
required=param_name in parameters.get("required", []),
)
for param_name, param_definition in properties.items()
},
).dict()
else:
raise ValueError(
f"Unsupported tool type {type(tool)}. Tool must be passed in as a BaseTool instance, JSON schema dict, or BaseModel type." # noqa: E501
)
class _CohereToolsAgentOutputParser(
BaseOutputParser[Union[List[AgentAction], AgentFinish]]
):
"""Parses a message into agent actions/finish."""
def parse_result(
self, result: List[Generation], *, partial: bool = False
) -> Union[List[AgentAction], AgentFinish]:
if not isinstance(result[0], ChatGeneration):
raise ValueError(f"Expected ChatGeneration, got {type(result)}")
if "tool_calls" in result[0].message.additional_kwargs:
actions = []
for tool in result[0].message.additional_kwargs["tool_calls"]:
function = tool.get("function", {})
actions.append(
AgentAction(
tool=function.get("name"),
tool_input=function.get("arguments"),
log=function.get("name"),
)
)
return actions
else:
return AgentFinish(
return_values={
"text": result[0].message.content,
"additional_info": result[0].message.additional_kwargs,
},
log="",
)
def parse(self, text: str) -> Union[List[AgentAction], AgentFinish]:
raise ValueError("Can only parse messages")

View File

@@ -1,36 +0,0 @@
from dataclasses import dataclass
from typing import Any, List, Mapping
@dataclass
class CohereCitation:
"""
Cohere has fine-grained citations that specify the exact part of text.
More info at https://docs.cohere.com/docs/documents-and-citations
"""
"""
The index of text that the citation starts at, counting from zero. For example, a
generation of 'Hello, world!' with a citation on 'world' would have a start value
of 7. This is because the citation starts at 'w', which is the seventh character.
"""
start: int
"""
The index of text that the citation ends after, counting from zero. For example, a
generation of 'Hello, world!' with a citation on 'world' would have an end value of
11. This is because the citation ends after 'd', which is the eleventh character.
"""
end: int
"""
The text of the citation. For example, a generation of 'Hello, world!' with a
citation of 'world' would have a text value of 'world'.
"""
text: str
"""
The contents of the documents that were cited. When used with agents these will be
the contents of relevant agent outputs.
"""
documents: List[Mapping[str, Any]]

View File

@@ -1,173 +0,0 @@
import typing
from typing import Any, Dict, List, Optional
import cohere
from langchain_core.embeddings import Embeddings
from langchain_core.pydantic_v1 import BaseModel, Extra, root_validator
from langchain_core.utils import get_from_dict_or_env
from .utils import _create_retry_decorator
class CohereEmbeddings(BaseModel, Embeddings):
"""Cohere embedding models.
To use, you should have the ``cohere`` python package installed, and the
environment variable ``COHERE_API_KEY`` set with your API key or pass it
as a named parameter to the constructor.
Example:
.. code-block:: python
from langchain_cohere import CohereEmbeddings
cohere = CohereEmbeddings(
model="embed-english-light-v3.0",
cohere_api_key="my-api-key"
)
"""
client: Any #: :meta private:
"""Cohere client."""
async_client: Any #: :meta private:
"""Cohere async client."""
model: str = "embed-english-v2.0"
"""Model name to use."""
truncate: Optional[str] = None
"""Truncate embeddings that are too long from start or end ("NONE"|"START"|"END")"""
cohere_api_key: Optional[str] = None
max_retries: int = 3
"""Maximum number of retries to make when generating."""
request_timeout: Optional[float] = None
"""Timeout in seconds for the Cohere API request."""
user_agent: str = "langchain:partner"
"""Identifier for the application making the request."""
base_url: Optional[str] = None
"""Override the default Cohere API URL."""
class Config:
"""Configuration for this pydantic object."""
arbitrary_types_allowed = True
extra = Extra.forbid
@root_validator()
def validate_environment(cls, values: Dict) -> Dict:
"""Validate that api key and python package exists in environment."""
cohere_api_key = get_from_dict_or_env(
values, "cohere_api_key", "COHERE_API_KEY"
)
request_timeout = values.get("request_timeout")
client_name = values["user_agent"]
values["client"] = cohere.Client(
cohere_api_key,
timeout=request_timeout,
client_name=client_name,
base_url=values["base_url"],
)
values["async_client"] = cohere.AsyncClient(
cohere_api_key,
timeout=request_timeout,
client_name=client_name,
base_url=values["base_url"],
)
return values
def embed_with_retry(self, **kwargs: Any) -> Any:
"""Use tenacity to retry the embed call."""
retry_decorator = _create_retry_decorator(self.max_retries)
@retry_decorator
def _embed_with_retry(**kwargs: Any) -> Any:
return self.client.embed(**kwargs)
return _embed_with_retry(**kwargs)
def aembed_with_retry(self, **kwargs: Any) -> Any:
"""Use tenacity to retry the embed call."""
retry_decorator = _create_retry_decorator(self.max_retries)
@retry_decorator
async def _embed_with_retry(**kwargs: Any) -> Any:
return await self.async_client.embed(**kwargs)
return _embed_with_retry(**kwargs)
def embed(
self,
texts: List[str],
*,
input_type: typing.Optional[cohere.EmbedInputType] = None,
) -> List[List[float]]:
embeddings = self.embed_with_retry(
model=self.model,
texts=texts,
input_type=input_type,
truncate=self.truncate,
).embeddings
return [list(map(float, e)) for e in embeddings]
async def aembed(
self,
texts: List[str],
*,
input_type: typing.Optional[cohere.EmbedInputType] = None,
) -> List[List[float]]:
embeddings = (
await self.aembed_with_retry(
model=self.model,
texts=texts,
input_type=input_type,
truncate=self.truncate,
)
).embeddings
return [list(map(float, e)) for e in embeddings]
def embed_documents(self, texts: List[str]) -> List[List[float]]:
"""Embed a list of document texts.
Args:
texts: The list of texts to embed.
Returns:
List of embeddings, one for each text.
"""
return self.embed(texts, input_type="search_document")
async def aembed_documents(self, texts: List[str]) -> List[List[float]]:
"""Async call out to Cohere's embedding endpoint.
Args:
texts: The list of texts to embed.
Returns:
List of embeddings, one for each text.
"""
return await self.aembed(texts, input_type="search_document")
def embed_query(self, text: str) -> List[float]:
"""Call out to Cohere's embedding endpoint.
Args:
text: The text to embed.
Returns:
Embeddings for the text.
"""
return self.embed([text], input_type="search_query")[0]
async def aembed_query(self, text: str) -> List[float]:
"""Async call out to Cohere's embedding endpoint.
Args:
text: The text to embed.
Returns:
Embeddings for the text.
"""
return (await self.aembed([text], input_type="search_query"))[0]

View File

@@ -1,245 +0,0 @@
from __future__ import annotations
import logging
import re
from typing import Any, Dict, List, Optional
import cohere
from langchain_core.callbacks import (
AsyncCallbackManagerForLLMRun,
CallbackManagerForLLMRun,
)
from langchain_core.language_models.llms import LLM
from langchain_core.load.serializable import Serializable
from langchain_core.pydantic_v1 import Extra, Field, SecretStr, root_validator
from langchain_core.utils import convert_to_secret_str, get_from_dict_or_env
from .utils import _create_retry_decorator
def enforce_stop_tokens(text: str, stop: List[str]) -> str:
"""Cut off the text as soon as any stop words occur."""
return re.split("|".join(stop), text, maxsplit=1)[0]
logger = logging.getLogger(__name__)
def completion_with_retry(llm: Cohere, **kwargs: Any) -> Any:
"""Use tenacity to retry the completion call."""
retry_decorator = _create_retry_decorator(llm.max_retries)
@retry_decorator
def _completion_with_retry(**kwargs: Any) -> Any:
return llm.client.generate(**kwargs)
return _completion_with_retry(**kwargs)
def acompletion_with_retry(llm: Cohere, **kwargs: Any) -> Any:
"""Use tenacity to retry the completion call."""
retry_decorator = _create_retry_decorator(llm.max_retries)
@retry_decorator
async def _completion_with_retry(**kwargs: Any) -> Any:
return await llm.async_client.generate(**kwargs)
return _completion_with_retry(**kwargs)
class BaseCohere(Serializable):
"""Base class for Cohere models."""
client: Any = None #: :meta private:
async_client: Any = None #: :meta private:
model: Optional[str] = Field(default=None)
"""Model name to use."""
temperature: Optional[float] = None
"""A non-negative float that tunes the degree of randomness in generation."""
cohere_api_key: Optional[SecretStr] = None
"""Cohere API key. If not provided, will be read from the environment variable."""
stop: Optional[List[str]] = None
streaming: bool = Field(default=False)
"""Whether to stream the results."""
user_agent: str = "langchain"
"""Identifier for the application making the request."""
timeout_seconds: Optional[float] = 300
"""Timeout in seconds for the Cohere API request."""
base_url: Optional[str] = None
"""Override the default Cohere API URL."""
@root_validator()
def validate_environment(cls, values: Dict) -> Dict:
"""Validate that api key and python package exists in environment."""
values["cohere_api_key"] = convert_to_secret_str(
get_from_dict_or_env(values, "cohere_api_key", "COHERE_API_KEY")
)
client_name = values["user_agent"]
timeout_seconds = values.get("timeout_seconds")
values["client"] = cohere.Client(
api_key=values["cohere_api_key"].get_secret_value(),
timeout=timeout_seconds,
client_name=client_name,
base_url=values["base_url"],
)
values["async_client"] = cohere.AsyncClient(
api_key=values["cohere_api_key"].get_secret_value(),
client_name=client_name,
timeout=timeout_seconds,
base_url=values["base_url"],
)
return values
class Cohere(LLM, BaseCohere):
"""Cohere large language models.
To use, you should have the ``cohere`` python package installed, and the
environment variable ``COHERE_API_KEY`` set with your API key, or pass
it as a named parameter to the constructor.
Example:
.. code-block:: python
from langchain_cohere import Cohere
cohere = Cohere(cohere_api_key="my-api-key")
"""
max_tokens: Optional[int] = None
"""Denotes the number of tokens to predict per generation."""
k: Optional[int] = None
"""Number of most likely tokens to consider at each step."""
p: Optional[int] = None
"""Total probability mass of tokens to consider at each step."""
frequency_penalty: Optional[float] = None
"""Penalizes repeated tokens according to frequency. Between 0 and 1."""
presence_penalty: Optional[float] = None
"""Penalizes repeated tokens. Between 0 and 1."""
truncate: Optional[str] = None
"""Specify how the client handles inputs longer than the maximum token
length: Truncate from START, END or NONE"""
max_retries: int = 10
"""Maximum number of retries to make when generating."""
class Config:
"""Configuration for this pydantic object."""
arbitrary_types_allowed = True
extra = Extra.forbid
@property
def _default_params(self) -> Dict[str, Any]:
"""Configurable parameters for calling Cohere's generate API."""
base_params = {
"model": self.model,
"temperature": self.temperature,
"max_tokens": self.max_tokens,
"k": self.k,
"p": self.p,
"frequency_penalty": self.frequency_penalty,
"presence_penalty": self.presence_penalty,
"truncate": self.truncate,
}
return {k: v for k, v in base_params.items() if v is not None}
@property
def lc_secrets(self) -> Dict[str, str]:
return {"cohere_api_key": "COHERE_API_KEY"}
@property
def _identifying_params(self) -> Dict[str, Any]:
"""Get the identifying parameters."""
return self._default_params
@property
def _llm_type(self) -> str:
"""Return type of llm."""
return "cohere"
def _invocation_params(self, stop: Optional[List[str]], **kwargs: Any) -> dict:
params = self._default_params
if self.stop is not None and stop is not None:
raise ValueError("`stop` found in both the input and default params.")
elif self.stop is not None:
params["stop_sequences"] = self.stop
else:
params["stop_sequences"] = stop
return {**params, **kwargs}
def _process_response(self, response: Any, stop: Optional[List[str]]) -> str:
text = response.generations[0].text
# If stop tokens are provided, Cohere's endpoint returns them.
# In order to make this consistent with other endpoints, we strip them.
if stop:
text = enforce_stop_tokens(text, stop)
return text
def _call(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> str:
"""Call out to Cohere's generate endpoint.
Args:
prompt: The prompt to pass into the model.
stop: Optional list of stop words to use when generating.
Returns:
The string generated by the model.
Example:
.. code-block:: python
response = cohere("Tell me a joke.")
"""
params = self._invocation_params(stop, **kwargs)
response = completion_with_retry(
self, model=self.model, prompt=prompt, **params
)
_stop = params.get("stop_sequences")
return self._process_response(response, _stop)
async def _acall(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> str:
"""Async call out to Cohere's generate endpoint.
Args:
prompt: The prompt to pass into the model.
stop: Optional list of stop words to use when generating.
Returns:
The string generated by the model.
Example:
.. code-block:: python
response = await cohere("Tell me a joke.")
"""
params = self._invocation_params(stop, **kwargs)
response = await acompletion_with_retry(
self, model=self.model, prompt=prompt, **params
)
_stop = params.get("stop_sequences")
return self._process_response(response, _stop)

View File

@@ -1,102 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Dict, List, Optional
from langchain_core.callbacks import (
AsyncCallbackManagerForRetrieverRun,
CallbackManagerForRetrieverRun,
)
from langchain_core.documents import Document
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import HumanMessage
from langchain_core.pydantic_v1 import Field
from langchain_core.retrievers import BaseRetriever
if TYPE_CHECKING:
from langchain_core.messages import BaseMessage
def _get_docs(response: Any) -> List[Document]:
docs = []
if (
"documents" in response.generation_info
and len(response.generation_info["documents"]) > 0
):
for doc in response.generation_info["documents"]:
content = doc.get("snippet", None) or doc.get("text", None)
if content is not None:
docs.append(Document(page_content=content, metadata=doc))
docs.append(
Document(
page_content=response.message.content,
metadata={
"type": "model_response",
"citations": response.generation_info["citations"],
"search_results": response.generation_info["search_results"],
"search_queries": response.generation_info["search_queries"],
"token_count": response.generation_info["token_count"],
},
)
)
return docs
class CohereRagRetriever(BaseRetriever):
"""Cohere Chat API with RAG."""
connectors: List[Dict] = Field(default_factory=lambda: [{"id": "web-search"}])
"""
When specified, the model's reply will be enriched with information found by
querying each of the connectors (RAG). These will be returned as langchain
documents.
Currently only accepts {"id": "web-search"}.
"""
llm: BaseChatModel
"""Cohere ChatModel to use."""
class Config:
"""Configuration for this pydantic object."""
arbitrary_types_allowed = True
"""Allow arbitrary types."""
def _get_relevant_documents(
self,
query: str,
*,
run_manager: CallbackManagerForRetrieverRun,
documents: Optional[List[Dict[str, str]]] = None,
**kwargs: Any,
) -> List[Document]:
messages: List[List[BaseMessage]] = [[HumanMessage(content=query)]]
res = self.llm.generate(
messages,
connectors=self.connectors if documents is None else None,
documents=documents,
callbacks=run_manager.get_child(),
**kwargs,
).generations[0][0]
return _get_docs(res)
async def _aget_relevant_documents(
self,
query: str,
*,
run_manager: AsyncCallbackManagerForRetrieverRun,
documents: Optional[List[Dict[str, str]]] = None,
**kwargs: Any,
) -> List[Document]:
messages: List[List[BaseMessage]] = [[HumanMessage(content=query)]]
res = (
await self.llm.agenerate(
messages,
connectors=self.connectors if documents is None else None,
documents=documents,
callbacks=run_manager.get_child(),
**kwargs,
)
).generations[0][0]
return _get_docs(res)

View File

@@ -1,137 +0,0 @@
"""
Cohere multi-hop agent enables multiple tools to be used in sequence to complete a
task.
This agent uses a multi hop prompt by Cohere, which is experimental and subject
to change. The latest prompt can be used by upgrading the langchain-cohere package.
"""
from typing import Any, Dict, List, Mapping, Optional, Sequence, Union
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.language_models import BaseLanguageModel
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.runnables import (
Runnable,
RunnableConfig,
RunnableParallel,
RunnablePassthrough,
)
from langchain_core.tools import BaseTool
from langchain_cohere.react_multi_hop.parsing import (
GROUNDED_ANSWER_KEY,
OUTPUT_KEY,
CohereToolsReactAgentOutputParser,
parse_citations,
)
from langchain_cohere.react_multi_hop.prompt import (
convert_to_documents,
multi_hop_prompt,
)
def create_cohere_react_agent(
llm: BaseLanguageModel,
tools: Sequence[BaseTool],
prompt: ChatPromptTemplate,
) -> Runnable:
"""
Create an agent that enables multiple tools to be used in sequence to complete
a task.
Args:
llm: The ChatCohere LLM instance to use.
tools: Tools this agent has access to.
prompt: The prompt to use.
Returns:
A Runnable sequence representing an agent. It takes as input all the same input
variables as the prompt passed in does and returns a List[AgentAction] or a
single AgentFinish.
The AgentFinish will have two fields:
* output: str - The output string generated by the model
* citations: List[CohereCitation] - A list of citations that refer to the
output and observations made by the agent. If there are no citations this
list will be empty.
Example:
. code-block:: python
from langchain.agents import AgentExecutor
from langchain.prompts import ChatPromptTemplate
from langchain_cohere import ChatCohere, create_cohere_react_agent
prompt = ChatPromptTemplate.from_template("{input}")
tools = [] # Populate this with a list of tools you would like to use.
llm = ChatCohere()
agent = create_cohere_react_agent(
llm,
tools,
prompt
)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?",
})
""" # noqa: E501
# Creates a prompt, invokes the model, and produces a
# "Union[List[AgentAction], AgentFinish]"
generate_agent_steps = (
multi_hop_prompt(tools=tools, prompt=prompt)
| llm.bind(stop=["\nObservation:"], raw_prompting=True)
| CohereToolsReactAgentOutputParser()
)
agent = (
RunnablePassthrough.assign(
# agent_scratchpad isn't used in this chain, but added here for
# interoperability with other chains that may require it.
agent_scratchpad=lambda _: [],
)
| RunnableParallel(
chain_input=RunnablePassthrough(), agent_steps=generate_agent_steps
)
| _AddCitations()
)
return agent
class _AddCitations(Runnable):
"""
Adds a list of citations to the output of the Cohere multi hop chain when the
last step is an AgentFinish. Citations are generated from the observations (made
in previous agent steps) and the grounded answer (made in the last step).
"""
def invoke(
self, input: Dict[str, Any], config: Optional[RunnableConfig] = None
) -> Union[List[AgentAction], AgentFinish]:
agent_steps = input.get("agent_steps", [])
if not agent_steps:
# The input wasn't as expected.
return []
if not isinstance(agent_steps, AgentFinish):
# We're not on the AgentFinish step.
return agent_steps
agent_finish = agent_steps
# Build a list of documents from the intermediate_steps used in this chain.
intermediate_steps = input.get("chain_input", {}).get("intermediate_steps", [])
documents: List[Mapping] = []
for _, observation in intermediate_steps:
documents.extend(convert_to_documents(observation))
# Build a list of citations, if any, from the documents + grounded answer.
grounded_answer = agent_finish.return_values.pop(GROUNDED_ANSWER_KEY, "")
output, citations = parse_citations(
grounded_answer=grounded_answer, documents=documents
)
agent_finish.return_values[OUTPUT_KEY] = output
agent_finish.return_values["citations"] = citations
return agent_finish

View File

@@ -1,31 +0,0 @@
from enum import Enum
class _SpecialToken(str, Enum):
bos = "<BOS_TOKEN>"
start_turn = "<|START_OF_TURN_TOKEN|>"
end_turn = "<|END_OF_TURN_TOKEN|>"
role_system = "<|SYSTEM_TOKEN|>"
role_chatbot = "<|CHATBOT_TOKEN|>"
role_user = "<|USER_TOKEN|>"
default_basic_rules = "You are a powerful language agent trained by Cohere to help people. You are capable of complex reasoning and augmented with a number of tools. Your job is to plan and reason about how you will use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see an instruction informing you what kind of response to generate. You will construct a plan and then perform a number of reasoning and action steps to solve the problem. When you have determined the answer to the user's request, you will cite your sources in your answers, according the instructions" # noqa: E501
default_task_context = "You use your advanced complex reasoning capabilities to help people by answering their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You may need to use multiple tools in parallel or sequentially to complete your task. You should focus on serving the user's needs as best you can, which will be wide-ranging. The current date is {now}" # noqa: E501
default_style_guide = "Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling" # noqa: E501
default_safety_rules = "The instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral" # noqa: E501
default_multi_hop_instruction = """Carefully perform the following instructions, in order, starting each with a new line.
Firstly, You may need to use complex and advanced reasoning to complete your task and answer the question. Think about how you can use the provided tools to answer the question and come up with a high level plan you will execute.
Write 'Plan:' followed by an initial high level plan of how you will solve the problem including the tools and steps required.
Secondly, Carry out your plan by repeatedly using actions, reasoning over the results, and re-evaluating your plan. Perform Action, Observation, Reflection steps with the following format. Write 'Action:' followed by a json formatted action containing the "tool_name" and "parameters"
Next you will analyze the 'Observation:', this is the result of the action.
After that you should always think about what to do next. Write 'Reflection:' followed by what you've figured out so far, any changes you need to make to your plan, and what you will do next including if you know the answer to the question.
... (this Action/Observation/Reflection can repeat N times)
Thirdly, Decide which of the retrieved documents are relevant to the user's last input by writing 'Relevant Documents:' followed by comma-separated list of document numbers. If none are relevant, you should instead write 'None'.
Fourthly, Decide which of the retrieved documents contain facts that should be cited in a good answer to the user's last input by writing 'Cited Documents:' followed a comma-separated list of document numbers. If you dont want to cite any of them, you should instead write 'None'.
Fifthly, Write 'Answer:' followed by a response to the user's last input in high quality natural english. Use the retrieved documents to help you. Do not insert any citations or grounding markup.
Finally, Write 'Grounded answer:' followed by a response to the user's last input in high quality natural english. Use the symbols <co: doc> and </co: doc> to indicate when a fact comes from a document in the search result, e.g <co: 4>my fact</co: 4> for a fact from document 4.""" # noqa: E501

View File

@@ -1,303 +0,0 @@
import json
import logging
import re
from typing import Any, Dict, List, Mapping, Tuple, Union
from langchain_core.agents import AgentAction, AgentActionMessageLog, AgentFinish
from langchain_core.messages import AIMessage
from langchain_core.output_parsers import BaseOutputParser
from langchain_cohere import CohereCitation
OUTPUT_KEY = "output"
GROUNDED_ANSWER_KEY = "grounded_answer"
class CohereToolsReactAgentOutputParser(
BaseOutputParser[Union[List[AgentAction], AgentFinish]]
):
"""Parses a message into agent actions/finish."""
def parse(self, text: str) -> Union[List[AgentAction], AgentFinish]:
# Parse the structured output of the final answer.
if "Answer: " in text:
prefix_map = {
"answer": "Answer:",
"grounded_answer": "Grounded answer:",
"relevant_docs": "Relevant Documents:",
"cited_docs": "Cited Documents:",
}
parsed_answer = parse_answer_with_prefixes(text, prefix_map)
return AgentFinish(
return_values={
OUTPUT_KEY: parsed_answer["answer"],
GROUNDED_ANSWER_KEY: parsed_answer["grounded_answer"],
},
log=text,
)
elif any([x in text for x in ["Plan: ", "Reflection: ", "Action: "]]):
completion, plan, actions = parse_actions(text)
agent_actions: List[AgentAction] = []
for i, action in enumerate(actions):
agent_action = AgentActionMessageLog(
tool=action["tool_name"],
tool_input=action["parameters"],
log=f"\n{action}\n" if i > 0 else f"\n{plan}\n{action}\n",
message_log=[AIMessage(content=completion)],
)
agent_actions.append(agent_action)
return agent_actions
else:
raise ValueError(
"\nCould not parse generation as it did not contain Plan, Reflection,"
+ f"Action, or Answer. Input: {text}\n\n"
)
def parse_jsonified_tool_use_generation(
tool_use_generation: str, tool_use_prefix: str
) -> List[Dict]:
"""Parses model-generated jsonified actions.
Expects input of the form
"{tool_use_prefix}: ```json\n[{list of jsonified objects}]```"
outputs parsed list of jsonified objects.
"""
def _extract_codeblocks_from_md(text: str) -> List[str]:
return re.findall(r"`{3}([^`]*)`{0,3}", text)
raw_generation = re.sub(f"^{tool_use_prefix} ", "", tool_use_generation)
code_block_sections = _extract_codeblocks_from_md(raw_generation)
if len(code_block_sections) != 1: # should have exactly 1 code block
raise ValueError(f"Action Parsing Failed: {tool_use_generation}")
# only json allowed:
assert code_block_sections[0].startswith(
"json\n"
), f"Action Parsing Failed: {tool_use_generation}"
actions = json.loads(re.sub("^json\n", "", code_block_sections[0]))
if not isinstance(actions, list):
raise ValueError(f"Action Parsing Failed: {tool_use_generation}")
if len(actions):
if any(
not isinstance(action, Dict) or "tool_name" not in action
for action in actions
):
raise ValueError(f"Action Parsing Failed: {tool_use_generation}")
return actions
def parse_answer_with_prefixes(
completion: str, prefixes: Dict[str, str]
) -> Dict[str, str]:
"""parses string into key-value pairs,
according to patterns supplied in prefixes. Also strips.
if inputs are:
completion = "\nhello: sam\ngoodbye then: paul.",
prefixes = {"greeting": "hello:", "farewell": "goodbye then:"}
the expected returned result is:
{"greeting": "sam", "farewell": "paul."}
Args:
completion (str): text to split
prefixes (Dict[str, str]): a key-value dict of keys and patterns.
See example above
Returns:
Dict[str, str]: parsed result
"""
# sort out prefixes
re_pat = "(" + "|".join([re.escape(p) for p in prefixes.values()]) + ")"
reverse_prefix_map = {v: k for k, v in prefixes.items()}
split = re.split(re_pat, completion)
split = split[1:]
parsed = {}
for prefix, value in zip(split[::2], split[1::2]):
if prefix in reverse_prefix_map: # if the prefix is a match
if (
reverse_prefix_map[prefix] not in parsed
): # first occurrence of a prefix is kept, others discarded
parsed[reverse_prefix_map[prefix]] = value.strip()
return parsed
def parse_actions(generation: str) -> Tuple[str, str, List[Dict]]:
"""Parse action selections from model output."""
plan = ""
actions = generation
try:
if "Plan: " in generation or "Reflection: " in generation:
# Model is trained to output a Plan or Reflection followed by an action.
# Use regex to extract the plan and action.
regex = r"^(Plan|Reflection)\s*\d*\s*:(.*?)(Action\s*\d*\s*:\s*\d*\s*```json\n.*?```)" # noqa: E501
action_match = re.search(regex, generation, re.DOTALL)
if not action_match:
raise ValueError(
f"Failed to parse multihop completion for input: {generation}"
)
plan = action_match.group(2).strip()
actions = action_match.group(3).strip()
else:
# Catch the case where model outputs only an action.
regex = r"^(Action\s*\d*\s*:\s*\d*\s*```json\n.*?```)"
action_match = re.search(regex, generation, re.DOTALL)
if not action_match:
raise ValueError(
f"Failed to parse multihop completion for input: {generation}"
)
actions = action_match.group(1).strip()
except Exception as e:
logging.error(f"Failed to parse multihop completion for input: {generation}")
logging.error(f"Error: {e}")
parsed_actions = parse_jsonified_tool_use_generation(actions, "Action:")
return generation, plan, parsed_actions
def parse_citations(
grounded_answer: str, documents: List[Mapping]
) -> Tuple[str, List[CohereCitation]]:
"""
Parses a grounded_generation (from parse_actions) and documents (from
convert_to_documents) into a (generation, CohereCitation list) tuple.
"""
no_markup_answer, parsed_answer = _parse_answer_spans(grounded_answer)
citations: List[CohereCitation] = []
start = 0
for answer in parsed_answer:
text = answer.get("text", "")
document_indexes = answer.get("cited_docs")
if not document_indexes:
# There were no citations for this piece of text.
start += len(text)
continue
end = start + len(text)
# Look up the cited document by index
cited_documents: List[Mapping] = []
for index in set(document_indexes):
if index >= len(documents):
# The document index doesn't exist
continue
cited_documents.append(documents[index])
citations.append(
CohereCitation(
start=start,
end=end,
text=text,
documents=cited_documents,
)
)
start = end
return no_markup_answer, citations
def _strip_spans(answer: str) -> str:
"""removes any <co> tags from a string, including trailing partial tags
input: "hi my <co>name</co> is <co: 1> patrick</co:3> and <co"
output: "hi my name is patrick and"
Args:
answer (str): string
Returns:
str: same string with co tags removed
"""
answer = re.sub(r"<co(.*?)>|</co(.*?)>", "", answer)
idx = answer.find("<co")
if idx > -1:
answer = answer[:idx]
idx = answer.find("</")
if idx > -1:
answer = answer[:idx]
return answer
def _parse_answer_spans(grounded_answer: str) -> Tuple[str, List[Dict[str, Any]]]:
actual_cites = []
for c in re.findall(r"<co:(.*?)>", grounded_answer):
actual_cites.append(c.strip().split(","))
no_markup_answer = _strip_spans(grounded_answer)
current_idx = 0
parsed_answer: List[Dict[str, Union[str, List[int]]]] = []
cited_docs_set = []
last_entry_is_open_cite = False
parsed_current_cite_document_idxs: List[int] = []
while current_idx < len(grounded_answer):
current_cite = re.search(r"<co: (.*?)>", grounded_answer[current_idx:])
if current_cite:
# previous part
parsed_answer.append(
{
"text": grounded_answer[
current_idx : current_idx + current_cite.start()
]
}
)
current_cite_document_idxs = current_cite.group(1).split(",")
parsed_current_cite_document_idxs = []
for cited_idx in current_cite_document_idxs:
if cited_idx.isdigit():
cited_idx = int(cited_idx.strip())
parsed_current_cite_document_idxs.append(cited_idx)
if cited_idx not in cited_docs_set:
cited_docs_set.append(cited_idx)
current_idx += current_cite.end()
current_cite_close = re.search(
r"</co: " + current_cite.group(1) + ">", grounded_answer[current_idx:]
)
if current_cite_close:
# there might have been issues parsing the ids, so we need to check
# that they are actually ints and available
if len(parsed_current_cite_document_idxs) > 0:
pt = grounded_answer[
current_idx : current_idx + current_cite_close.start()
]
parsed_answer.append(
{"text": pt, "cited_docs": parsed_current_cite_document_idxs}
)
else:
parsed_answer.append(
{
"text": grounded_answer[
current_idx : current_idx + current_cite_close.start()
],
}
)
current_idx += current_cite_close.end()
else:
last_entry_is_open_cite = True
break
else:
break
# don't forget about the last one
if last_entry_is_open_cite:
pt = _strip_spans(grounded_answer[current_idx:])
parsed_answer.append(
{"text": pt, "cited_docs": parsed_current_cite_document_idxs}
)
else:
parsed_answer.append({"text": _strip_spans(grounded_answer[current_idx:])})
return no_markup_answer, parsed_answer

View File

@@ -1,299 +0,0 @@
from __future__ import annotations
from datetime import datetime
from typing import (
Any,
Callable,
Dict,
List,
Mapping,
Optional,
Sequence,
Tuple,
Union,
)
from langchain_core.agents import AgentAction, AgentActionMessageLog
from langchain_core.messages import AIMessage, BaseMessage, SystemMessage
from langchain_core.prompts import (
BasePromptTemplate,
ChatPromptTemplate,
PromptTemplate,
)
from langchain_core.pydantic_v1 import BaseModel
from langchain_core.tools import BaseTool
from langchain_cohere.react_multi_hop.default_prompt_constants import (
_SpecialToken,
default_basic_rules,
default_multi_hop_instruction,
default_safety_rules,
default_style_guide,
default_task_context,
)
from langchain_cohere.utils import (
JSON_TO_PYTHON_TYPES,
_remove_signature_from_tool_description,
)
multi_hop_prompt_partial = PromptTemplate.from_template(
"""{structured_preamble}
## Available Tools
Here is a list of tools that you have available to you:
{tools}{end_turn}{history}{user_prompt}{start_turn}{system_role}{multi_hop_instruction}{end_turn}{steps}"""
).partial(
start_turn=_SpecialToken.start_turn.value,
end_turn=_SpecialToken.end_turn.value,
system_role=_SpecialToken.role_system.value,
multi_hop_instruction=default_multi_hop_instruction,
)
def render_structured_preamble(
preamble: Optional[str] = None,
) -> str:
"""Renders the structured preamble part of the prompt content."""
if preamble is None:
default_preamble = """## Task And Context
{task_and_context}
## Style Guide
{style_guide}"""
preamble = default_preamble.format(
task_and_context=default_task_context.format(
now=datetime.now().strftime("%A, %B %d, %Y %H:%M:%S")
),
style_guide=default_style_guide,
)
structured_preamble_template = """{prompt_start}# Safety Preamble
{safety_rules}
# System Preamble
## Basic Rules
{basic_rules}
# User Preamble
{preamble}"""
return structured_preamble_template.format(
prompt_start=f"{_SpecialToken.bos.value}{_SpecialToken.start_turn.value}{_SpecialToken.role_system.value}",
safety_rules=default_safety_rules,
basic_rules=default_basic_rules,
preamble=preamble,
)
def render_tool(tool: BaseTool) -> str:
"""Renders a tool into prompt content"""
template = """```python
{tool_signature}
\"\"\"{tool_description}{tool_args}
\"\"\"
pass
```"""
return template.format(
tool_signature=render_tool_signature(tool),
tool_description=_remove_signature_from_tool_description(
tool.name, tool.description
),
tool_args=render_tool_args(tool),
)
def render_observations(
observations: Union[List[Mapping[str, str]], List[str], Mapping[str, str], str],
index: int,
) -> Tuple[BaseMessage, int]:
"""Renders the 'output' part of an Agent's intermediate step into prompt content."""
documents = convert_to_documents(observations)
rendered_documents: List[str] = []
document_prompt = """Document: {index}
{fields}"""
for doc in documents:
# Render document fields into Key: value strings.
fields: List[str] = []
for k, v in doc.items():
if k.lower() == "url":
# 'url' is a special key which is always upper case.
k = "URL"
else:
# keys are otherwise transformed into title case.
k = k.title()
fields.append(f"{k}: {v}")
rendered_documents.append(
document_prompt.format(
index=index,
fields="\n".join(fields),
)
)
index += 1
prompt_content = "<results>\n" + "\n\n".join(rendered_documents) + "\n</results>"
return SystemMessage(content=prompt_content), index
def convert_to_documents(
observations: Any,
) -> List[Mapping]:
"""Converts observations into a 'document' dict"""
documents: List[Mapping] = []
if isinstance(observations, str):
# strings are turned into a key/value pair and a key of 'output' is added.
observations = [{"output": observations}]
elif isinstance(observations, Mapping):
# single mappings are transformed into a list to simplify the rest of the code.
observations = [observations]
elif not isinstance(observations, Sequence):
# all other types are turned into a key/value pair within a list
observations = [{"output": observations}]
for doc in observations:
if not isinstance(doc, Mapping):
# types that aren't Mapping are turned into a key/value pair.
doc = {"output": doc}
documents.append(doc)
return documents
def render_intermediate_steps(
intermediate_steps: List[Tuple[AgentAction, Any]],
) -> str:
"""Renders an agent's intermediate steps into prompt content."""
prompt_content = ""
if any(
not isinstance(action, AgentActionMessageLog)
for action, _ in intermediate_steps
):
raise ValueError("all AgentAction steps must implement AgentActionMessageLog")
i = 0
for action, observation in intermediate_steps:
prompt_content += render_messages(action.messages)
observation_message, i = render_observations(observation, i)
prompt_content += render_messages([observation_message])
# Always add an 'open' chatbot turn because prompts for the current turn always end
# with an open turn.
prompt_content += (
f"{_SpecialToken.start_turn.value}{_SpecialToken.role_chatbot.value}"
)
return prompt_content
def multi_hop_prompt(
tools: Sequence[BaseTool], prompt: ChatPromptTemplate
) -> Callable[[Dict], BasePromptTemplate]:
"""The returned function produces a BasePromptTemplate suitable for multi-hop."""
# the directly_answer tool is used internally by the model, but never produces an
# AgentAction, so we only need to add it to the prompt.
tools = list(tools)
tools.insert(0, create_directly_answer_tool())
def inner(x: Dict) -> BasePromptTemplate:
return multi_hop_prompt_partial.partial(
structured_preamble=render_structured_preamble(
preamble=x.get("preamble", None)
),
tools="\n\n".join([render_tool(t) for t in tools]),
user_prompt=render_messages(prompt.invoke(x).to_messages()),
steps=render_intermediate_steps(x["intermediate_steps"]),
history=render_messages(x.get("chat_history", [])),
)
return inner
def render_type(type_: str, is_optional: bool) -> str:
"""
Renders a tool's type into prompt content. Types should be Python types, but JSON
schema is allowed and converted.
"""
python_type = JSON_TO_PYTHON_TYPES.get(type_, type_)
if is_optional:
return f"Optional[{python_type}]"
else:
return python_type
def render_tool_signature(tool: BaseTool) -> str:
"""Renders the signature of a tool into prompt content."""
args = []
for parameter_name, parameter_definition in tool.args.items():
type_ = render_type(
parameter_definition.get("type"), "default" in parameter_definition
)
args.append(f"{parameter_name}: {type_}")
signature = ", ".join(args)
return f"def {tool.name}({signature}) -> List[Dict]:"
def render_tool_args(tool: BaseTool) -> str:
"""Renders the 'Args' section of a tool's prompt content."""
if not tool.args:
return ""
indent = " "
prompt_content = f"\n\n{indent * 4}Args:\n{indent * 8}"
rendered_args = []
for parameter_name, parameter_definition in tool.args.items():
type_ = render_type(
parameter_definition.get("type"), "default" in parameter_definition
)
description = parameter_definition.get("description", "")
rendered_args.append(f"{parameter_name} ({type_}): {description}")
prompt_content += f"\n{indent * 8}".join(rendered_args)
return prompt_content
def create_directly_answer_tool() -> BaseTool:
"""
directly_answer is a special tool that's always presented to the model as an
available tool. The model only ever invokes this whilst answering and no AgentAction
is produced, so it only needs to be added to the prompt.
"""
class DirectlyAnswerTool(BaseTool):
class InputSchema(BaseModel):
pass
name = "directly_answer"
description = "Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history" # noqa: E501
args_schema = InputSchema
@property
def args(self) -> dict:
return {}
def _run(self, *args: Any, **kwargs: Any) -> Any:
raise NotImplementedError()
return DirectlyAnswerTool()
def render_role(message: BaseMessage) -> str:
"""Renders the role of a message into prompt content."""
if isinstance(message, AIMessage):
return _SpecialToken.role_chatbot.value
elif isinstance(message, SystemMessage):
return _SpecialToken.role_system.value
else:
return _SpecialToken.role_user.value
def render_messages(messages: Sequence[BaseMessage]) -> str:
"""Renders one or more BaseMessage implementations into prompt content."""
return "".join(
[
f"{_SpecialToken.start_turn.value}{render_role(message)}{message.content}{_SpecialToken.end_turn.value}"
for message in messages
]
)

View File

@@ -1,108 +0,0 @@
from __future__ import annotations
from copy import deepcopy
from typing import Any, Dict, List, Optional, Sequence, Union
import cohere
from langchain_core.callbacks.manager import Callbacks
from langchain_core.documents import BaseDocumentCompressor, Document
from langchain_core.pydantic_v1 import Extra, root_validator
from langchain_core.utils import get_from_dict_or_env
class CohereRerank(BaseDocumentCompressor):
"""Document compressor that uses `Cohere Rerank API`."""
client: Any = None
"""Cohere client to use for compressing documents."""
top_n: Optional[int] = 3
"""Number of documents to return."""
model: str = "rerank-english-v2.0"
"""Model to use for reranking."""
cohere_api_key: Optional[str] = None
"""Cohere API key. Must be specified directly or via environment variable
COHERE_API_KEY."""
user_agent: str = "langchain"
"""Identifier for the application making the request."""
class Config:
"""Configuration for this pydantic object."""
extra = Extra.forbid
arbitrary_types_allowed = True
@root_validator(pre=True)
def validate_environment(cls, values: Dict) -> Dict:
"""Validate that api key and python package exists in environment."""
if not values.get("client"):
cohere_api_key = get_from_dict_or_env(
values, "cohere_api_key", "COHERE_API_KEY"
)
client_name = values.get("user_agent", "langchain")
values["client"] = cohere.Client(cohere_api_key, client_name=client_name)
return values
def rerank(
self,
documents: Sequence[Union[str, Document, dict]],
query: str,
*,
model: Optional[str] = None,
top_n: Optional[int] = -1,
max_chunks_per_doc: Optional[int] = None,
) -> List[Dict[str, Any]]:
"""Returns an ordered list of documents ordered by their relevance to the provided query.
Args:
query: The query to use for reranking.
documents: A sequence of documents to rerank.
model: The model to use for re-ranking. Default to self.model.
top_n : The number of results to return. If None returns all results.
Defaults to self.top_n.
max_chunks_per_doc : The maximum number of chunks derived from a document.
""" # noqa: E501
if len(documents) == 0: # to avoid empty api call
return []
docs = [
doc.page_content if isinstance(doc, Document) else doc for doc in documents
]
model = model or self.model
top_n = top_n if (top_n is None or top_n > 0) else self.top_n
results = self.client.rerank(
query=query,
documents=docs,
model=model,
top_n=top_n,
max_chunks_per_doc=max_chunks_per_doc,
)
result_dicts = []
for res in results.results:
result_dicts.append(
{"index": res.index, "relevance_score": res.relevance_score}
)
return result_dicts
def compress_documents(
self,
documents: Sequence[Document],
query: str,
callbacks: Optional[Callbacks] = None,
) -> Sequence[Document]:
"""
Compress documents using Cohere's rerank API.
Args:
documents: A sequence of documents to compress.
query: The query to use for compressing the documents.
callbacks: Callbacks to run during the compression process.
Returns:
A sequence of compressed documents.
"""
compressed = []
for res in self.rerank(documents, query):
doc = documents[res["index"]]
doc_copy = Document(doc.page_content, metadata=deepcopy(doc.metadata))
doc_copy.metadata["relevance_score"] = res["relevance_score"]
compressed.append(doc_copy)
return compressed

View File

@@ -1,57 +0,0 @@
from __future__ import annotations
import logging
import re
from typing import Any, Callable
import cohere
from tenacity import (
before_sleep_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_exponential,
)
logger = logging.getLogger(__name__)
JSON_TO_PYTHON_TYPES = {
"string": "str",
"number": "float",
"boolean": "bool",
"integer": "int",
"array": "List",
"object": "Dict",
}
def _create_retry_decorator(max_retries: int) -> Callable[[Any], Any]:
# support v4 and v5
retry_conditions = (
retry_if_exception_type(cohere.error.CohereError)
if hasattr(cohere, "error")
else retry_if_exception_type(Exception)
)
min_seconds = 4
max_seconds = 10
# Wait 2^x * 1 second between each retry starting with
# 4 seconds, then up to 10 seconds, then 10 seconds afterwards
return retry(
reraise=True,
stop=stop_after_attempt(max_retries),
wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds),
retry=retry_conditions,
before_sleep=before_sleep_log(logger, logging.WARNING),
)
def _remove_signature_from_tool_description(name: str, description: str) -> str:
"""
Removes the `{name}{signature} - ` prefix and Args: section from tool description.
The signature is usually present for tools created with the @tool decorator,
whereas the Args: section may be present in function doc blocks.
"""
description = re.sub(rf"^{name}\(.*?\) -(?:> \w+? -)? ", "", description)
description = re.sub(r"(?s)(?:\n?\n\s*?)?Args:.*$", "", description)
return description

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +0,0 @@
[tool.poetry]
name = "langchain-cohere"
version = "0.1.0"
description = "An integration package connecting Cohere and LangChain"
authors = []
readme = "README.md"
repository = "https://github.com/langchain-ai/langchain"
license = "MIT"
[tool.poetry.urls]
"Source Code" = "https://github.com/langchain-ai/langchain/tree/master/libs/partners/cohere"
[tool.poetry.dependencies]
python = ">=3.8.1,<4.0"
langchain-core = "^0.1.32"
cohere = ">=5.1.8,<5.2"
[tool.poetry.group.test]
optional = true
[tool.poetry.group.test.dependencies]
pytest = "^7.3.0"
freezegun = "^1.2.2"
pytest-mock = "^3.10.0"
syrupy = "^4.0.2"
pytest-watcher = "^0.3.4"
pytest-asyncio = "^0.21.1"
langchain-core = { path = "../../core", develop = true }
langchain-community = { path = "../../community", develop = true }
langchain = { path = "../../langchain", develop = true }
[tool.poetry.group.codespell]
optional = true
[tool.poetry.group.codespell.dependencies]
codespell = "^2.2.0"
[tool.poetry.group.test_integration]
optional = true
[tool.poetry.group.test_integration.dependencies]
[tool.poetry.group.lint]
optional = true
[tool.poetry.group.lint.dependencies]
ruff = "^0.1.5"
[tool.poetry.group.typing.dependencies]
mypy = "^0.991"
langchain-core = { path = "../../core", develop = true }
[tool.poetry.group.dev]
optional = true
[tool.poetry.group.dev.dependencies]
langchain-core = { path = "../../core", develop = true }
[tool.ruff]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
]
[tool.mypy]
disallow_untyped_defs = "True"
[tool.coverage.run]
omit = ["tests/*"]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.pytest.ini_options]
# --strict-markers will raise errors on unknown marks.
# https://docs.pytest.org/en/7.1.x/how-to/mark.html#raising-errors-on-unknown-marks
#
# https://docs.pytest.org/en/7.1.x/reference/reference.html
# --strict-config any warnings encountered while parsing the `pytest`
# section of the configuration file raise errors.
#
# https://github.com/tophat/syrupy
# --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite.
addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5"
# Registering custom markers.
# https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers
markers = [
"requires: mark tests as requiring a specific library",
"asyncio: mark tests as requiring asyncio",
"compile: mark placeholder test used to compile integration tests without running them",
]
asyncio_mode = "auto"

View File

@@ -1,17 +0,0 @@
import sys
import traceback
from importlib.machinery import SourceFileLoader
if __name__ == "__main__":
files = sys.argv[1:]
has_failure = False
for file in files:
try:
SourceFileLoader("x", file).load_module()
except Exception:
has_faillure = True
print(file)
traceback.print_exc()
print()
sys.exit(1 if has_failure else 0)

View File

@@ -1,27 +0,0 @@
#!/bin/bash
#
# This script searches for lines starting with "import pydantic" or "from pydantic"
# in tracked files within a Git repository.
#
# Usage: ./scripts/check_pydantic.sh /path/to/repository
# Check if a path argument is provided
if [ $# -ne 1 ]; then
echo "Usage: $0 /path/to/repository"
exit 1
fi
repository_path="$1"
# Search for lines matching the pattern within the specified repository
result=$(git -C "$repository_path" grep -E '^import pydantic|^from pydantic')
# Check if any matching lines were found
if [ -n "$result" ]; then
echo "ERROR: The following lines need to be updated:"
echo "$result"
echo "Please replace the code with an import from langchain_core.pydantic_v1."
echo "For example, replace 'from pydantic import BaseModel'"
echo "with 'from langchain_core.pydantic_v1 import BaseModel'"
exit 1
fi

View File

@@ -1,17 +0,0 @@
#!/bin/bash
set -eu
# Initialize a variable to keep track of errors
errors=0
# make sure not importing from langchain or langchain_experimental
git --no-pager grep '^from langchain\.' . && errors=$((errors+1))
git --no-pager grep '^from langchain_experimental\.' . && errors=$((errors+1))
# Decide on an exit status based on the errors
if [ "$errors" -gt 0 ]; then
exit 1
else
exit 0
fi

View File

@@ -1,76 +0,0 @@
"""
Tests the agent created by langchain_cohere.create_cohere_react_agent
You will need to set:
* COHERE_API_KEY
"""
from typing import Any, Type
from langchain.agents import AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import BaseTool
from langchain_cohere import ChatCohere, create_cohere_react_agent
def test_invoke_multihop_agent() -> None:
llm = ChatCohere(temperature=0.0)
class _InputSchema(BaseModel):
query: str = Field(description="Query to search the internet with")
class InternetSearchTool(BaseTool):
"""Mimic an internet search engine"""
name: str = "internet_search"
description: str = "Returns a list of relevant document snippets for a textual query retrieved from the internet" # noqa: E501
args_schema: Type[BaseModel] = _InputSchema
def _run(self, *args: Any, **kwargs: Any) -> Any:
query = kwargs.get("query", "")
if "sound of music" in query.lower():
return [
{
"URL": "https://www.cnbc.com/2015/05/26/19-famous-companies-that-originally-had-different-names.html", # noqa: E501
"title": "19 famous companies that originally had different names", # noqa: E501
"text": 'Sound of Music made more money during this "best buy" four-day sale than it did in a typical month thus, the store was renamed to Best Buy in 1983.\n4. Apple Computers » Apple, Inc.\nFounded in 1976, the tech giant we know today as Apple was originally named Apple Computers by founders Steve Jobs, Ronald Wayne and Steve Wozniak. In 2007, Jobs announced that the company was dropping the word "Computer" from its name to better reflect their move into a wider field of consumer electronics. "The Mac, iPod, Apple TV and iPhone. Only one of those is a computer.', # noqa: E501
},
{
"URL": "https://en.wikipedia.org/wiki/The_Sound_of_Music_(film)",
"title": "The Sound of Music (film) - Wikipedia",
"text": 'In 1966, American Express created the first Sound of Music guided tour in Salzburg. Since 1972, Panorama Tours has been the leading Sound of Music bus tour company in the city, taking approximately 50,000 tourists a year to various film locations in Salzburg and the surrounding region. Although the Salzburg tourism industry took advantage of the attention from foreign tourists, residents of the city were apathetic about "everything that is dubious about tourism." The guides on the bus tour "seem to have little idea of what really happened on the set." Even the ticket agent for the Sound of Music Dinner Show tried to dissuade Austrians from attending a performance that was intended for American tourists, saying that it "does not have anything to do with the real Austria."', # noqa: E501
},
]
elif "best buy" in query.lower():
return [
{
"URL": "https://en.wikipedia.org/wiki/Best_Buy",
"title": "Best Buy - Wikipedia",
"text": "Concept IV stores included an open layout with products organized by category, cash registers located throughout the store, and slightly smaller stores than Concept III stores. The stores also had large areas for demonstrating home theater systems and computer software.\nIn 1999, Best Buy was added to Standard & Poor's S&P 500.\n2000s\nIn 2000, Best Buy formed Redline Entertainment, an independent music label and action-sports video distributor. The company acquired Magnolia Hi-Fi, Inc., an audio-video retailer located in California, Washington, and Oregon, in December 2000.\nIn January 2001, Best Buy acquired Musicland Stores Corporation, a Minnetonka, Minnesota-based retailer that sold home-entertainment products under the Sam Goody, Suncoast Motion Picture Company, Media Play, and OnCue brands.", # noqa: E501
},
{
"URL": "https://en.wikipedia.org/wiki/Best_Buy",
"title": "Best Buy - Wikipedia",
"text": 'Later that year, Best Buy opened its first superstore in Burnsville, Minnesota. The Burnsville location featured a high-volume, low-price business model, which was borrowed partially from Schulze\'s successful Tornado Sale in 1981. In its first year, the Burnsville store out-performed all other Best Buy stores combined.\nBest Buy was taken public in 1985, and two years later it debuted on the New York Stock Exchange. In 1988, Best Buy was in a price and location war with Detroit-based appliance chain Highland Superstores, and Schulze attempted to sell the company to Circuit City for US$30 million. Circuit City rejected the offer, claiming they could open a store in Minneapolis and "blow them away."', # noqa: E501
},
]
return []
tools = [InternetSearchTool()]
prompt = ChatPromptTemplate.from_template("{input}")
agent = create_cohere_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
actual = agent_executor.invoke(
{
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?", # noqa: E501
}
)
assert "output" in actual
assert "best buy" in actual["output"].lower()
assert "citations" in actual

View File

@@ -1,144 +0,0 @@
"""Test ChatCohere chat model."""
import json
from typing import Any
import pytest
from langchain_core.messages import AIMessage, AIMessageChunk
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_cohere import ChatCohere
def test_stream() -> None:
"""Test streaming tokens from ChatCohere."""
llm = ChatCohere()
for token in llm.stream("I'm Pickle Rick"):
assert isinstance(token.content, str)
async def test_astream() -> None:
"""Test streaming tokens from ChatCohere."""
llm = ChatCohere()
async for token in llm.astream("I'm Pickle Rick"):
assert isinstance(token.content, str)
async def test_abatch() -> None:
"""Test streaming tokens from ChatCohere."""
llm = ChatCohere()
result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"])
for token in result:
assert isinstance(token.content, str)
async def test_abatch_tags() -> None:
"""Test batch tokens from ChatCohere."""
llm = ChatCohere()
result = await llm.abatch(
["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]}
)
for token in result:
assert isinstance(token.content, str)
def test_batch() -> None:
"""Test batch tokens from ChatCohere."""
llm = ChatCohere()
result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"])
for token in result:
assert isinstance(token.content, str)
async def test_ainvoke() -> None:
"""Test invoke tokens from ChatCohere."""
llm = ChatCohere()
result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]})
assert isinstance(result.content, str)
def test_invoke() -> None:
"""Test invoke tokens from ChatCohere."""
llm = ChatCohere()
result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"]))
assert isinstance(result.content, str)
def test_invoke_tool_calls() -> None:
llm = ChatCohere(temperature=0)
class Person(BaseModel):
name: str = Field(type=str, description="The name of the person")
age: int = Field(type=int, description="The age of the person")
tool_llm = llm.bind_tools([Person])
# where it calls the tool
result = tool_llm.invoke("Erick, 27 years old")
assert isinstance(result, AIMessage)
additional_kwargs = result.additional_kwargs
assert "tool_calls" in additional_kwargs
assert len(additional_kwargs["tool_calls"]) == 1
assert additional_kwargs["tool_calls"][0]["function"]["name"] == "Person"
assert json.loads(additional_kwargs["tool_calls"][0]["function"]["arguments"]) == {
"name": "Erick",
"age": 27,
}
def test_streaming_tool_call() -> None:
llm = ChatCohere(temperature=0)
class Person(BaseModel):
name: str = Field(type=str, description="The name of the person")
age: int = Field(type=int, description="The age of the person")
tool_llm = llm.bind_tools([Person])
# where it calls the tool
strm = tool_llm.stream("Erick, 27 years old")
additional_kwargs = None
for chunk in strm:
assert isinstance(chunk, AIMessageChunk)
assert chunk.content == ""
additional_kwargs = chunk.additional_kwargs
assert additional_kwargs is not None
assert "tool_calls" in additional_kwargs
assert len(additional_kwargs["tool_calls"]) == 1
assert additional_kwargs["tool_calls"][0]["function"]["name"] == "Person"
assert json.loads(additional_kwargs["tool_calls"][0]["function"]["arguments"]) == {
"name": "Erick",
"age": 27,
}
@pytest.mark.xfail(
reason="Cohere models return empty output when a tool is passed in but not called."
)
def test_streaming_tool_call_no_tool_calls() -> None:
llm = ChatCohere(temperature=0)
class Person(BaseModel):
name: str = Field(type=str, description="The name of the person")
age: int = Field(type=int, description="The age of the person")
tool_llm = llm.bind_tools([Person])
# where it doesn't call the tool
strm = tool_llm.stream("What is 2+2?")
acc: Any = None
for chunk in strm:
assert isinstance(chunk, AIMessageChunk)
acc = chunk if acc is None else acc + chunk
assert acc.content != ""
assert "tool_calls" not in acc.additional_kwargs

View File

@@ -1,7 +0,0 @@
import pytest
@pytest.mark.compile
def test_placeholder() -> None:
"""Used for compiling integration tests without running any real tests."""
pass

View File

@@ -1,19 +0,0 @@
"""Test Cohere embeddings."""
from langchain_cohere import CohereEmbeddings
def test_langchain_cohere_embedding_documents() -> None:
"""Test cohere embeddings."""
documents = ["foo bar"]
embedding = CohereEmbeddings()
output = embedding.embed_documents(documents)
assert len(output) == 1
assert len(output[0]) > 0
def test_langchain_cohere_embedding_query() -> None:
"""Test cohere embeddings."""
document = "foo bar"
embedding = CohereEmbeddings()
output = embedding.embed_query(document)
assert len(output) > 0

View File

@@ -1,63 +0,0 @@
"""Test ChatCohere chat model."""
from typing import Any, Dict, List
from langchain_core.documents import Document
from langchain_core.messages.human import HumanMessage
from langchain_core.prompts.chat import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import (
RunnablePassthrough,
RunnableSerializable,
)
from langchain_cohere import ChatCohere
def test_connectors() -> None:
"""Test connectors parameter support from ChatCohere."""
llm = ChatCohere().bind(connectors=[{"id": "web-search"}])
result = llm.invoke("Who directed dune two? reply with just the name.")
assert isinstance(result.content, str)
def test_documents() -> None:
"""Test documents paraneter support from ChatCohere."""
docs = [{"text": "The sky is green."}]
llm = ChatCohere().bind(documents=docs)
prompt = "What color is the sky?"
result = llm.invoke(prompt)
assert isinstance(result.content, str)
assert len(result.response_metadata["documents"]) == 1
def test_documents_chain() -> None:
"""Test documents paraneter support from ChatCohere."""
llm = ChatCohere()
def get_documents(_: Any) -> List[Document]:
return [Document(page_content="The sky is green.")]
def format_input_msgs(input: Dict[str, Any]) -> List[HumanMessage]:
return [
HumanMessage(
content=input["message"],
additional_kwargs={
"documents": input.get("documents", None),
},
)
]
prompt = ChatPromptTemplate.from_messages([MessagesPlaceholder("input_msgs")])
chain: RunnableSerializable[Any, Any] = (
{"message": RunnablePassthrough(), "documents": get_documents}
| RunnablePassthrough()
| {"input_msgs": format_input_msgs}
| prompt
| llm
)
result = chain.invoke("What color is the sky?")
assert isinstance(result.content, str)
assert len(result.response_metadata["documents"]) == 1

View File

@@ -1,16 +0,0 @@
"""Test Cohere reranks."""
from langchain_core.documents import Document
from langchain_cohere import CohereRerank
def test_langchain_cohere_rerank_documents() -> None:
"""Test cohere rerank."""
rerank = CohereRerank()
test_documents = [
Document(page_content="This is a test document."),
Document(page_content="Another test document."),
]
test_query = "Test query"
results = rerank.rerank(test_documents, test_query)
assert len(results) == 2

View File

@@ -1,27 +0,0 @@
import os
from enum import Enum
DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data")
class ExpectationType(str, Enum):
prompts = "prompts"
completions = "completions"
def read_expectation_from_file(
expectation_type: ExpectationType, scenario_name: str
) -> str:
"""
Returns an expected prompt or completion from a given scenario name.
Expectations are stored as .txt files make it as easy as possible to read.
"""
with open(
os.path.join(DATA_DIR, expectation_type.value, f"{scenario_name}.txt"), "r"
) as f:
content = f.read()
# Remove a single trailing new line, if present, to aid authoring the txt file.
if content.endswith("\n"):
content = content[: -len("\n")]
return content

View File

@@ -1,72 +0,0 @@
from typing import Any, Dict
from unittest import mock
import pytest
from langchain_core.agents import AgentAction, AgentFinish
from langchain_cohere import CohereCitation
from langchain_cohere.react_multi_hop.agent import _AddCitations
CITATIONS = [CohereCitation(start=1, end=2, text="foo", documents=[{"bar": "baz"}])]
GENERATION = "mocked generation"
@pytest.mark.parametrize(
"invoke_with,expected",
[
pytest.param({}, [], id="no agent_steps or chain_input"),
pytest.param(
{
"chain_input": {"intermediate_steps": []},
"agent_steps": [
AgentAction(
tool="tool_name", tool_input="tool_input", log="tool_log"
)
],
},
[AgentAction(tool="tool_name", tool_input="tool_input", log="tool_log")],
id="not an AgentFinish",
),
pytest.param(
{
"chain_input": {
"intermediate_steps": [
(
AgentAction(
tool="tool_name",
tool_input="tool_input",
log="tool_log",
),
{"tool_output": "output"},
)
]
},
"agent_steps": AgentFinish(
return_values={"output": "output1", "grounded_answer": GENERATION},
log="",
),
},
AgentFinish(
return_values={"output": GENERATION, "citations": CITATIONS}, log=""
),
id="AgentFinish",
),
],
)
@mock.patch(
"langchain_cohere.react_multi_hop.agent.parse_citations",
autospec=True,
return_value=(GENERATION, CITATIONS),
)
def test_add_citations(
parse_citations_mock: Any, invoke_with: Dict[str, Any], expected: Any
) -> None:
chain = _AddCitations()
actual = chain.invoke(invoke_with)
assert expected == actual
if isinstance(expected, AgentFinish):
parse_citations_mock.assert_called_once_with(
grounded_answer=GENERATION, documents=[{"tool_output": "output"}]
)

View File

@@ -1,11 +0,0 @@
Action: ```json
[
{
"tool_name": "tool1",
"parameters": {
"arg1": "value1",
"arg2": 2
}
}
]
```

View File

@@ -1,4 +0,0 @@
Relevant Documents: 0,2,3
Cited Documents: 0,2
Answer: Best Buy, originally called Sound of Music, was added to Standard & Poor's S&P 500 in 1999.
Grounded answer: <co: 0,2>Best Buy</co: 0,2>, originally called Sound of Music, was added to <co: 2>Standard & Poor's S&P 500</co: 2> in <co: 2>1999</co: 2>.

View File

@@ -1,20 +0,0 @@
Plan: Do a thing.
And then do another thing.
Action: ```json
[
{
"tool_name": "tool1",
"parameters": {
"arg1": "value1",
"arg2": 2
}
},
{
"tool_name": "tool2",
"parameters": {
"arg3": "value3",
"arg4": true
}
}
]
```

View File

@@ -1,13 +0,0 @@
Reflection: I found out a thing.
And then do another thing.
Action: ```json
[
{
"tool_name": "tool1",
"parameters": {
"arg1": "value1",
"arg2": 2
}
}
]
```

View File

@@ -1,43 +0,0 @@
<BOS_TOKEN><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|># Safety Preamble
The instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral
# System Preamble
## Basic Rules
You are a powerful language agent trained by Cohere to help people. You are capable of complex reasoning and augmented with a number of tools. Your job is to plan and reason about how you will use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see an instruction informing you what kind of response to generate. You will construct a plan and then perform a number of reasoning and action steps to solve the problem. When you have determined the answer to the user's request, you will cite your sources in your answers, according the instructions
# User Preamble
## Task And Context
You use your advanced complex reasoning capabilities to help people by answering their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You may need to use multiple tools in parallel or sequentially to complete your task. You should focus on serving the user's needs as best you can, which will be wide-ranging. The current date is Saturday, March 30, 2024 13:20:40
## Style Guide
Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling
## Available Tools
Here is a list of tools that you have available to you:
```python
def directly_answer() -> List[Dict]:
"""Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history
"""
pass
```
```python
def internet_search(query: str) -> List[Dict]:
"""Returns a list of relevant document snippets for a textual query retrieved from the internet
Args:
query (str): Query to search the internet with
"""
pass
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|USER_TOKEN|>In what year was the company that was founded as Sound of Music added to the S&P 500?<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Carefully perform the following instructions, in order, starting each with a new line.
Firstly, You may need to use complex and advanced reasoning to complete your task and answer the question. Think about how you can use the provided tools to answer the question and come up with a high level plan you will execute.
Write 'Plan:' followed by an initial high level plan of how you will solve the problem including the tools and steps required.
Secondly, Carry out your plan by repeatedly using actions, reasoning over the results, and re-evaluating your plan. Perform Action, Observation, Reflection steps with the following format. Write 'Action:' followed by a json formatted action containing the "tool_name" and "parameters"
Next you will analyze the 'Observation:', this is the result of the action.
After that you should always think about what to do next. Write 'Reflection:' followed by what you've figured out so far, any changes you need to make to your plan, and what you will do next including if you know the answer to the question.
... (this Action/Observation/Reflection can repeat N times)
Thirdly, Decide which of the retrieved documents are relevant to the user's last input by writing 'Relevant Documents:' followed by comma-separated list of document numbers. If none are relevant, you should instead write 'None'.
Fourthly, Decide which of the retrieved documents contain facts that should be cited in a good answer to the user's last input by writing 'Cited Documents:' followed a comma-separated list of document numbers. If you dont want to cite any of them, you should instead write 'None'.
Fifthly, Write 'Answer:' followed by a response to the user's last input in high quality natural english. Use the retrieved documents to help you. Do not insert any citations or grounding markup.
Finally, Write 'Grounded answer:' followed by a response to the user's last input in high quality natural english. Use the symbols <co: doc> and </co: doc> to indicate when a fact comes from a document in the search result, e.g <co: 4>my fact</co: 4> for a fact from document 4.<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>

View File

@@ -1,65 +0,0 @@
<BOS_TOKEN><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|># Safety Preamble
The instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral
# System Preamble
## Basic Rules
You are a powerful language agent trained by Cohere to help people. You are capable of complex reasoning and augmented with a number of tools. Your job is to plan and reason about how you will use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see an instruction informing you what kind of response to generate. You will construct a plan and then perform a number of reasoning and action steps to solve the problem. When you have determined the answer to the user's request, you will cite your sources in your answers, according the instructions
# User Preamble
## Task And Context
You use your advanced complex reasoning capabilities to help people by answering their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You may need to use multiple tools in parallel or sequentially to complete your task. You should focus on serving the user's needs as best you can, which will be wide-ranging. The current date is Saturday, March 30, 2024 13:20:40
## Style Guide
Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling
## Available Tools
Here is a list of tools that you have available to you:
```python
def directly_answer() -> List[Dict]:
"""Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history
"""
pass
```
```python
def internet_search(query: str) -> List[Dict]:
"""Returns a list of relevant document snippets for a textual query retrieved from the internet
Args:
query (str): Query to search the internet with
"""
pass
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|USER_TOKEN|>In what year was the company that was founded as Sound of Music added to the S&P 500?<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Carefully perform the following instructions, in order, starting each with a new line.
Firstly, You may need to use complex and advanced reasoning to complete your task and answer the question. Think about how you can use the provided tools to answer the question and come up with a high level plan you will execute.
Write 'Plan:' followed by an initial high level plan of how you will solve the problem including the tools and steps required.
Secondly, Carry out your plan by repeatedly using actions, reasoning over the results, and re-evaluating your plan. Perform Action, Observation, Reflection steps with the following format. Write 'Action:' followed by a json formatted action containing the "tool_name" and "parameters"
Next you will analyze the 'Observation:', this is the result of the action.
After that you should always think about what to do next. Write 'Reflection:' followed by what you've figured out so far, any changes you need to make to your plan, and what you will do next including if you know the answer to the question.
... (this Action/Observation/Reflection can repeat N times)
Thirdly, Decide which of the retrieved documents are relevant to the user's last input by writing 'Relevant Documents:' followed by comma-separated list of document numbers. If none are relevant, you should instead write 'None'.
Fourthly, Decide which of the retrieved documents contain facts that should be cited in a good answer to the user's last input by writing 'Cited Documents:' followed a comma-separated list of document numbers. If you dont want to cite any of them, you should instead write 'None'.
Fifthly, Write 'Answer:' followed by a response to the user's last input in high quality natural english. Use the retrieved documents to help you. Do not insert any citations or grounding markup.
Finally, Write 'Grounded answer:' followed by a response to the user's last input in high quality natural english. Use the symbols <co: doc> and </co: doc> to indicate when a fact comes from a document in the search result, e.g <co: 4>my fact</co: 4> for a fact from document 4.<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>Plan: First, I need to find out which company was originally called Sound of Music, then I need to find out when it was added to the S&P 500.
Action: ```json
[
{
"tool_name": "internet_search",
"parameters": {
"query": "which company was originally called sound of music"
}
}
]
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|><results>
Document: 0
URL: https://www.cnbc.com/2015/05/26/19-famous-companies-that-originally-had-different-names.html
Title: 19 famous companies that originally had different names
Text: Sound of Music made more money during this "best buy" four-day sale than it did in a typical month thus, the store was renamed to Best Buy in 1983.
4. Apple Computers » Apple, Inc.
Founded in 1976, the tech giant we know today as Apple was originally named Apple Computers by founders Steve Jobs, Ronald Wayne and Steve Wozniak. In 2007, Jobs announced that the company was dropping the word "Computer" from its name to better reflect their move into a wider field of consumer electronics. "The Mac, iPod, Apple TV and iPhone. Only one of those is a computer.
Document: 1
URL: https://en.wikipedia.org/wiki/The_Sound_of_Music_(film)
Title: The Sound of Music (film) - Wikipedia
Text: In 1966, American Express created the first Sound of Music guided tour in Salzburg. Since 1972, Panorama Tours has been the leading Sound of Music bus tour company in the city, taking approximately 50,000 tourists a year to various film locations in Salzburg and the surrounding region. Although the Salzburg tourism industry took advantage of the attention from foreign tourists, residents of the city were apathetic about "everything that is dubious about tourism." The guides on the bus tour "seem to have little idea of what really happened on the set." Even the ticket agent for the Sound of Music Dinner Show tried to dissuade Austrians from attending a performance that was intended for American tourists, saying that it "does not have anything to do with the real Austria."
</results><|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>

View File

@@ -1,90 +0,0 @@
<BOS_TOKEN><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|># Safety Preamble
The instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral
# System Preamble
## Basic Rules
You are a powerful language agent trained by Cohere to help people. You are capable of complex reasoning and augmented with a number of tools. Your job is to plan and reason about how you will use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see an instruction informing you what kind of response to generate. You will construct a plan and then perform a number of reasoning and action steps to solve the problem. When you have determined the answer to the user's request, you will cite your sources in your answers, according the instructions
# User Preamble
## Task And Context
You use your advanced complex reasoning capabilities to help people by answering their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You may need to use multiple tools in parallel or sequentially to complete your task. You should focus on serving the user's needs as best you can, which will be wide-ranging. The current date is Saturday, March 30, 2024 13:20:40
## Style Guide
Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling
## Available Tools
Here is a list of tools that you have available to you:
```python
def directly_answer() -> List[Dict]:
"""Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history
"""
pass
```
```python
def internet_search(query: str) -> List[Dict]:
"""Returns a list of relevant document snippets for a textual query retrieved from the internet
Args:
query (str): Query to search the internet with
"""
pass
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|USER_TOKEN|>In what year was the company that was founded as Sound of Music added to the S&P 500?<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Carefully perform the following instructions, in order, starting each with a new line.
Firstly, You may need to use complex and advanced reasoning to complete your task and answer the question. Think about how you can use the provided tools to answer the question and come up with a high level plan you will execute.
Write 'Plan:' followed by an initial high level plan of how you will solve the problem including the tools and steps required.
Secondly, Carry out your plan by repeatedly using actions, reasoning over the results, and re-evaluating your plan. Perform Action, Observation, Reflection steps with the following format. Write 'Action:' followed by a json formatted action containing the "tool_name" and "parameters"
Next you will analyze the 'Observation:', this is the result of the action.
After that you should always think about what to do next. Write 'Reflection:' followed by what you've figured out so far, any changes you need to make to your plan, and what you will do next including if you know the answer to the question.
... (this Action/Observation/Reflection can repeat N times)
Thirdly, Decide which of the retrieved documents are relevant to the user's last input by writing 'Relevant Documents:' followed by comma-separated list of document numbers. If none are relevant, you should instead write 'None'.
Fourthly, Decide which of the retrieved documents contain facts that should be cited in a good answer to the user's last input by writing 'Cited Documents:' followed a comma-separated list of document numbers. If you dont want to cite any of them, you should instead write 'None'.
Fifthly, Write 'Answer:' followed by a response to the user's last input in high quality natural english. Use the retrieved documents to help you. Do not insert any citations or grounding markup.
Finally, Write 'Grounded answer:' followed by a response to the user's last input in high quality natural english. Use the symbols <co: doc> and </co: doc> to indicate when a fact comes from a document in the search result, e.g <co: 4>my fact</co: 4> for a fact from document 4.<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>Plan: First, I need to find out which company was originally called Sound of Music, then I need to find out when it was added to the S&P 500.
Action: ```json
[
{
"tool_name": "internet_search",
"parameters": {
"query": "which company was originally called sound of music"
}
}
]
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|><results>
Document: 0
URL: https://www.cnbc.com/2015/05/26/19-famous-companies-that-originally-had-different-names.html
Title: 19 famous companies that originally had different names
Text: Sound of Music made more money during this "best buy" four-day sale than it did in a typical month thus, the store was renamed to Best Buy in 1983.
4. Apple Computers » Apple, Inc.
Founded in 1976, the tech giant we know today as Apple was originally named Apple Computers by founders Steve Jobs, Ronald Wayne and Steve Wozniak. In 2007, Jobs announced that the company was dropping the word "Computer" from its name to better reflect their move into a wider field of consumer electronics. "The Mac, iPod, Apple TV and iPhone. Only one of those is a computer.
Document: 1
URL: https://en.wikipedia.org/wiki/The_Sound_of_Music_(film)
Title: The Sound of Music (film) - Wikipedia
Text: In 1966, American Express created the first Sound of Music guided tour in Salzburg. Since 1972, Panorama Tours has been the leading Sound of Music bus tour company in the city, taking approximately 50,000 tourists a year to various film locations in Salzburg and the surrounding region. Although the Salzburg tourism industry took advantage of the attention from foreign tourists, residents of the city were apathetic about "everything that is dubious about tourism." The guides on the bus tour "seem to have little idea of what really happened on the set." Even the ticket agent for the Sound of Music Dinner Show tried to dissuade Austrians from attending a performance that was intended for American tourists, saying that it "does not have anything to do with the real Austria."
</results><|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>Reflection: I found out that Sound of Music was renamed Best Buy in 1983, now I need to find out when Best Buy was added to the S&P 500.
Action: ```json
[
{
"tool_name": "internet_search",
"parameters": {
"query": "when was best buy added to S&P 500"
}
}
]
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|><results>
Document: 2
URL: https://en.wikipedia.org/wiki/Best_Buy
Title: Best Buy - Wikipedia
Text: Concept IV stores included an open layout with products organized by category, cash registers located throughout the store, and slightly smaller stores than Concept III stores. The stores also had large areas for demonstrating home theater systems and computer software.
In 1999, Best Buy was added to Standard & Poor's S&P 500.
2000s
In 2000, Best Buy formed Redline Entertainment, an independent music label and action-sports video distributor. The company acquired Magnolia Hi-Fi, Inc., an audio-video retailer located in California, Washington, and Oregon, in December 2000.
In January 2001, Best Buy acquired Musicland Stores Corporation, a Minnetonka, Minnesota-based retailer that sold home-entertainment products under the Sam Goody, Suncoast Motion Picture Company, Media Play, and OnCue brands.
Document: 3
URL: https://en.wikipedia.org/wiki/Best_Buy
Title: Best Buy - Wikipedia
Text: Later that year, Best Buy opened its first superstore in Burnsville, Minnesota. The Burnsville location featured a high-volume, low-price business model, which was borrowed partially from Schulze's successful Tornado Sale in 1981. In its first year, the Burnsville store out-performed all other Best Buy stores combined.
Best Buy was taken public in 1985, and two years later it debuted on the New York Stock Exchange. In 1988, Best Buy was in a price and location war with Detroit-based appliance chain Highland Superstores, and Schulze attempted to sell the company to Circuit City for US$30 million. Circuit City rejected the offer, claiming they could open a store in Minneapolis and "blow them away."
</results><|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>

View File

@@ -1,43 +0,0 @@
<BOS_TOKEN><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|># Safety Preamble
The instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral
# System Preamble
## Basic Rules
You are a powerful language agent trained by Cohere to help people. You are capable of complex reasoning and augmented with a number of tools. Your job is to plan and reason about how you will use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see an instruction informing you what kind of response to generate. You will construct a plan and then perform a number of reasoning and action steps to solve the problem. When you have determined the answer to the user's request, you will cite your sources in your answers, according the instructions
# User Preamble
## Task And Context
You use your advanced complex reasoning capabilities to help people by answering their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You may need to use multiple tools in parallel or sequentially to complete your task. You should focus on serving the user's needs as best you can, which will be wide-ranging. The current date is Saturday, March 30, 2024 13:20:40
## Style Guide
Unless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling
## Available Tools
Here is a list of tools that you have available to you:
```python
def directly_answer() -> List[Dict]:
"""Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history
"""
pass
```
```python
def internet_search(query: str) -> List[Dict]:
"""Returns a list of relevant document snippets for a textual query retrieved from the internet
Args:
query (str): Query to search the internet with
"""
pass
```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|USER_TOKEN|>Hello, how are you doing?<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>I'm doing well, thanks!<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|USER_TOKEN|>In what year was the company that was founded as Sound of Music added to the S&P 500?<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Carefully perform the following instructions, in order, starting each with a new line.
Firstly, You may need to use complex and advanced reasoning to complete your task and answer the question. Think about how you can use the provided tools to answer the question and come up with a high level plan you will execute.
Write 'Plan:' followed by an initial high level plan of how you will solve the problem including the tools and steps required.
Secondly, Carry out your plan by repeatedly using actions, reasoning over the results, and re-evaluating your plan. Perform Action, Observation, Reflection steps with the following format. Write 'Action:' followed by a json formatted action containing the "tool_name" and "parameters"
Next you will analyze the 'Observation:', this is the result of the action.
After that you should always think about what to do next. Write 'Reflection:' followed by what you've figured out so far, any changes you need to make to your plan, and what you will do next including if you know the answer to the question.
... (this Action/Observation/Reflection can repeat N times)
Thirdly, Decide which of the retrieved documents are relevant to the user's last input by writing 'Relevant Documents:' followed by comma-separated list of document numbers. If none are relevant, you should instead write 'None'.
Fourthly, Decide which of the retrieved documents contain facts that should be cited in a good answer to the user's last input by writing 'Cited Documents:' followed a comma-separated list of document numbers. If you dont want to cite any of them, you should instead write 'None'.
Fifthly, Write 'Answer:' followed by a response to the user's last input in high quality natural english. Use the retrieved documents to help you. Do not insert any citations or grounding markup.
Finally, Write 'Grounded answer:' followed by a response to the user's last input in high quality natural english. Use the symbols <co: doc> and </co: doc> to indicate when a fact comes from a document in the search result, e.g <co: 4>my fact</co: 4> for a fact from document 4.<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>

View File

@@ -1,65 +0,0 @@
from typing import Any, Dict, List
from unittest import mock
import pytest
from langchain_core.agents import AgentActionMessageLog, AgentFinish
from langchain_core.messages import AIMessage
from langchain_cohere.react_multi_hop.parsing import CohereToolsReactAgentOutputParser
from tests.unit_tests.react_multi_hop import ExpectationType, read_expectation_from_file
@pytest.mark.parametrize(
"scenario_name,expected",
[
pytest.param(
"answer_sound_of_music",
AgentFinish(
return_values={
"output": "Best Buy, originally called Sound of Music, was added to Standard & Poor's S&P 500 in 1999.", # noqa: E501
"grounded_answer": "<co: 0,2>Best Buy</co: 0,2>, originally called Sound of Music, was added to <co: 2>Standard & Poor's S&P 500</co: 2> in <co: 2>1999</co: 2>.", # noqa: E501
},
log="Relevant Documents: 0,2,3\nCited Documents: 0,2\nAnswer: Best Buy, originally called Sound of Music, was added to Standard & Poor's S&P 500 in 1999.\nGrounded answer: <co: 0,2>Best Buy</co: 0,2>, originally called Sound of Music, was added to <co: 2>Standard & Poor's S&P 500</co: 2> in <co: 2>1999</co: 2>.", # noqa: E501
),
id="best buy example",
)
],
)
def test_it_parses_answer(scenario_name: str, expected: AgentFinish) -> None:
text = read_expectation_from_file(ExpectationType.completions, scenario_name)
actual = CohereToolsReactAgentOutputParser().parse(text)
assert expected == actual
@mock.patch("langchain_cohere.react_multi_hop.parsing.parse_actions", autospec=True)
def test_it_returns_parses_action(parse_actions_mock: mock.Mock) -> None:
# The actual parsing is mocked and tested elsewhere
text = "Reflection: mocked"
generation = "mocked generation"
plan = "mocked plan"
parser = CohereToolsReactAgentOutputParser()
parsed_actions: List[Dict[str, Any]] = [
{"tool_name": "tool1", "parameters": {"param1": "value1"}},
{"tool_name": "tool2", "parameters": {"param2": "value2"}},
]
parse_actions_mock.return_value = (generation, plan, parsed_actions)
expected = [
AgentActionMessageLog(
tool=parsed_actions[0]["tool_name"],
tool_input=parsed_actions[0]["parameters"],
log=f"\n{plan}\n{str(parsed_actions[0])}\n",
message_log=[AIMessage(content=generation)],
),
AgentActionMessageLog(
tool=parsed_actions[1]["tool_name"],
tool_input=parsed_actions[1]["parameters"],
log=f"\n{str(parsed_actions[1])}\n",
message_log=[AIMessage(content=generation)],
),
]
actual = parser.parse(text)
parse_actions_mock.assert_called_once_with(text)
assert expected == actual

View File

@@ -1,67 +0,0 @@
from typing import Any, Dict, List, Optional
import pytest
from langchain_cohere.react_multi_hop.parsing import parse_actions
from tests.unit_tests.react_multi_hop import ExpectationType, read_expectation_from_file
@pytest.mark.parametrize(
"scenario_name, expected_plan, expected_actions, expected_error",
[
pytest.param(
"plan_with_action_normal",
"Do a thing.\nAnd then do another thing.",
[
{"parameters": {"arg1": "value1", "arg2": 2}, "tool_name": "tool1"},
{"parameters": {"arg3": "value3", "arg4": True}, "tool_name": "tool2"},
],
None,
id="plan with action (normal)",
),
pytest.param(
"reflection_with_action_normal",
"I found out a thing.\nAnd then do another thing.",
[
{"parameters": {"arg1": "value1", "arg2": 2}, "tool_name": "tool1"},
],
None,
id="plan with reflection (normal)",
),
pytest.param(
"action_only_abnormal",
"",
[
{"parameters": {"arg1": "value1", "arg2": 2}, "tool_name": "tool1"},
],
None,
id="action only (abnormal)",
),
pytest.param(
"not_a_plan_reflection_or_action",
"",
[],
ValueError,
id="invalid generation (abnormal)",
),
],
)
def test_parse_actions(
scenario_name: str,
expected_plan: str,
expected_actions: List[Dict],
expected_error: Optional[Any],
) -> None:
completion = read_expectation_from_file(ExpectationType.completions, scenario_name)
if expected_error:
with pytest.raises(expected_error):
parse_actions(generation=completion)
else:
actual_completion, actual_plan, actual_actions = parse_actions(
generation=completion
)
assert completion == actual_completion
assert expected_plan == actual_plan
assert expected_actions == actual_actions

View File

@@ -1,86 +0,0 @@
from typing import List, Mapping
import pytest
from langchain_cohere import CohereCitation
from langchain_cohere.react_multi_hop.parsing import parse_citations
DOCUMENTS = [{"foo": "bar"}, {"baz": "foobar"}]
@pytest.mark.parametrize(
"text,documents,expected_generation,expected_citations",
[
pytest.param(
"no citations",
DOCUMENTS,
"no citations",
[],
id="no citations",
),
pytest.param(
"with <co: 0>one citation</co: 0>.",
DOCUMENTS,
"with one citation.",
[
CohereCitation(
start=5, end=17, text="one citation", documents=[DOCUMENTS[0]]
)
],
id="one citation (normal)",
),
pytest.param(
"with <co: 0,1>two documents</co: 0,1>.",
DOCUMENTS,
"with two documents.",
[
CohereCitation(
start=5,
end=18,
text="two documents",
documents=[DOCUMENTS[0], DOCUMENTS[1]],
)
],
id="two cited documents (normal)",
),
pytest.param(
"with <co: 0>two</co: 0> <co: 1>citations</co: 1>.",
DOCUMENTS,
"with two citations.",
[
CohereCitation(start=5, end=8, text="two", documents=[DOCUMENTS[0]]),
CohereCitation(
start=9, end=18, text="citations", documents=[DOCUMENTS[1]]
),
],
id="more than one citation (normal)",
),
pytest.param(
"with <co: 2>incorrect citation</co: 2>.",
DOCUMENTS,
"with incorrect citation.",
[
CohereCitation(
start=5,
end=23,
text="incorrect citation",
documents=[], # note no documents.
)
],
id="cited document doesn't exist (abnormal)",
),
],
)
def test_parse_citations(
text: str,
documents: List[Mapping],
expected_generation: str,
expected_citations: List[CohereCitation],
) -> None:
actual_generation, actual_citations = parse_citations(
grounded_answer=text, documents=documents
)
assert expected_generation == actual_generation
assert expected_citations == actual_citations
for citation in actual_citations:
assert text[citation.start : citation.end]

View File

@@ -1,182 +0,0 @@
from typing import Any, Dict, List, Tuple, Type
import pytest
from freezegun import freeze_time
from langchain_core.agents import AgentAction, AgentActionMessageLog
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.prompt_values import StringPromptValue
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import BaseTool
from langchain_cohere.react_multi_hop.prompt import multi_hop_prompt
from tests.unit_tests.react_multi_hop import ExpectationType, read_expectation_from_file
class InternetSearchTool(BaseTool):
class _InputSchema(BaseModel):
query: str = Field(type=str, description="Query to search the internet with")
name = "internet_search"
description = (
"Returns a list of relevant document snippets for a textual query "
"retrieved from the internet"
)
args_schema: Type[_InputSchema] = _InputSchema
def _run(self, *args: Any, **kwargs: Any) -> Any:
pass
TOOLS: List[BaseTool] = [InternetSearchTool()] # type: ignore
DOCUMENTS = [
{
"URL": "https://www.cnbc.com/2015/05/26/19-famous-companies-that-originally-had-different-names.html",
"title": "19 famous companies that originally had different names",
"text": 'Sound of Music made more money during this "best buy" four-day sale than it did in a typical month thus, the store was renamed to Best Buy in 1983.\n4. Apple Computers » Apple, Inc.\nFounded in 1976, the tech giant we know today as Apple was originally named Apple Computers by founders Steve Jobs, Ronald Wayne and Steve Wozniak. In 2007, Jobs announced that the company was dropping the word "Computer" from its name to better reflect their move into a wider field of consumer electronics. "The Mac, iPod, Apple TV and iPhone. Only one of those is a computer.', # noqa: E501
},
{
"URL": "https://en.wikipedia.org/wiki/The_Sound_of_Music_(film)",
"title": "The Sound of Music (film) - Wikipedia",
"text": 'In 1966, American Express created the first Sound of Music guided tour in Salzburg. Since 1972, Panorama Tours has been the leading Sound of Music bus tour company in the city, taking approximately 50,000 tourists a year to various film locations in Salzburg and the surrounding region. Although the Salzburg tourism industry took advantage of the attention from foreign tourists, residents of the city were apathetic about "everything that is dubious about tourism." The guides on the bus tour "seem to have little idea of what really happened on the set." Even the ticket agent for the Sound of Music Dinner Show tried to dissuade Austrians from attending a performance that was intended for American tourists, saying that it "does not have anything to do with the real Austria."', # noqa: E501
},
{
"URL": "https://en.wikipedia.org/wiki/Best_Buy",
"title": "Best Buy - Wikipedia",
"text": "Concept IV stores included an open layout with products organized by category, cash registers located throughout the store, and slightly smaller stores than Concept III stores. The stores also had large areas for demonstrating home theater systems and computer software.\nIn 1999, Best Buy was added to Standard & Poor's S&P 500.\n2000s\nIn 2000, Best Buy formed Redline Entertainment, an independent music label and action-sports video distributor. The company acquired Magnolia Hi-Fi, Inc., an audio-video retailer located in California, Washington, and Oregon, in December 2000.\nIn January 2001, Best Buy acquired Musicland Stores Corporation, a Minnetonka, Minnesota-based retailer that sold home-entertainment products under the Sam Goody, Suncoast Motion Picture Company, Media Play, and OnCue brands.", # noqa: E501
},
{
"URL": "https://en.wikipedia.org/wiki/Best_Buy",
"title": "Best Buy - Wikipedia",
"text": 'Later that year, Best Buy opened its first superstore in Burnsville, Minnesota. The Burnsville location featured a high-volume, low-price business model, which was borrowed partially from Schulze\'s successful Tornado Sale in 1981. In its first year, the Burnsville store out-performed all other Best Buy stores combined.\nBest Buy was taken public in 1985, and two years later it debuted on the New York Stock Exchange. In 1988, Best Buy was in a price and location war with Detroit-based appliance chain Highland Superstores, and Schulze attempted to sell the company to Circuit City for US$30 million. Circuit City rejected the offer, claiming they could open a store in Minneapolis and "blow them away."', # noqa: E501
},
]
COMPLETIONS = [
"""Plan: First, I need to find out which company was originally called Sound of Music, then I need to find out when it was added to the S&P 500.
Action: ```json
[
{
"tool_name": "internet_search",
"parameters": {
"query": "which company was originally called sound of music"
}
}
]
```""", # noqa: E501
"""Reflection: I found out that Sound of Music was renamed Best Buy in 1983, now I need to find out when Best Buy was added to the S&P 500.
Action: ```json
[
{
"tool_name": "internet_search",
"parameters": {
"query": "when was best buy added to S&P 500"
}
}
]
```""", # noqa: E501,
]
MESSAGES = [
HumanMessage(content="Hello, how are you doing?"),
AIMessage(content="I'm doing well, thanks!"),
HumanMessage(
content="In what year was the company that was founded as Sound of Music added to the S&P 500?" # noqa: E501
),
]
@freeze_time("Saturday, March 30, 2024 13:20:40")
@pytest.mark.parametrize(
"tools,template,invoke_with,intermediate_steps,scenario_name",
[
pytest.param(
[TOOLS[0]],
ChatPromptTemplate.from_template("{input}"),
{
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?" # noqa: E501
},
[],
"base",
id="base",
),
pytest.param(
[TOOLS[0]],
ChatPromptTemplate.from_template("{input}"),
{
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?" # noqa: E501
},
[
(
AgentActionMessageLog(
tool=TOOLS[0].name,
tool_input={
"query": "which company was originally called sound of music" # noqa: E501
},
log="\nFirst I will search for the company founded as Sound of Music. Then I will search for the year this company was added to the S&P 500.{'tool_name': 'internet_search', 'parameters': {'query': 'company founded as Sound of Music'}}\n", # noqa: E501
message_log=[AIMessage(content=COMPLETIONS[0])],
),
[DOCUMENTS[0], DOCUMENTS[1]],
),
],
"base_after_one_hop",
id="after one hop",
),
pytest.param(
[TOOLS[0]],
ChatPromptTemplate.from_template("{input}"),
{
"input": "In what year was the company that was founded as Sound of Music added to the S&P 500?" # noqa: E501
},
[
(
AgentActionMessageLog(
tool=TOOLS[0].name,
tool_input={
"query": "which company was originally called sound of music" # noqa: E501
},
log="\nFirst I will search for the company founded as Sound of Music. Then I will search for the year this company was added to the S&P 500.{'tool_name': 'internet_search', 'parameters': {'query': 'company founded as Sound of Music'}}\n", # noqa: E501
message_log=[AIMessage(content=COMPLETIONS[0])],
),
[DOCUMENTS[0], DOCUMENTS[1]],
),
(
AgentActionMessageLog(
tool=TOOLS[0].name,
tool_input={"query": "when was best buy added to S&P 500"},
log="\nI found out that Sound of Music was renamed Best Buy in 1983, now I need to find out when Best Buy was added to the S&P 500.\n{'tool_name': 'internet_search', 'parameters': {'query': 'when was best buy added to S&P 500'}}\n", # noqa: E501
message_log=[AIMessage(content=COMPLETIONS[1])],
),
[DOCUMENTS[2], DOCUMENTS[3]],
),
],
"base_after_two_hops",
id="after two hops",
),
pytest.param(
[TOOLS[0]],
ChatPromptTemplate.from_messages([MESSAGES[0], MESSAGES[1], MESSAGES[2]]),
{},
[],
"base_with_chat_history",
id="base with chat history",
),
],
)
def test_multihop_prompt(
tools: List[BaseTool],
template: ChatPromptTemplate,
invoke_with: Dict[str, Any],
intermediate_steps: List[Tuple[AgentAction, Any]],
scenario_name: str,
) -> None:
"""Tests prompt rendering against hardcoded expectations."""
expected = read_expectation_from_file(ExpectationType.prompts, scenario_name)
chain = RunnablePassthrough.assign(
agent_scratchpad=lambda _: [], # Usually provided by create_cohere_react_agent.
intermediate_steps=lambda _: intermediate_steps,
) | multi_hop_prompt(tools=tools, prompt=template)
actual = chain.invoke(invoke_with) # type: StringPromptValue # type: ignore
assert StringPromptValue == type(actual)
assert expected == actual.text

View File

@@ -1,82 +0,0 @@
from typing import Any
import pytest
from langchain_core.messages import SystemMessage
from langchain_cohere.react_multi_hop.prompt import render_observations
def test_render_observation_has_correct_indexes() -> None:
index = 13
observations = ["foo", "bar"]
expected_index = 15
_, actual = render_observations(observations=observations, index=index)
assert expected_index == actual
document_template = """Document: {index}
{fields}"""
@pytest.mark.parametrize(
"observation,expected_content",
[
pytest.param(
"foo", document_template.format(index=0, fields="Output: foo"), id="string"
),
pytest.param(
{"foo": "bar"},
document_template.format(index=0, fields="Foo: bar"),
id="dictionary",
),
pytest.param(
{"url": "foo"},
document_template.format(index=0, fields="URL: foo"),
id="dictionary with url",
),
pytest.param(
{"foo": "bar", "baz": "foobar"},
document_template.format(index=0, fields="Foo: bar\nBaz: foobar"),
id="dictionary with multiple keys",
),
pytest.param(
["foo", "bar"],
"\n\n".join(
[
document_template.format(index=0, fields="Output: foo"),
document_template.format(index=1, fields="Output: bar"),
]
),
id="list of strings",
),
pytest.param(
[{"foo": "bar"}, {"baz": "foobar"}],
"\n\n".join(
[
document_template.format(index=0, fields="Foo: bar"),
document_template.format(index=1, fields="Baz: foobar"),
]
),
id="list of dictionaries",
),
pytest.param(
2,
document_template.format(index=0, fields="Output: 2"),
id="int",
),
pytest.param(
[2],
document_template.format(index=0, fields="Output: 2"),
id="list of int",
),
],
)
def test_render_observation_has_correct_content(
observation: Any, expected_content: str
) -> None:
actual, _ = render_observations(observations=observation, index=0)
expected_content = f"<results>\n{expected_content}\n</results>"
assert SystemMessage(content=expected_content) == actual

View File

@@ -1,115 +0,0 @@
"""Test chat model integration."""
import typing
import pytest
from cohere.types import NonStreamedChatResponse, ToolCall
from langchain_cohere.chat_models import ChatCohere
def test_initialization() -> None:
"""Test chat model initialization."""
ChatCohere(cohere_api_key="test")
@pytest.mark.parametrize(
"chat_cohere,expected",
[
pytest.param(ChatCohere(cohere_api_key="test"), {}, id="defaults"),
pytest.param(
ChatCohere(cohere_api_key="test", model="foo", temperature=1.0),
{
"model": "foo",
"temperature": 1.0,
},
id="values are set",
),
],
)
def test_default_params(chat_cohere: ChatCohere, expected: typing.Dict) -> None:
actual = chat_cohere._default_params
assert expected == actual
@pytest.mark.parametrize(
"response, expected",
[
pytest.param(
NonStreamedChatResponse(
generation_id="foo",
text="",
tool_calls=[
ToolCall(name="tool1", parameters={"arg1": 1, "arg2": "2"}),
ToolCall(name="tool2", parameters={"arg3": 3, "arg4": "4"}),
],
),
{
"documents": None,
"citations": None,
"search_results": None,
"search_queries": None,
"is_search_required": None,
"generation_id": "foo",
"tool_calls": [
{
"id": "foo",
"function": {
"name": "tool1",
"arguments": '{"arg1": 1, "arg2": "2"}',
},
"type": "function",
},
{
"id": "foo",
"function": {
"name": "tool2",
"arguments": '{"arg3": 3, "arg4": "4"}',
},
"type": "function",
},
],
},
id="tools should be called",
),
pytest.param(
NonStreamedChatResponse(
generation_id="foo",
text="",
tool_calls=[],
),
{
"documents": None,
"citations": None,
"search_results": None,
"search_queries": None,
"is_search_required": None,
"generation_id": "foo",
},
id="no tools should be called",
),
pytest.param(
NonStreamedChatResponse(
generation_id="foo",
text="bar",
tool_calls=[],
),
{
"documents": None,
"citations": None,
"search_results": None,
"search_queries": None,
"is_search_required": None,
"generation_id": "foo",
},
id="chat response without tools/documents/citations/tools etc",
),
],
)
def test_get_generation_info(
response: typing.Any, expected: typing.Dict[str, typing.Any]
) -> None:
chat_cohere = ChatCohere(cohere_api_key="test")
actual = chat_cohere._get_generation_info(response)
assert expected == actual

View File

@@ -1,140 +0,0 @@
import json
from typing import Any, Dict, List, Optional, Tuple, Type, Union
import pytest
from langchain_core.agents import AgentAction
from langchain_core.tools import BaseModel, BaseTool, Field
from langchain_cohere.cohere_agent import (
_format_to_cohere_tools,
_format_to_cohere_tools_messages,
)
expected_test_tool_definition = {
"description": "test_tool description",
"name": "test_tool",
"parameter_definitions": {
"arg_1": {
"description": "Arg1 description",
"required": True,
"type": "str",
},
"optional_arg_2": {
"description": "Arg2 description",
"required": False,
"type": "str",
},
"arg_3": {
"description": "Arg3 description",
"required": True,
"type": "int",
},
},
}
class _TestToolSchema(BaseModel):
arg_1: str = Field(description="Arg1 description")
optional_arg_2: Optional[str] = Field(description="Arg2 description", default="2")
arg_3: int = Field(description="Arg3 description")
class _TestTool(BaseTool):
name = "test_tool"
description = "test_tool description"
args_schema: Type[_TestToolSchema] = _TestToolSchema
def _run(self, *args: Any, **kwargs: Any) -> Any:
pass
class test_tool(BaseModel):
"""test_tool description"""
arg_1: str = Field(description="Arg1 description")
optional_arg_2: Optional[str] = Field(description="Arg2 description", default="2")
arg_3: int = Field(description="Arg3 description")
test_tool_as_dict = {
"title": "test_tool",
"description": "test_tool description",
"properties": {
"arg_1": {"description": "Arg1 description", "type": "string"},
"optional_arg_2": {
"description": "Arg2 description",
"type": "string",
"default": "2",
},
"arg_3": {"description": "Arg3 description", "type": "integer"},
},
}
@pytest.mark.parametrize(
"tool",
[
pytest.param(_TestTool(), id="tool from BaseTool"),
pytest.param(test_tool, id="BaseModel"),
pytest.param(test_tool_as_dict, id="JSON schema dict"),
],
)
def test_format_to_cohere_tools(
tool: Union[Dict[str, Any], BaseTool, Type[BaseModel]],
) -> None:
actual = _format_to_cohere_tools([tool])
assert [expected_test_tool_definition] == actual
@pytest.mark.parametrize(
"intermediate_step,expected",
[
pytest.param(
(
AgentAction(tool="tool_name", tool_input={"arg1": "value1"}, log=""),
"result",
),
{
"call": {"name": "tool_name", "parameters": {"arg1": "value1"}},
"outputs": [{"answer": "result"}],
},
id="tool_input as dict",
),
pytest.param(
(
AgentAction(
tool="tool_name", tool_input=json.dumps({"arg1": "value1"}), log=""
),
"result",
),
{
"call": {"name": "tool_name", "parameters": {"arg1": "value1"}},
"outputs": [{"answer": "result"}],
},
id="tool_input as serialized dict",
),
pytest.param(
(AgentAction(tool="tool_name", tool_input="foo", log=""), "result"),
{
"call": {"name": "tool_name", "parameters": {"input": "foo"}},
"outputs": [{"answer": "result"}],
},
id="tool_input as string",
),
pytest.param(
(AgentAction(tool="tool_name", tool_input="['foo']", log=""), "result"),
{
"call": {"name": "tool_name", "parameters": {"input": "['foo']"}},
"outputs": [{"answer": "result"}],
},
id="tool_input unrelated JSON",
),
],
)
def test_format_to_cohere_tools_messages(
intermediate_step: Tuple[AgentAction, str], expected: List[Dict[str, Any]]
) -> None:
actual = _format_to_cohere_tools_messages(intermediate_steps=[intermediate_step])
assert [expected] == actual

View File

@@ -1,9 +0,0 @@
"""Test embedding model integration."""
from langchain_cohere.embeddings import CohereEmbeddings
def test_initialization() -> None:
"""Test embedding model initialization."""
CohereEmbeddings(cohere_api_key="test")

View File

@@ -1,15 +0,0 @@
from langchain_cohere import __all__
EXPECTED_ALL = [
"CohereCitation",
"ChatCohere",
"CohereEmbeddings",
"CohereRagRetriever",
"CohereRerank",
"create_cohere_tools_agent",
"create_cohere_react_agent",
]
def test_all_imports() -> None:
assert sorted(EXPECTED_ALL) == sorted(__all__)

View File

@@ -1,61 +0,0 @@
"""Test Cohere API wrapper."""
import typing
import pytest
from langchain_core.pydantic_v1 import SecretStr
from langchain_cohere.llms import BaseCohere, Cohere
def test_cohere_api_key(monkeypatch: pytest.MonkeyPatch) -> None:
"""Test that cohere api key is a secret key."""
# test initialization from init
assert isinstance(BaseCohere(cohere_api_key="1").cohere_api_key, SecretStr)
# test initialization from env variable
monkeypatch.setenv("COHERE_API_KEY", "secret-api-key")
assert isinstance(BaseCohere().cohere_api_key, SecretStr)
@pytest.mark.parametrize(
"cohere,expected",
[
pytest.param(Cohere(cohere_api_key="test"), {}, id="defaults"),
pytest.param(
Cohere(
# the following are arbitrary testing values which shouldn't be used:
cohere_api_key="test",
model="foo",
temperature=0.1,
max_tokens=2,
k=3,
p=4,
frequency_penalty=0.5,
presence_penalty=0.6,
truncate="START",
),
{
"model": "foo",
"temperature": 0.1,
"max_tokens": 2,
"k": 3,
"p": 4,
"frequency_penalty": 0.5,
"presence_penalty": 0.6,
"truncate": "START",
},
id="with values set",
),
],
)
def test_default_params(cohere: Cohere, expected: typing.Dict) -> None:
actual = cohere._default_params
assert expected == actual
# def test_saving_loading_llm(tmp_path: Path) -> None:
# """Test saving/loading an Cohere LLM."""
# llm = BaseCohere(max_tokens=10)
# llm.save(file_path=tmp_path / "cohere.yaml")
# loaded_llm = load_llm(tmp_path / "cohere.yaml")
# assert_llm_equality(llm, loaded_llm)

View File

@@ -1,10 +0,0 @@
"""Test rag retriever integration."""
from langchain_cohere.chat_models import ChatCohere
from langchain_cohere.rag_retrievers import CohereRagRetriever
def test_initialization() -> None:
"""Test chat model initialization."""
CohereRagRetriever(llm=ChatCohere(cohere_api_key="test"))

View File

@@ -1,8 +0,0 @@
"""Test chat model integration."""
from langchain_cohere import CohereRerank
def test_initialization() -> None:
"""Test chat model initialization."""
CohereRerank(cohere_api_key="test")

View File

@@ -1,43 +0,0 @@
import pytest
from langchain_cohere.utils import _remove_signature_from_tool_description
@pytest.mark.parametrize(
"name,description,expected",
[
pytest.param(
"foo", "bar baz", "bar baz", id="description doesn't have signature"
),
pytest.param("foo", "", "", id="description is empty"),
pytest.param("foo", "foo(a: str) - bar baz", "bar baz", id="signature"),
pytest.param(
"foo", "foo() - bar baz", "bar baz", id="signature with empty args"
),
pytest.param(
"foo",
"foo(a: str) - foo(b: str) - bar",
"foo(b: str) - bar",
id="signature with edge case",
),
pytest.param(
"foo", "foo() -> None - bar baz", "bar baz", id="signature with return type"
),
pytest.param(
"foo",
"""My description.
Args:
Bar:
""",
"My description.",
id="signature with Args: section",
),
],
)
def test_remove_signature_from_description(
name: str, description: str, expected: str
) -> None:
actual = _remove_signature_from_tool_description(name=name, description=description)
assert expected == actual