Skip to content

Commit

Permalink
x
Browse files Browse the repository at this point in the history
  • Loading branch information
efriis committed Dec 23, 2024
2 parents 777c0e4 + 6352edf commit 630df20
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 31 deletions.
46 changes: 43 additions & 3 deletions docs/docs/integrations/chat/mlx.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,48 @@
"tools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n",
"\n",
"# setup ReAct style prompt\n",
"prompt = hub.pull(\"hwchase17/react-json\")\n",
"prompt = prompt.partial(\n",
"# Based on 'hwchase17/react' prompt modification, cause mlx does not support the `System` role\n",
"human_prompt = \"\"\"\n",
"Answer the following questions as best you can. You have access to the following tools:\n",
"\n",
"{tools}\n",
"\n",
"The way you use the tools is by specifying a json blob.\n",
"Specifically, this json should have a `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).\n",
"\n",
"The only values that should be in the \"action\" field are: {tool_names}\n",
"\n",
"The $JSON_BLOB should only contain a SINGLE action, do NOT return a list of multiple actions. Here is an example of a valid $JSON_BLOB:\n",
"\n",
"```\n",
"{{\n",
" \"action\": $TOOL_NAME,\n",
" \"action_input\": $INPUT\n",
"}}\n",
"```\n",
"\n",
"ALWAYS use the following format:\n",
"\n",
"Question: the input question you must answer\n",
"Thought: you should always think about what to do\n",
"Action:\n",
"```\n",
"$JSON_BLOB\n",
"```\n",
"Observation: the result of the action\n",
"... (this Thought/Action/Observation can repeat N times)\n",
"Thought: I now know the final answer\n",
"Final Answer: the final answer to the original input question\n",
"\n",
"Begin! Reminder to always use the exact characters `Final Answer` when responding.\n",
"\n",
"{input}\n",
"\n",
"{agent_scratchpad}\n",
"\n",
"\"\"\"\n",
"\n",
"prompt = human_prompt.partial(\n",
" tools=render_text_description(tools),\n",
" tool_names=\", \".join([t.name for t in tools]),\n",
")\n",
Expand Down Expand Up @@ -207,7 +247,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.18"
"version": "3.12.7"
}
},
"nbformat": 4,
Expand Down
132 changes: 132 additions & 0 deletions docs/docs/integrations/providers/cratedb.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# CrateDB

> [CrateDB] is a distributed and scalable SQL database for storing and
> analyzing massive amounts of data in near real-time, even with complex
> queries. It is PostgreSQL-compatible, based on Lucene, and inheriting
> from Elasticsearch.

## Installation and Setup

### Setup CrateDB
There are two ways to get started with CrateDB quickly. Alternatively,
choose other [CrateDB installation options].

#### Start CrateDB on your local machine
Example: Run a single-node CrateDB instance with security disabled,
using Docker or Podman. This is not recommended for production use.

```bash
docker run --name=cratedb --rm \
--publish=4200:4200 --publish=5432:5432 --env=CRATE_HEAP_SIZE=2g \
crate:latest -Cdiscovery.type=single-node
```

#### Deploy cluster on CrateDB Cloud
[CrateDB Cloud] is a managed CrateDB service. Sign up for a
[free trial][CrateDB Cloud Console].

### Install Client
Install the most recent version of the `langchain-cratedb` package
and a few others that are needed for this tutorial.
```bash
pip install --upgrade langchain-cratedb langchain-openai unstructured
```


## Documentation
For a more detailed walkthrough of the CrateDB wrapper, see
[using LangChain with CrateDB]. See also [all features of CrateDB]
to learn about other functionality provided by CrateDB.


## Features
The CrateDB adapter for LangChain provides APIs to use CrateDB as vector store,
document loader, and storage for chat messages.

### Vector Store
Use the CrateDB vector store functionality around `FLOAT_VECTOR` and `KNN_MATCH`
for similarity search and other purposes. See also [CrateDBVectorStore Tutorial].

Make sure you've configured a valid OpenAI API key.
```bash
export OPENAI_API_KEY=sk-XJZ...
```
```python
from langchain_community.document_loaders import UnstructuredURLLoader
from langchain_cratedb import CrateDBVectorStore
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter

loader = UnstructuredURLLoader(urls=["https://github.com/langchain-ai/langchain/raw/refs/tags/langchain-core==0.3.28/docs/docs/how_to/state_of_the_union.txt"])
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()

# Connect to a self-managed CrateDB instance on localhost.
CONNECTION_STRING = "crate://?schema=testdrive"

store = CrateDBVectorStore.from_documents(
documents=docs,
embedding=embeddings,
collection_name="state_of_the_union",
connection=CONNECTION_STRING,
)

query = "What did the president say about Ketanji Brown Jackson"
docs_with_score = store.similarity_search_with_score(query)
```

### Document Loader
Load load documents from a CrateDB database table, using the document loader
`CrateDBLoader`, which is based on SQLAlchemy. See also [CrateDBLoader Tutorial].

To use the document loader in your applications:
```python
import sqlalchemy as sa
from langchain_community.utilities import SQLDatabase
from langchain_cratedb import CrateDBLoader

# Connect to a self-managed CrateDB instance on localhost.
CONNECTION_STRING = "crate://?schema=testdrive"

db = SQLDatabase(engine=sa.create_engine(CONNECTION_STRING))

loader = CrateDBLoader(
'SELECT * FROM sys.summits LIMIT 42',
db=db,
)
documents = loader.load()
```

### Chat Message History
Use CrateDB as the storage for your chat messages.
See also [CrateDBChatMessageHistory Tutorial].

To use the chat message history in your applications:
```python
from langchain_cratedb import CrateDBChatMessageHistory

# Connect to a self-managed CrateDB instance on localhost.
CONNECTION_STRING = "crate://?schema=testdrive"

message_history = CrateDBChatMessageHistory(
session_id="test-session",
connection=CONNECTION_STRING,
)

message_history.add_user_message("hi!")
```


[all features of CrateDB]: https://cratedb.com/docs/guide/feature/
[CrateDB]: https://cratedb.com/database
[CrateDB Cloud]: https://cratedb.com/database/cloud
[CrateDB Cloud Console]: https://console.cratedb.cloud/?utm_source=langchain&utm_content=documentation
[CrateDB installation options]: https://cratedb.com/docs/guide/install/
[CrateDBChatMessageHistory Tutorial]: https://github.com/crate/cratedb-examples/blob/main/topic/machine-learning/llm-langchain/conversational_memory.ipynb
[CrateDBLoader Tutorial]: https://github.com/crate/cratedb-examples/blob/main/topic/machine-learning/llm-langchain/document_loader.ipynb
[CrateDBVectorStore Tutorial]: https://github.com/crate/cratedb-examples/blob/main/topic/machine-learning/llm-langchain/vector_search.ipynb
[using LangChain with CrateDB]: https://cratedb.com/docs/guide/integrate/langchain/
5 changes: 3 additions & 2 deletions docs/scripts/packages_yml_get_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ def _reorder_keys(p):
if p["name"] in seen:
raise ValueError(f"Duplicate package: {p['name']}")
seen.add(p["name"])
downloads_updated_at = datetime.fromisoformat(p["downloads_updated_at"])
downloads_updated_at_str = p.get("downloads_updated_at")
downloads_updated_at = datetime.fromisoformat(downloads_updated_at_str) if downloads_updated_at_str else None

if downloads_updated_at > yesterday:
if downloads_updated_at is not None and downloads_updated_at > yesterday:
print(f"done: {p['name']}: {p['downloads']}")
continue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ def lazy_load(self) -> Iterator[Document]:
"""Lazy load records from dataframe."""

for _, row in self.data_frame.iterrows():
text = row[self.page_content_column]
metadata = row.to_dict()
metadata.pop(self.page_content_column)
text = metadata.pop(self.page_content_column)
yield Document(page_content=text, metadata=metadata)


Expand Down
50 changes: 32 additions & 18 deletions libs/community/langchain_community/embeddings/llamacpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class LlamaCppEmbeddings(BaseModel, Embeddings):
"""

client: Any = None #: :meta private:
model_path: str
model_path: str = Field(default="")

n_ctx: int = Field(512, alias="n_ctx")
"""Token context window."""
Expand Down Expand Up @@ -88,21 +88,22 @@ def validate_environment(self) -> Self:
if self.n_gpu_layers is not None:
model_params["n_gpu_layers"] = self.n_gpu_layers

try:
from llama_cpp import Llama

self.client = Llama(model_path, embedding=True, **model_params)
except ImportError:
raise ImportError(
"Could not import llama-cpp-python library. "
"Please install the llama-cpp-python library to "
"use this embedding model: pip install llama-cpp-python"
)
except Exception as e:
raise ValueError(
f"Could not load Llama model from path: {model_path}. "
f"Received error {e}"
)
if not self.client:
try:
from llama_cpp import Llama

self.client = Llama(model_path, embedding=True, **model_params)
except ImportError:
raise ImportError(
"Could not import llama-cpp-python library. "
"Please install the llama-cpp-python library to "
"use this embedding model: pip install llama-cpp-python"
)
except Exception as e:
raise ValueError(
f"Could not load Llama model from path: {model_path}. "
f"Received error {e}"
)

return self

Expand All @@ -116,7 +117,17 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]:
List of embeddings, one for each text.
"""
embeddings = self.client.create_embedding(texts)
return [list(map(float, e["embedding"])) for e in embeddings["data"]]
final_embeddings = []
for e in embeddings["data"]:
try:
if isinstance(e["embedding"][0], list):
for data in e["embedding"]:
final_embeddings.append(list(map(float, data)))
else:
final_embeddings.append(list(map(float, e["embedding"])))
except (IndexError, TypeError):
final_embeddings.append(list(map(float, e["embedding"])))
return final_embeddings

def embed_query(self, text: str) -> List[float]:
"""Embed a query using the Llama model.
Expand All @@ -128,4 +139,7 @@ def embed_query(self, text: str) -> List[float]:
Embeddings for the text.
"""
embedding = self.client.embed(text)
return list(map(float, embedding))
if not isinstance(embedding, list):
return list(map(float, embedding))
else:
return list(map(float, embedding[0]))
21 changes: 18 additions & 3 deletions libs/community/langchain_community/vectorstores/azuresearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
logger = logging.getLogger()

if TYPE_CHECKING:
from azure.core.credentials import TokenCredential
from azure.core.credentials_async import AsyncTokenCredential
from azure.search.documents import SearchClient, SearchItemPaged
from azure.search.documents.aio import (
AsyncSearchItemPaged,
Expand Down Expand Up @@ -96,10 +98,13 @@ def _get_search_client(
cors_options: Optional[CorsOptions] = None,
async_: bool = False,
additional_search_client_options: Optional[Dict[str, Any]] = None,
azure_credential: Optional[TokenCredential] = None,
azure_async_credential: Optional[AsyncTokenCredential] = None,
) -> Union[SearchClient, AsyncSearchClient]:
from azure.core.credentials import AccessToken, AzureKeyCredential, TokenCredential
from azure.core.exceptions import ResourceNotFoundError
from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential
from azure.identity.aio import DefaultAzureCredential as AsyncDefaultAzureCredential
from azure.search.documents import SearchClient
from azure.search.documents.aio import SearchClient as AsyncSearchClient
from azure.search.documents.indexes import SearchIndexClient
Expand Down Expand Up @@ -143,12 +148,17 @@ def get_token(
if key.upper() == "INTERACTIVE":
credential = InteractiveBrowserCredential()
credential.get_token("https://search.azure.com/.default")
async_credential = credential
else:
credential = AzureKeyCredential(key)
async_credential = credential
elif azure_ad_access_token is not None:
credential = AzureBearerTokenCredential(azure_ad_access_token)
async_credential = credential
else:
credential = DefaultAzureCredential()
credential = azure_credential or DefaultAzureCredential()
async_credential = azure_async_credential or AsyncDefaultAzureCredential()

index_client: SearchIndexClient = SearchIndexClient(
endpoint=endpoint,
credential=credential,
Expand Down Expand Up @@ -266,7 +276,7 @@ def fmt_err(x: str) -> str:
return AsyncSearchClient(
endpoint=endpoint,
index_name=index_name,
credential=credential,
credential=async_credential,
user_agent=user_agent,
**additional_search_client_options,
)
Expand All @@ -278,7 +288,7 @@ class AzureSearch(VectorStore):
def __init__(
self,
azure_search_endpoint: str,
azure_search_key: str,
azure_search_key: Optional[str],
index_name: str,
embedding_function: Union[Callable, Embeddings],
search_type: str = "hybrid",
Expand All @@ -295,6 +305,8 @@ def __init__(
vector_search_dimensions: Optional[int] = None,
additional_search_client_options: Optional[Dict[str, Any]] = None,
azure_ad_access_token: Optional[str] = None,
azure_credential: Optional[TokenCredential] = None,
azure_async_credential: Optional[AsyncTokenCredential] = None,
**kwargs: Any,
):
try:
Expand Down Expand Up @@ -361,6 +373,7 @@ def __init__(
user_agent=user_agent,
cors_options=cors_options,
additional_search_client_options=additional_search_client_options,
azure_credential=azure_credential,
)
self.async_client = _get_search_client(
azure_search_endpoint,
Expand All @@ -377,6 +390,8 @@ def __init__(
user_agent=user_agent,
cors_options=cors_options,
async_=True,
azure_credential=azure_credential,
azure_async_credential=azure_async_credential,
)
self.search_type = search_type
self.semantic_configuration_name = semantic_configuration_name
Expand Down
Loading

0 comments on commit 630df20

Please sign in to comment.