mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-09 02:33:34 +00:00
Compare commits
131 Commits
mdrxy/make
...
fix/respon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d765a91c5c | ||
|
|
88af494b37 | ||
|
|
5b0a55ad35 | ||
|
|
08c4055347 | ||
|
|
6e2f46d04c | ||
|
|
5bf0b218c8 | ||
|
|
4e39c164bb | ||
|
|
0b3af47335 | ||
|
|
bc91a4811c | ||
|
|
05a61f9508 | ||
|
|
aa63de9366 | ||
|
|
86fa34f3eb | ||
|
|
36037c9251 | ||
|
|
ad26c892ea | ||
|
|
4828a85ab0 | ||
|
|
b999f356e8 | ||
|
|
062196a7b3 | ||
|
|
dc9f941326 | ||
|
|
238ecd09e0 | ||
|
|
6b5fdfb804 | ||
|
|
b42dac5fe6 | ||
|
|
e0a4af8d8b | ||
|
|
fcf7175392 | ||
|
|
1f2ab17dff | ||
|
|
2dc89a2ae7 | ||
|
|
e3c4aeaea1 | ||
|
|
444939945a | ||
|
|
ae8db86486 | ||
|
|
8a1419dad1 | ||
|
|
840e4c8e9f | ||
|
|
37aff0a153 | ||
|
|
a163d59988 | ||
|
|
b26e52aa4d | ||
|
|
38cdd7a2ec | ||
|
|
26e5d1302b | ||
|
|
107425c68d | ||
|
|
009cc3bf50 | ||
|
|
6185558449 | ||
|
|
0928ff5b12 | ||
|
|
7f9b0772fc | ||
|
|
d6e618258f | ||
|
|
806bc593ab | ||
|
|
047bcbaa13 | ||
|
|
18db07c292 | ||
|
|
1fe2c4084b | ||
|
|
c6c7fce6c9 | ||
|
|
3d08b6bd11 | ||
|
|
f2dcdae467 | ||
|
|
dbebe2ca97 | ||
|
|
008043977d | ||
|
|
1459d4f4ce | ||
|
|
f33480c2cf | ||
|
|
1c55536ec1 | ||
|
|
622337a297 | ||
|
|
1819c73d10 | ||
|
|
8171403b4a | ||
|
|
2d0713c2fc | ||
|
|
8060b371bb | ||
|
|
7851f66503 | ||
|
|
af3b88f58d | ||
|
|
45f1b67340 | ||
|
|
8e37d39d66 | ||
|
|
be9274054f | ||
|
|
03b9214737 | ||
|
|
fcebafea9b | ||
|
|
3efa31d786 | ||
|
|
1eb45d17fb | ||
|
|
8545d4731e | ||
|
|
21f7a9a9e5 | ||
|
|
61bc1bf9cc | ||
|
|
4ba222148d | ||
|
|
b825f85bf2 | ||
|
|
b5c44406eb | ||
|
|
2ec63ca7da | ||
|
|
f896bcdb1d | ||
|
|
73a7de63aa | ||
|
|
cd5f3ee364 | ||
|
|
02d6b9106b | ||
|
|
b470c79f1d | ||
|
|
d204f0dd55 | ||
|
|
00259b0061 | ||
|
|
4fb1132e30 | ||
|
|
a6690eb9fd | ||
|
|
f69f9598f5 | ||
|
|
8d0fb2d04b | ||
|
|
8042b04da6 | ||
|
|
fb74265175 | ||
|
|
21b61aaf9a | ||
|
|
03138f41a0 | ||
|
|
fd891ee3d4 | ||
|
|
b8cdbc4eca | ||
|
|
791d309c06 | ||
|
|
d3d23e2372 | ||
|
|
2f32c444b8 | ||
|
|
fe740a9397 | ||
|
|
b2b835cb36 | ||
|
|
4656f727da | ||
|
|
34800332bf | ||
|
|
06ba80ff68 | ||
|
|
2bd8096faa | ||
|
|
a0331285d7 | ||
|
|
8f68a08528 | ||
|
|
71651c4a11 | ||
|
|
44ec1f32b2 | ||
|
|
0c81499243 | ||
|
|
397cd89988 | ||
|
|
db438d8dcc | ||
|
|
4f71c35eb0 | ||
|
|
156ae2e69b | ||
|
|
f4f919768e | ||
|
|
7932e1edd1 | ||
|
|
024422e9b0 | ||
|
|
d52036accc | ||
|
|
5b701b5189 | ||
|
|
8848b3e018 | ||
|
|
80068432ed | ||
|
|
b9dcce95be | ||
|
|
be83ce74a7 | ||
|
|
1167e7458e | ||
|
|
d5fd0bca35 | ||
|
|
30d646b576 | ||
|
|
262c83763f | ||
|
|
0024dffa68 | ||
|
|
98797f367a | ||
|
|
1563099f3f | ||
|
|
7f259863e1 | ||
|
|
c8df6c7ec9 | ||
|
|
cf2b4bbe09 | ||
|
|
09a616fe85 | ||
|
|
46bbd52e81 | ||
|
|
8b663ed6c6 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -7,4 +7,4 @@ To learn how to contribute to LangChain, please follow the [contribution guide h
|
||||
|
||||
## New features
|
||||
|
||||
For new features, please start a new [discussion](https://forum.langchain.com/), where the maintainers will help with scoping out the necessary changes.
|
||||
For new features, please start a new [discussion on our forum](https://forum.langchain.com/), where the maintainers will help with scoping out the necessary changes.
|
||||
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,3 +1,5 @@
|
||||
(Replace this entire block of text)
|
||||
|
||||
Thank you for contributing to LangChain! Follow these steps to mark your pull request as ready for review. **If any of these steps are not completed, your PR will not be considered for review.**
|
||||
|
||||
- [ ] **PR title**: Follows the format: {TYPE}({SCOPE}): {DESCRIPTION}
|
||||
@@ -9,14 +11,13 @@ Thank you for contributing to LangChain! Follow these steps to mark your pull re
|
||||
- feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, release
|
||||
- Allowed `{SCOPE}` values (optional):
|
||||
- core, cli, langchain, standard-tests, docs, anthropic, chroma, deepseek, exa, fireworks, groq, huggingface, mistralai, nomic, ollama, openai, perplexity, prompty, qdrant, xai
|
||||
- Note: the `{DESCRIPTION}` must not start with an uppercase letter.
|
||||
- *Note:* the `{DESCRIPTION}` must not start with an uppercase letter.
|
||||
- Once you've written the title, please delete this checklist item; do not include it in the PR.
|
||||
|
||||
- [ ] **PR message**: ***Delete this entire checklist*** and replace with
|
||||
- **Description:** a description of the change. Include a [closing keyword](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) if applicable to a relevant issue.
|
||||
- **Issue:** the issue # it fixes, if applicable (e.g. Fixes #123)
|
||||
- **Dependencies:** any dependencies required for this change
|
||||
- **Twitter handle:** if your PR gets announced, and you'd like a mention, we'll gladly shout you out!
|
||||
|
||||
- [ ] **Add tests and docs**: If you're adding a new integration, you must include:
|
||||
1. A test for the integration, preferably unit tests that do not rely on network access,
|
||||
@@ -26,7 +27,7 @@ Thank you for contributing to LangChain! Follow these steps to mark your pull re
|
||||
|
||||
Additional guidelines:
|
||||
|
||||
- Make sure optional dependencies are imported within a function.
|
||||
- Please do not add dependencies to `pyproject.toml` files (even optional ones) unless they are **required** for unit tests.
|
||||
- Most PRs should not touch more than one package.
|
||||
- Please do not add dependencies to `pyproject.toml` files (even optional ones) unless they are **required** for unit tests.
|
||||
- Changes should be backwards compatible.
|
||||
- Make sure optional dependencies are imported within a function.
|
||||
|
||||
2
.github/scripts/check_diff.py
vendored
2
.github/scripts/check_diff.py
vendored
@@ -132,6 +132,8 @@ def _get_configs_for_single_dir(job: str, dir_: str) -> List[Dict[str, str]]:
|
||||
|
||||
elif dir_ == "libs/langchain" and job == "extended-tests":
|
||||
py_versions = ["3.9", "3.13"]
|
||||
elif dir_ == "libs/langchain_v1":
|
||||
py_versions = ["3.10", "3.13"]
|
||||
|
||||
elif dir_ == ".":
|
||||
# unable to install with 3.13 because tokenizers doesn't support 3.13 yet
|
||||
|
||||
29
.github/workflows/check_core_versions.yml
vendored
29
.github/workflows/check_core_versions.yml
vendored
@@ -20,15 +20,30 @@ jobs:
|
||||
|
||||
- name: '✅ Verify pyproject.toml & version.py Match'
|
||||
run: |
|
||||
PYPROJECT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' libs/core/pyproject.toml)
|
||||
VERSION_PY_VERSION=$(grep -Po '(?<=^VERSION = ")[^"]*' libs/core/langchain_core/version.py)
|
||||
# Check core versions
|
||||
CORE_PYPROJECT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' libs/core/pyproject.toml)
|
||||
CORE_VERSION_PY_VERSION=$(grep -Po '(?<=^VERSION = ")[^"]*' libs/core/langchain_core/version.py)
|
||||
|
||||
# Compare the two versions
|
||||
if [ "$PYPROJECT_VERSION" != "$VERSION_PY_VERSION" ]; then
|
||||
# Compare core versions
|
||||
if [ "$CORE_PYPROJECT_VERSION" != "$CORE_VERSION_PY_VERSION" ]; then
|
||||
echo "langchain-core versions in pyproject.toml and version.py do not match!"
|
||||
echo "pyproject.toml version: $PYPROJECT_VERSION"
|
||||
echo "version.py version: $VERSION_PY_VERSION"
|
||||
echo "pyproject.toml version: $CORE_PYPROJECT_VERSION"
|
||||
echo "version.py version: $CORE_VERSION_PY_VERSION"
|
||||
exit 1
|
||||
else
|
||||
echo "Versions match: $PYPROJECT_VERSION"
|
||||
echo "Core versions match: $CORE_PYPROJECT_VERSION"
|
||||
fi
|
||||
|
||||
# Check langchain_v1 versions
|
||||
LANGCHAIN_PYPROJECT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' libs/langchain_v1/pyproject.toml)
|
||||
LANGCHAIN_INIT_PY_VERSION=$(grep -Po '(?<=^__version__ = ")[^"]*' libs/langchain_v1/langchain/__init__.py)
|
||||
|
||||
# Compare langchain_v1 versions
|
||||
if [ "$LANGCHAIN_PYPROJECT_VERSION" != "$LANGCHAIN_INIT_PY_VERSION" ]; then
|
||||
echo "langchain_v1 versions in pyproject.toml and __init__.py do not match!"
|
||||
echo "pyproject.toml version: $LANGCHAIN_PYPROJECT_VERSION"
|
||||
echo "version.py version: $LANGCHAIN_INIT_PY_VERSION"
|
||||
exit 1
|
||||
else
|
||||
echo "Langchain v1 versions match: $LANGCHAIN_PYPROJECT_VERSION"
|
||||
fi
|
||||
|
||||
16
README.md
16
README.md
@@ -9,15 +9,13 @@
|
||||
</div>
|
||||
|
||||
[](https://github.com/langchain-ai/langchain/releases)
|
||||
[](https://github.com/langchain-ai/langchain/actions/workflows/check_diffs.yml)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://pypistats.org/packages/langchain-core)
|
||||
[](https://pypistats.org/packages/langchain-core)
|
||||
[](https://star-history.com/#langchain-ai/langchain)
|
||||
[](https://github.com/langchain-ai/langchain/issues)
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/langchain-ai/langchain)
|
||||
[<img src="https://github.com/codespaces/badge.svg" alt="Open in Github Codespace" title="Open in Github Codespace" width="150" height="20">](https://codespaces.new/langchain-ai/langchain)
|
||||
[](https://twitter.com/langchainai)
|
||||
[](https://codspeed.io/langchain-ai/langchain)
|
||||
[](https://twitter.com/langchainai)
|
||||
|
||||
> [!NOTE]
|
||||
> Looking for the JS/TS library? Check out [LangChain.js](https://github.com/langchain-ai/langchainjs).
|
||||
@@ -45,7 +43,7 @@ interface for models, embeddings, vector stores, and more.
|
||||
Use LangChain for:
|
||||
|
||||
- **Real-time data augmentation**. Easily connect LLMs to diverse data sources and
|
||||
external / internal systems, drawing from LangChain’s vast library of integrations with
|
||||
external/internal systems, drawing from LangChain’s vast library of integrations with
|
||||
model providers, tools, vector stores, retrievers, and more.
|
||||
- **Model interoperability**. Swap models in and out as your engineering team
|
||||
experiments to find the best choice for your application’s needs. As the industry
|
||||
@@ -60,7 +58,7 @@ applications.
|
||||
|
||||
To improve your LLM application development, pair LangChain with:
|
||||
|
||||
- [LangSmith](http://www.langchain.com/langsmith) - Helpful for agent evals and
|
||||
- [LangSmith](https://www.langchain.com/langsmith) - Helpful for agent evals and
|
||||
observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain
|
||||
visibility in production, and improve performance over time.
|
||||
- [LangGraph](https://langchain-ai.github.io/langgraph/) - Build agents that can
|
||||
@@ -68,9 +66,8 @@ reliably handle complex tasks with LangGraph, our low-level agent orchestration
|
||||
framework. LangGraph offers customizable architecture, long-term memory, and
|
||||
human-in-the-loop workflows — and is trusted in production by companies like LinkedIn,
|
||||
Uber, Klarna, and GitLab.
|
||||
- [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/) - Deploy
|
||||
and scale agents effortlessly with a purpose-built deployment platform for long
|
||||
running, stateful workflows. Discover, reuse, configure, and share agents across
|
||||
- [LangGraph Platform](https://docs.langchain.com/langgraph-platform) - Deploy
|
||||
and scale agents effortlessly with a purpose-built deployment platform for long-running, stateful workflows. Discover, reuse, configure, and share agents across
|
||||
teams — and iterate quickly with visual prototyping in
|
||||
[LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/).
|
||||
|
||||
@@ -85,3 +82,4 @@ concepts behind the LangChain framework.
|
||||
- [LangChain Forum](https://forum.langchain.com/): Connect with the community and share all of your technical questions, ideas, and feedback.
|
||||
- [API Reference](https://python.langchain.com/api_reference/): Detailed reference on
|
||||
navigating base packages and integrations for LangChain.
|
||||
- [Chat LangChain](https://chat.langchain.com/): Ask questions & chat with our documentation.
|
||||
|
||||
@@ -4,9 +4,9 @@ LangChain has a large ecosystem of integrations with various external resources
|
||||
|
||||
## Best practices
|
||||
|
||||
When building such applications developers should remember to follow good security practices:
|
||||
When building such applications, developers should remember to follow good security practices:
|
||||
|
||||
* [**Limit Permissions**](https://en.wikipedia.org/wiki/Principle_of_least_privilege): Scope permissions specifically to the application's need. Granting broad or excessive permissions can introduce significant security vulnerabilities. To avoid such vulnerabilities, consider using read-only credentials, disallowing access to sensitive resources, using sandboxing techniques (such as running inside a container), specifying proxy configurations to control external requests, etc. as appropriate for your application.
|
||||
* [**Limit Permissions**](https://en.wikipedia.org/wiki/Principle_of_least_privilege): Scope permissions specifically to the application's need. Granting broad or excessive permissions can introduce significant security vulnerabilities. To avoid such vulnerabilities, consider using read-only credentials, disallowing access to sensitive resources, using sandboxing techniques (such as running inside a container), specifying proxy configurations to control external requests, etc., as appropriate for your application.
|
||||
* **Anticipate Potential Misuse**: Just as humans can err, so can Large Language Models (LLMs). Always assume that any system access or credentials may be used in any way allowed by the permissions they are assigned. For example, if a pair of database credentials allows deleting data, it's safest to assume that any LLM able to use those credentials may in fact delete data.
|
||||
* [**Defense in Depth**](https://en.wikipedia.org/wiki/Defense_in_depth_(computing)): No security technique is perfect. Fine-tuning and good chain design can reduce, but not eliminate, the odds that a Large Language Model (LLM) may make a mistake. It's best to combine multiple layered security approaches rather than relying on any single layer of defense to ensure security. For example: use both read-only permissions and sandboxing to ensure that LLMs are only able to access data that is explicitly meant for them to use.
|
||||
|
||||
@@ -67,8 +67,7 @@ All out of scope targets defined by huntr as well as:
|
||||
for more details, but generally tools interact with the real world. Developers are
|
||||
expected to understand the security implications of their code and are responsible
|
||||
for the security of their tools.
|
||||
* Code documented with security notices. This will be decided on a case by
|
||||
case basis, but likely will not be eligible for a bounty as the code is already
|
||||
* Code documented with security notices. This will be decided on a case-by-case basis, but likely will not be eligible for a bounty as the code is already
|
||||
documented with guidelines for developers that should be followed for making their
|
||||
application secure.
|
||||
* Any LangSmith related repositories or APIs (see [Reporting LangSmith Vulnerabilities](#reporting-langsmith-vulnerabilities)).
|
||||
|
||||
@@ -97,7 +97,7 @@ def skip_private_members(app, what, name, obj, skip, options):
|
||||
if hasattr(obj, "__doc__") and obj.__doc__ and ":private:" in obj.__doc__:
|
||||
return True
|
||||
if name == "__init__" and obj.__objclass__ is object:
|
||||
# dont document default init
|
||||
# don't document default init
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[Vector stores](/docs/concepts/vectorstores)**: Storage of and efficient search over vectors and associated metadata.
|
||||
- **[Retriever](/docs/concepts/retrievers)**: A component that returns relevant documents from a knowledge base in response to a query.
|
||||
- **[Retrieval Augmented Generation (RAG)](/docs/concepts/rag)**: A technique that enhances language models by combining them with external knowledge bases.
|
||||
- **[Agents](/docs/concepts/agents)**: Use a [language model](/docs/concepts/chat_models) to choose a sequence of actions to take. Agents can interact with external resources via [tool](/docs/concepts/tools).
|
||||
- **[Agents](/docs/concepts/agents)**: Use a [language model](/docs/concepts/chat_models) to choose a sequence of actions to take. Agents can interact with external resources via [tools](/docs/concepts/tools).
|
||||
- **[Prompt templates](/docs/concepts/prompt_templates)**: Component for factoring out the static parts of a model "prompt" (usually a sequence of messages). Useful for serializing, versioning, and reusing these static parts.
|
||||
- **[Output parsers](/docs/concepts/output_parsers)**: Responsible for taking the output of a model and transforming it into a more suitable format for downstream tasks. Output parsers were primarily useful prior to the general availability of [tool calling](/docs/concepts/tool_calling) and [structured outputs](/docs/concepts/structured_outputs).
|
||||
- **[Few-shot prompting](/docs/concepts/few_shot_prompting)**: A technique for improving model performance by providing a few examples of the task to perform in the prompt.
|
||||
@@ -48,7 +48,7 @@ The conceptual guide does not cover step-by-step instructions or specific implem
|
||||
- **[AIMessage](/docs/concepts/messages#aimessage)**: Represents a complete response from an AI model.
|
||||
- **[astream_events](/docs/concepts/chat_models#key-methods)**: Stream granular information from [LCEL](/docs/concepts/lcel) chains.
|
||||
- **[BaseTool](/docs/concepts/tools/#tool-interface)**: The base class for all tools in LangChain.
|
||||
- **[batch](/docs/concepts/runnables)**: Use to execute a runnable with batch inputs.
|
||||
- **[batch](/docs/concepts/runnables)**: Used to execute a runnable with batch inputs.
|
||||
- **[bind_tools](/docs/concepts/tool_calling/#tool-binding)**: Allows models to interact with tools.
|
||||
- **[Caching](/docs/concepts/chat_models#caching)**: Storing results to avoid redundant calls to a chat model.
|
||||
- **[Chat models](/docs/concepts/multimodality/#multimodality-in-chat-models)**: Chat models that handle multiple data modalities.
|
||||
|
||||
@@ -147,7 +147,7 @@ An `AIMessage` has the following attributes. The attributes which are **standard
|
||||
| `tool_calls` | Standardized | Tool calls associated with the message. See [tool calling](/docs/concepts/tool_calling) for details. |
|
||||
| `invalid_tool_calls` | Standardized | Tool calls with parsing errors associated with the message. See [tool calling](/docs/concepts/tool_calling) for details. |
|
||||
| `usage_metadata` | Standardized | Usage metadata for a message, such as [token counts](/docs/concepts/tokens). See [Usage Metadata API Reference](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html). |
|
||||
| `id` | Standardized | An optional unique identifier for the message, ideally provided by the provider/model that created the message. |
|
||||
| `id` | Standardized | An optional unique identifier for the message, ideally provided by the provider/model that created the message. See [Message IDs](#message-ids) for details. |
|
||||
| `response_metadata` | Raw | Response metadata, e.g., response headers, logprobs, token counts. |
|
||||
|
||||
#### content
|
||||
@@ -243,3 +243,37 @@ At the moment, the output of the model will be in terms of LangChain messages, s
|
||||
need OpenAI format for the output as well.
|
||||
|
||||
The [convert_to_openai_messages](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.convert_to_openai_messages.html) utility function can be used to convert from LangChain messages to OpenAI format.
|
||||
|
||||
## Message IDs
|
||||
|
||||
LangChain messages include an optional `id` field that serves as a unique identifier. Understanding when and how these IDs are assigned can be helpful for debugging, tracing, and working with message history.
|
||||
|
||||
### When Messages Get IDs
|
||||
|
||||
Messages receive IDs in the following scenarios:
|
||||
|
||||
**Automatically assigned by LangChain:**
|
||||
- When generated through chat model invocation (`.invoke()`, `.stream()`, `.astream()`) with an active run manager/tracing context
|
||||
- IDs follow the format:
|
||||
- `run-$RUN_ID` (e.g., `run-ba48f958-6402-41a5-b461-5e250a4ebd36-0`)
|
||||
- `run-$RUN_ID-$IDX` (e.g., `run-ba48f958-6402-41a5-b461-5e250a4ebd36-1`) when there are multiple generations from a single chat model invocation.
|
||||
|
||||
**Provider-assigned IDs (highest priority):**
|
||||
- When the model provider assigns its own ID to the message
|
||||
- These take precedence over LangChain-generated run IDs
|
||||
- Format varies by provider
|
||||
|
||||
### When Messages Don't Get IDs
|
||||
|
||||
Messages will **not** receive IDs in these situations:
|
||||
|
||||
- **Manual message creation**: Messages created directly (e.g., `AIMessage(content="hello")`) without going through chat models
|
||||
- **No run manager context**: When there's no active callback/tracing infrastructure
|
||||
|
||||
### ID Priority System
|
||||
|
||||
LangChain follows a clear precedence system for message IDs:
|
||||
|
||||
1. **Provider-assigned IDs** (highest priority): IDs from the model provider
|
||||
2. **LangChain run IDs** (medium priority): IDs starting with `run-`
|
||||
3. **Manual IDs** (lowest priority): IDs explicitly set by users
|
||||
|
||||
@@ -29,6 +29,22 @@ model_with_structure = model.with_structured_output(schema)
|
||||
structured_output = model_with_structure.invoke(user_input)
|
||||
```
|
||||
|
||||
:::warning[Tool Order Matters]
|
||||
|
||||
When combining structured output with additional tools, bind tools **first**, then apply structured output:
|
||||
|
||||
```python
|
||||
# Correct
|
||||
model_with_tools = model.bind_tools([tool1, tool2])
|
||||
structured_model = model_with_tools.with_structured_output(schema)
|
||||
|
||||
# Incorrect - will cause tool resolution errors
|
||||
structured_model = model.with_structured_output(schema)
|
||||
broken_model = structured_model.bind_tools([tool1, tool2])
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Schema definition
|
||||
|
||||
The central concept is that the output structure of model responses needs to be represented in some way.
|
||||
|
||||
@@ -7,4 +7,4 @@ Traces contain individual steps called `runs`. These can be individual calls fro
|
||||
tool, or sub-chains.
|
||||
Tracing gives you observability inside your chains and agents, and is vital in diagnosing issues.
|
||||
|
||||
For a deeper dive, check out [this LangSmith conceptual guide](https://docs.smith.langchain.com/concepts/tracing).
|
||||
For a deeper dive, check out [this LangSmith conceptual guide](https://docs.langchain.com/langsmith/observability-quickstart).
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
Here are some things to keep in mind for all types of contributions:
|
||||
|
||||
- Follow the ["fork and pull request"](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) workflow.
|
||||
- Fill out the checked-in pull request template when opening pull requests. Note related issues and tag relevant maintainers.
|
||||
- Fill out the checked-in pull request template when opening pull requests. Note related issues.
|
||||
- Ensure your PR passes formatting, linting, and testing checks before requesting a review.
|
||||
- If you would like comments or feedback on your current progress, please open an issue or discussion and tag a maintainer.
|
||||
- If you would like comments or feedback on your current progress, please open an issue or discussion.
|
||||
- See the sections on [Testing](setup.mdx#testing) and [Formatting and Linting](setup.mdx#formatting-and-linting) for how to run these checks locally.
|
||||
- Backwards compatibility is key. Your changes must not be breaking, except in case of critical bug and security fixes.
|
||||
- Look for duplicate PRs or issues that have already been opened before opening a new one.
|
||||
|
||||
@@ -223,6 +223,49 @@ If codespell is incorrectly flagging a word, you can skip spellcheck for that wo
|
||||
ignore-words-list = 'momento,collison,ned,foor,reworkd,parth,whats,aapply,mysogyny,unsecure'
|
||||
```
|
||||
|
||||
### Pre-commit
|
||||
|
||||
We use [pre-commit](https://pre-commit.com/) to ensure commits are formatted/linted.
|
||||
|
||||
#### Installing Pre-commit
|
||||
|
||||
First, install pre-commit:
|
||||
|
||||
```bash
|
||||
# Option 1: Using uv (recommended)
|
||||
uv tool install pre-commit
|
||||
|
||||
# Option 2: Using Homebrew (globally for macOS/Linux)
|
||||
brew install pre-commit
|
||||
|
||||
# Option 3: Using pip
|
||||
pip install pre-commit
|
||||
```
|
||||
|
||||
Then install the git hook scripts:
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
#### How Pre-commit Works
|
||||
|
||||
Once installed, pre-commit will automatically run on every `git commit`. Hooks are specified in `.pre-commit-config.yaml` and will:
|
||||
|
||||
- Format code using `ruff` for the specific library/package you're modifying
|
||||
- Only run on files that have changed
|
||||
- Prevent commits if formatting fails
|
||||
|
||||
#### Skipping Pre-commit
|
||||
|
||||
In exceptional cases, you can skip pre-commit hooks with:
|
||||
|
||||
```bash
|
||||
git commit --no-verify
|
||||
```
|
||||
|
||||
However, this is discouraged as the CI system will still enforce the same formatting rules.
|
||||
|
||||
## Working with optional dependencies
|
||||
|
||||
`langchain`, `langchain-community`, and `langchain-experimental` rely on optional dependencies to keep these packages lightweight.
|
||||
|
||||
@@ -79,7 +79,7 @@ Here are some high-level tips on writing a good how-to guide:
|
||||
|
||||
### Conceptual guide
|
||||
|
||||
LangChain's conceptual guide falls under the **Explanation** quadrant of Diataxis. These guides should cover LangChain terms and concepts
|
||||
LangChain's conceptual guides fall under the **Explanation** quadrant of Diataxis. These guides should cover LangChain terms and concepts
|
||||
in a more abstract way than how-to guides or tutorials, targeting curious users interested in
|
||||
gaining a deeper understanding and insights of the framework. Try to avoid excessively large code examples as the primary goal is to
|
||||
provide perspective to the user rather than to finish a practical project. These guides should cover **why** things work the way they do.
|
||||
@@ -105,7 +105,7 @@ Here are some high-level tips on writing a good conceptual guide:
|
||||
### References
|
||||
|
||||
References contain detailed, low-level information that describes exactly what functionality exists and how to use it.
|
||||
In LangChain, this is mainly our API reference pages, which are populated from docstrings within code.
|
||||
In LangChain, these are mainly our API reference pages, which are populated from docstrings within code.
|
||||
References pages are generally not read end-to-end, but are consulted as necessary when a user needs to know
|
||||
how to use something specific.
|
||||
|
||||
@@ -119,7 +119,7 @@ but here are some high-level tips on writing a good docstring:
|
||||
- Be concise
|
||||
- Discuss special cases and deviations from a user's expectations
|
||||
- Go into detail on required inputs and outputs
|
||||
- Light details on when one might use the feature are fine, but in-depth details belong in other sections.
|
||||
- Light details on when one might use the feature are fine, but in-depth details belong in other sections
|
||||
|
||||
Each category serves a distinct purpose and requires a specific approach to writing and structuring the content.
|
||||
|
||||
@@ -127,17 +127,17 @@ Each category serves a distinct purpose and requires a specific approach to writ
|
||||
|
||||
Here are some other guidelines you should think about when writing and organizing documentation.
|
||||
|
||||
We generally do not merge new tutorials from outside contributors without an actue need.
|
||||
We generally do not merge new tutorials from outside contributors without an acute need.
|
||||
We welcome updates as well as new integration docs, how-tos, and references.
|
||||
|
||||
### Avoid duplication
|
||||
|
||||
Multiple pages that cover the same material in depth are difficult to maintain and cause confusion. There should
|
||||
be only one (very rarely two), canonical pages for a given concept or feature. Instead, you should link to other guides.
|
||||
be only one (very rarely two) canonical pages for a given concept or feature. Instead, you should link to other guides.
|
||||
|
||||
### Link to other sections
|
||||
|
||||
Because sections of the docs do not exist in a vacuum, it is important to link to other sections frequently,
|
||||
Because sections of the docs do not exist in a vacuum, it is important to link to other sections frequently
|
||||
to allow a developer to learn more about an unfamiliar topic within the flow of reading.
|
||||
|
||||
This includes linking to the API references and conceptual sections!
|
||||
|
||||
@@ -33,7 +33,7 @@ Sometimes you want to make a small change, like fixing a typo, and the easiest w
|
||||
- Click the "Commit changes..." button at the top-right corner of the page.
|
||||
- Give your commit a title like "Fix typo in X section."
|
||||
- Optionally, write an extended commit description.
|
||||
- Click "Propose changes"
|
||||
- Click "Propose changes".
|
||||
|
||||
5. **Submit a pull request (PR):**
|
||||
- GitHub will redirect you to a page where you can create a pull request.
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": null,
|
||||
"id": "321e3036-abd2-4e1f-bcc6-606efd036954",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
@@ -183,7 +183,7 @@
|
||||
],
|
||||
"source": [
|
||||
"configurable_model.invoke(\n",
|
||||
" \"what's your name\", config={\"configurable\": {\"model\": \"claude-3-5-sonnet-20240620\"}}\n",
|
||||
" \"what's your name\", config={\"configurable\": {\"model\": \"claude-3-5-sonnet-latest\"}}\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -234,7 +234,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": null,
|
||||
"id": "6c8755ba-c001-4f5a-a497-be3f1db83244",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
@@ -261,7 +261,7 @@
|
||||
" \"what's your name\",\n",
|
||||
" config={\n",
|
||||
" \"configurable\": {\n",
|
||||
" \"first_model\": \"claude-3-5-sonnet-20240620\",\n",
|
||||
" \"first_model\": \"claude-3-5-sonnet-latest\",\n",
|
||||
" \"first_temperature\": 0.5,\n",
|
||||
" \"first_max_tokens\": 100,\n",
|
||||
" }\n",
|
||||
@@ -336,7 +336,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": null,
|
||||
"id": "e57dfe9f-cd24-4e37-9ce9-ccf8daf78f89",
|
||||
"metadata": {
|
||||
"execution": {
|
||||
@@ -368,14 +368,14 @@
|
||||
"source": [
|
||||
"llm_with_tools.invoke(\n",
|
||||
" \"what's bigger in 2024 LA or NYC\",\n",
|
||||
" config={\"configurable\": {\"model\": \"claude-3-5-sonnet-20240620\"}},\n",
|
||||
" config={\"configurable\": {\"model\": \"claude-3-5-sonnet-latest\"}},\n",
|
||||
").tool_calls"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "langchain",
|
||||
"display_name": "langchain-monorepo",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -389,7 +389,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.16"
|
||||
"version": "3.12.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -741,13 +741,13 @@
|
||||
"\n",
|
||||
"If you're using tools with agents, you will likely need an error handling strategy, so the agent can recover from the error and continue execution.\n",
|
||||
"\n",
|
||||
"A simple strategy is to throw a `ToolException` from inside the tool and specify an error handler using `handle_tool_error`. \n",
|
||||
"A simple strategy is to throw a `ToolException` from inside the tool and specify an error handler using `handle_tool_errors`. \n",
|
||||
"\n",
|
||||
"When the error handler is specified, the exception will be caught and the error handler will decide which output to return from the tool.\n",
|
||||
"\n",
|
||||
"You can set `handle_tool_error` to `True`, a string value, or a function. If it's a function, the function should take a `ToolException` as a parameter and return a value.\n",
|
||||
"You can set `handle_tool_errors` to `True`, a string value, or a function. If it's a function, the function should take a `ToolException` as a parameter and return a value.\n",
|
||||
"\n",
|
||||
"Please note that only raising a `ToolException` won't be effective. You need to first set the `handle_tool_error` of the tool because its default value is `False`."
|
||||
"Please note that only raising a `ToolException` won't be effective. You need to first set the `handle_tool_errors` of the tool because its default value is `False`."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -777,7 +777,7 @@
|
||||
"id": "9d93b217-1d44-4d31-8956-db9ea680ff4f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here's an example with the default `handle_tool_error=True` behavior."
|
||||
"Here's an example with the default `handle_tool_errors=True` behavior."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -807,7 +807,7 @@
|
||||
"source": [
|
||||
"get_weather_tool = StructuredTool.from_function(\n",
|
||||
" func=get_weather,\n",
|
||||
" handle_tool_error=True,\n",
|
||||
" handle_tool_errors=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"get_weather_tool.invoke({\"city\": \"foobar\"})"
|
||||
@@ -818,7 +818,7 @@
|
||||
"id": "f91d6dc0-3271-4adc-a155-21f2e62ffa56",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We can set `handle_tool_error` to a string that will always be returned."
|
||||
"We can set `handle_tool_errors` to a string that will always be returned."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -848,7 +848,7 @@
|
||||
"source": [
|
||||
"get_weather_tool = StructuredTool.from_function(\n",
|
||||
" func=get_weather,\n",
|
||||
" handle_tool_error=\"There is no such city, but it's probably above 0K there!\",\n",
|
||||
" handle_tool_errors=\"There is no such city, but it's probably above 0K there!\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"get_weather_tool.invoke({\"city\": \"foobar\"})"
|
||||
@@ -893,7 +893,7 @@
|
||||
"\n",
|
||||
"get_weather_tool = StructuredTool.from_function(\n",
|
||||
" func=get_weather,\n",
|
||||
" handle_tool_error=_handle_error,\n",
|
||||
" handle_tool_errors=_handle_error,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"get_weather_tool.invoke({\"city\": \"foobar\"})"
|
||||
|
||||
@@ -565,7 +565,7 @@
|
||||
"id": "3ac2c37a-06a1-40d3-a192-9078eb83994b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<table><thead><tr><th colspan=\"3\">able 1. LUllclll 1ayoul actCCLloll 1110AdCs 111 L1C LayoOulralsel 1110U4cl 200</th></tr><tr><th>Dataset</th><th>| Base Model\\'|</th><th>Notes</th></tr></thead><tbody><tr><td>PubLayNet [38]</td><td>F/M</td><td>Layouts of modern scientific documents</td></tr><tr><td>PRImA</td><td>M</td><td>Layouts of scanned modern magazines and scientific reports</td></tr><tr><td>Newspaper</td><td>F</td><td>Layouts of scanned US newspapers from the 20th century</td></tr><tr><td>TableBank [18]</td><td>F</td><td>Table region on modern scientific and business document</td></tr><tr><td>HJDataset</td><td>F/M</td><td>Layouts of history Japanese documents</td></tr></tbody></table>"
|
||||
"<table><thead><tr><th colspan=\"3\">Table 1: Current layout detection models in the LayoutParser model zoo</th></tr><tr><th>Dataset</th><th>Base Model1</th><th>Large Model Notes</th></tr></thead><tbody><tr><td>PubLayNet [38]</td><td>F/M</td><td>Layouts of modern scientific documents</td></tr><tr><td>PRImA</td><td>M</td><td>Layouts of scanned modern magazines and scientific reports</td></tr><tr><td>Newspaper</td><td>F</td><td>Layouts of scanned US newspapers from the 20th century</td></tr><tr><td>TableBank [18]</td><td>F</td><td>Table region on modern scientific and business document</td></tr><tr><td>HJDataset</td><td>F/M</td><td>Layouts of history Japanese documents</td></tr></tbody></table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ sidebar_class_name: hidden
|
||||
|
||||
# How-to guides
|
||||
|
||||
Here you’ll find answers to “How do I….?” types of questions.
|
||||
Here you’ll find answers to "How do I….?" types of questions.
|
||||
These guides are *goal-oriented* and *concrete*; they're meant to help you complete a specific task.
|
||||
For conceptual explanations see the [Conceptual guide](/docs/concepts/).
|
||||
For end-to-end walkthroughs see [Tutorials](/docs/tutorials).
|
||||
@@ -47,7 +47,7 @@ See [supported integrations](/docs/integrations/chat/) for details on getting st
|
||||
- [How to: use chat model to call tools](/docs/how_to/tool_calling)
|
||||
- [How to: stream tool calls](/docs/how_to/tool_streaming)
|
||||
- [How to: handle rate limits](/docs/how_to/chat_model_rate_limiting)
|
||||
- [How to: few shot prompt tool behavior](/docs/how_to/tools_few_shot)
|
||||
- [How to: few-shot prompt tool behavior](/docs/how_to/tools_few_shot)
|
||||
- [How to: bind model-specific formatted tools](/docs/how_to/tools_model_specific)
|
||||
- [How to: force a specific tool call](/docs/how_to/tool_choice)
|
||||
- [How to: pass multimodal data directly to models](/docs/how_to/multimodal_inputs/)
|
||||
@@ -64,8 +64,8 @@ See [supported integrations](/docs/integrations/chat/) for details on getting st
|
||||
|
||||
[Prompt Templates](/docs/concepts/prompt_templates) are responsible for formatting user input into a format that can be passed to a language model.
|
||||
|
||||
- [How to: use few shot examples](/docs/how_to/few_shot_examples)
|
||||
- [How to: use few shot examples in chat models](/docs/how_to/few_shot_examples_chat/)
|
||||
- [How to: use few-shot examples](/docs/how_to/few_shot_examples)
|
||||
- [How to: use few-shot examples in chat models](/docs/how_to/few_shot_examples_chat/)
|
||||
- [How to: partially format prompt templates](/docs/how_to/prompts_partial)
|
||||
- [How to: compose prompts together](/docs/how_to/prompts_composition)
|
||||
- [How to: use multimodal prompts](/docs/how_to/multimodal_prompts/)
|
||||
@@ -168,7 +168,7 @@ See [supported integrations](/docs/integrations/vectorstores/) for details on ge
|
||||
|
||||
Indexing is the process of keeping your vectorstore in-sync with the underlying data source.
|
||||
|
||||
- [How to: reindex data to keep your vectorstore in-sync with the underlying data source](/docs/how_to/indexing)
|
||||
- [How to: reindex data to keep your vectorstore in sync with the underlying data source](/docs/how_to/indexing)
|
||||
|
||||
### Tools
|
||||
|
||||
@@ -178,7 +178,7 @@ LangChain [Tools](/docs/concepts/tools) contain a description of the tool (to pa
|
||||
- [How to: use built-in tools and toolkits](/docs/how_to/tools_builtin)
|
||||
- [How to: use chat models to call tools](/docs/how_to/tool_calling)
|
||||
- [How to: pass tool outputs to chat models](/docs/how_to/tool_results_pass_to_model)
|
||||
- [How to: pass run time values to tools](/docs/how_to/tool_runtime)
|
||||
- [How to: pass runtime values to tools](/docs/how_to/tool_runtime)
|
||||
- [How to: add a human-in-the-loop for tools](/docs/how_to/tools_human)
|
||||
- [How to: handle tool errors](/docs/how_to/tools_error)
|
||||
- [How to: force models to call a tool](/docs/how_to/tool_choice)
|
||||
@@ -297,7 +297,7 @@ For a high-level tutorial, check out [this guide](/docs/tutorials/sql_qa/).
|
||||
You can use an LLM to do question answering over graph databases.
|
||||
For a high-level tutorial, check out [this guide](/docs/tutorials/graph/).
|
||||
|
||||
- [How to: add a semantic layer over the database](/docs/how_to/graph_semantic)
|
||||
- [How to: add a semantic layer over a database](/docs/how_to/graph_semantic)
|
||||
- [How to: construct knowledge graphs](/docs/how_to/graph_constructing)
|
||||
|
||||
### Summarization
|
||||
@@ -345,7 +345,7 @@ LangGraph is an extension of LangChain aimed at
|
||||
building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph.
|
||||
|
||||
LangGraph documentation is currently hosted on a separate site.
|
||||
You can peruse [LangGraph how-to guides here](https://langchain-ai.github.io/langgraph/how-tos/).
|
||||
You can find the [LangGraph guides here](https://langchain-ai.github.io/langgraph/guides/).
|
||||
|
||||
## [LangSmith](https://docs.smith.langchain.com/)
|
||||
|
||||
|
||||
@@ -998,6 +998,91 @@
|
||||
"\n",
|
||||
"chain.invoke({\"query\": query})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "xfejabhtn2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Combining with Additional Tools\n",
|
||||
"\n",
|
||||
"When you need to use both structured output and additional tools (like web search), note the order of operations:\n",
|
||||
"\n",
|
||||
"**Correct Order**:\n",
|
||||
"```python\n",
|
||||
"# 1. Bind tools first\n",
|
||||
"llm_with_tools = llm.bind_tools([web_search_tool, calculator_tool])\n",
|
||||
"\n",
|
||||
"# 2. Apply structured output\n",
|
||||
"structured_llm = llm_with_tools.with_structured_output(MySchema)\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"**Incorrect Order**:\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"# This will fail with \"Tool 'MySchema' not found\" error\n",
|
||||
"structured_llm = llm.with_structured_output(MySchema)\n",
|
||||
"broken_llm = structured_llm.bind_tools([web_search_tool])\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "653798ca",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Why Order Matters:**\n",
|
||||
"`with_structured_output()` internally uses tool calling to enforce the schema. When you bind additional tools afterward, it creates a conflict in the tool resolution system."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1345f4a4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Complete Example:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0835637b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class SearchResult(BaseModel):\n",
|
||||
" \"\"\"Structured search result.\"\"\"\n",
|
||||
"\n",
|
||||
" query: str = Field(description=\"The search query\")\n",
|
||||
" findings: str = Field(description=\"Summary of findings\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define tools\n",
|
||||
"search_tool = {\n",
|
||||
" \"type\": \"function\",\n",
|
||||
" \"function\": {\n",
|
||||
" \"name\": \"web_search\",\n",
|
||||
" \"description\": \"Search the web for information\",\n",
|
||||
" \"parameters\": {\n",
|
||||
" \"type\": \"object\",\n",
|
||||
" \"properties\": {\"query\": {\"type\": \"string\", \"description\": \"Search query\"}},\n",
|
||||
" \"required\": [\"query\"],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Correct approach\n",
|
||||
"llm = ChatOpenAI()\n",
|
||||
"llm_with_search = llm.bind_tools([search_tool])\n",
|
||||
"structured_search_llm = llm_with_search.with_structured_output(SearchResult)\n",
|
||||
"\n",
|
||||
"# Now you can use both search and get structured output\n",
|
||||
"result = structured_search_llm.invoke(\"Search for latest AI research and summarize\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"id": "74de0286-b003-4b48-9cdd-ecab435515ca",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -157,7 +157,7 @@
|
||||
"\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0)"
|
||||
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-latest\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"source": [
|
||||
"## Defining tool schemas\n",
|
||||
"\n",
|
||||
"For a model to be able to call tools, we need to pass in tool schemas that describe what the tool does and what it's arguments are. Chat models that support tool calling features implement a `.bind_tools()` method for passing tool schemas to the model. Tool schemas can be passed in as Python functions (with typehints and docstrings), Pydantic models, TypedDict classes, or LangChain [Tool objects](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.base.BaseTool.html#basetool). Subsequent invocations of the model will pass in these tool schemas along with the prompt.\n",
|
||||
"For a model to be able to call tools, we need to pass in tool schemas that describe what the tool does and what its arguments are. Chat models that support tool calling features implement a `.bind_tools()` method for passing tool schemas to the model. Tool schemas can be passed in as Python functions (with typehints and docstrings), Pydantic models, TypedDict classes, or LangChain [Tool objects](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.base.BaseTool.html#basetool). Subsequent invocations of the model will pass in these tool schemas along with the prompt.\n",
|
||||
"\n",
|
||||
"### Python functions\n",
|
||||
"Our tool schemas can be Python functions:"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -53,7 +53,7 @@
|
||||
"if \"ANTHROPIC_API_KEY\" not in os.environ:\n",
|
||||
" os.environ[\"ANTHROPIC_API_KEY\"] = getpass()\n",
|
||||
"\n",
|
||||
"model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0)"
|
||||
"model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\", temperature=0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"\n",
|
||||
"To keep the most recent messages, we set `strategy=\"last\"`. We'll also set `include_system=True` to include the `SystemMessage`, and `start_on=\"human\"` to make sure the resulting chat history is valid. \n",
|
||||
"\n",
|
||||
"This is a good default configuration when using `trim_messages` based on token count. Remember to adjust `token_counter` and `max_tokens` for your use case.\n",
|
||||
"This is a good default configuration when using `trim_messages` based on token count. Remember to adjust `token_counter` and `max_tokens` for your use case. Keep in mind that new queries added to the chat history will be included in the token count unless you trim prior to adding the new query.\n",
|
||||
"\n",
|
||||
"Notice that for our `token_counter` we can pass in a function (more on that below) or a language model (since language models have a message token counting method). It makes sense to pass in a model when you're trimming your messages to fit into the context window of that specific model:"
|
||||
]
|
||||
@@ -525,7 +525,7 @@
|
||||
"id": "4d91d390-e7f7-467b-ad87-d100411d7a21",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Looking at the LangSmith trace we can see that before the messages are passed to the model they are first trimmed: https://smith.langchain.com/public/65af12c4-c24d-4824-90f0-6547566e59bb/r\n",
|
||||
"Looking at [the LangSmith trace](https://smith.langchain.com/public/65af12c4-c24d-4824-90f0-6547566e59bb/r) we can see that before the messages are passed to the model they are first trimmed.\n",
|
||||
"\n",
|
||||
"Looking at just the trimmer, we can see that it's a Runnable object that can be invoked like all Runnables:"
|
||||
]
|
||||
@@ -620,7 +620,7 @@
|
||||
"id": "556b7b4c-43cb-41de-94fc-1a41f4ec4d2e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Looking at the LangSmith trace we can see that we retrieve all of our messages but before the messages are passed to the model they are trimmed to be just the system message and last human message: https://smith.langchain.com/public/17dd700b-9994-44ca-930c-116e00997315/r"
|
||||
"Looking at [the LangSmith trace](https://smith.langchain.com/public/17dd700b-9994-44ca-930c-116e00997315/r) we can see that we retrieve all of our messages but before the messages are passed to the model they are trimmed to be just the system message and last human message."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -630,7 +630,7 @@
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For a complete description of all arguments head to the API reference: https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html"
|
||||
"For a complete description of all arguments head to the [API reference](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html)."
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -132,7 +132,7 @@
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(\n",
|
||||
" model=\"claude-3-5-sonnet-20240620\",\n",
|
||||
" model=\"claude-3-5-sonnet-latest\",\n",
|
||||
" temperature=0,\n",
|
||||
" max_tokens=1024,\n",
|
||||
" timeout=None,\n",
|
||||
@@ -1240,6 +1240,58 @@
|
||||
"response = llm_with_tools.invoke(\"How do I update a web app to TypeScript 5.5?\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "kloc4rvd1w",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Web search + structured output\n",
|
||||
"\n",
|
||||
"When combining web search tools with structured output, it's important to **bind the tools first and then apply structured output**:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "rjjergy6ef",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pydantic import BaseModel, Field\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define structured output schema\n",
|
||||
"class ResearchResult(BaseModel):\n",
|
||||
" \"\"\"Structured research result from web search.\"\"\"\n",
|
||||
"\n",
|
||||
" topic: str = Field(description=\"The research topic\")\n",
|
||||
" summary: str = Field(description=\"Summary of key findings\")\n",
|
||||
" key_points: list[str] = Field(description=\"List of important points discovered\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Configure web search tool\n",
|
||||
"websearch_tools = [\n",
|
||||
" {\n",
|
||||
" \"type\": \"web_search_20250305\",\n",
|
||||
" \"name\": \"web_search\",\n",
|
||||
" \"max_uses\": 10,\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(model=\"claude-3-5-sonnet-20241022\")\n",
|
||||
"\n",
|
||||
"# Correct order: bind tools first, then structured output\n",
|
||||
"llm_with_search = llm.bind_tools(websearch_tools)\n",
|
||||
"research_llm = llm_with_search.with_structured_output(ResearchResult)\n",
|
||||
"\n",
|
||||
"# Now you can use both web search and get structured output\n",
|
||||
"result = research_llm.invoke(\"Research the latest developments in quantum computing\")\n",
|
||||
"print(f\"Topic: {result.topic}\")\n",
|
||||
"print(f\"Summary: {result.summary}\")\n",
|
||||
"print(f\"Key Points: {result.key_points}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1478cdc6-2e52-4870-80f9-b4ddf88f2db2",
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": null,
|
||||
"id": "cb09c344-1836-4e0c-acf8-11d13ac1dbae",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -137,7 +137,7 @@
|
||||
"from langchain_aws import ChatBedrockConverse\n",
|
||||
"\n",
|
||||
"llm = ChatBedrockConverse(\n",
|
||||
" model_id=\"anthropic.claude-3-5-sonnet-20240620-v1:0\",\n",
|
||||
" model_id=\"anthropic.claude-3-5-sonnet-latest-v1:0\",\n",
|
||||
" # region_name=...,\n",
|
||||
" # aws_access_key_id=...,\n",
|
||||
" # aws_secret_access_key=...,\n",
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"source": [
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"The LangChain OCIGenAI integration lives in the `langchain-community` package and you will also need to install the `oci` package:"
|
||||
"The LangChain OCIGenAI integration lives in the `langchain-oci` package and you will also need to install the `oci` package:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -63,7 +63,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -qU langchain-community oci"
|
||||
"%pip install -qU langchain-oci"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -83,7 +83,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.chat_models.oci_generative_ai import ChatOCIGenAI\n",
|
||||
"from langchain_oci.chat_models import ChatOCIGenAI\n",
|
||||
"from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
|
||||
"\n",
|
||||
"chat = ChatOCIGenAI(\n",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"source": [
|
||||
"# ChatOllama\n",
|
||||
"\n",
|
||||
"[Ollama](https://ollama.com/) allows you to run open-source large language models, such as `got-oss`, locally.\n",
|
||||
"[Ollama](https://ollama.com/) allows you to run open-source large language models, such as `gpt-oss`, locally.\n",
|
||||
"\n",
|
||||
"`ollama` bundles model weights, configuration, and data into a single package, defined by a Modelfile.\n",
|
||||
"\n",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"source": [
|
||||
"# Azure AI Data\n",
|
||||
"\n",
|
||||
">[Azure AI Studio](https://ai.azure.com/) provides the capability to upload data assets to cloud storage and register existing data assets from the following sources:\n",
|
||||
">[Azure AI Foundry (formerly Azure AI Studio)](https://ai.azure.com/) provides the capability to upload data assets to cloud storage and register existing data assets from the following sources:\n",
|
||||
">\n",
|
||||
">- `Microsoft OneLake`\n",
|
||||
">- `Azure Blob Storage`\n",
|
||||
|
||||
@@ -2,67 +2,91 @@
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"# Oracle Autonomous Database\n",
|
||||
"\n",
|
||||
"Oracle autonomous database is a cloud database that uses machine learning to automate database tuning, security, backups, updates, and other routine management tasks traditionally performed by DBAs.\n",
|
||||
"Oracle Autonomous Database is a cloud database that uses machine learning to automate database tuning, security, backups, updates, and other routine management tasks traditionally performed by DBAs.\n",
|
||||
"\n",
|
||||
"This notebook covers how to load documents from oracle autonomous database, the loader supports connection with connection string or tns configuration.\n",
|
||||
"This notebook covers how to load documents from Oracle Autonomous Database.\n",
|
||||
"\n",
|
||||
"## Prerequisites\n",
|
||||
"1. Database runs in a 'Thin' mode:\n",
|
||||
" https://python-oracledb.readthedocs.io/en/latest/user_guide/appendix_b.html\n",
|
||||
"2. `pip install oracledb`:\n",
|
||||
" https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
"1. Install python-oracledb:\n",
|
||||
"\n",
|
||||
" `pip install oracledb`\n",
|
||||
" \n",
|
||||
" See [Installing python-oracledb](https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html).\n",
|
||||
"\n",
|
||||
"2. A database that python-oracledb's default 'Thin' mode can connected to. This is true of Oracle Autonomous Database, see [python-oracledb Architecture](https://python-oracledb.readthedocs.io/en/latest/user_guide/introduction.html#architecture).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"## Instructions"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pip install oracledb"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.document_loaders import OracleAutonomousDatabaseLoader\n",
|
||||
"from settings import s"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"With mutual TLS authentication (mTLS), wallet_location and wallet_password are required to create the connection, user can create connection by providing either connection string or tns configuration details."
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"With mutual TLS authentication (mTLS), wallet_location and wallet_password parameters are required to create the connection. See python-oracledb documentation [Connecting to Oracle Cloud Autonomous Databases](https://python-oracledb.readthedocs.io/en/latest/user_guide/connection_handling.html#connecting-to-oracle-cloud-autonomous-databases)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SQL_QUERY = \"select prod_id, time_id from sh.costs fetch first 5 rows only\"\n",
|
||||
@@ -89,24 +113,30 @@
|
||||
" wallet_password=s.PASSWORD,\n",
|
||||
")\n",
|
||||
"doc_2 = doc_loader_2.load()"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"With TLS authentication, wallet_location and wallet_password are not required.\n",
|
||||
"Bind variable option is provided by argument \"parameters\"."
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"With 1-way TLS authentication, only the database credentials and connection string are required to establish a connection.\n",
|
||||
"The example below also shows passing bind variable values with the argument \"parameters\"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SQL_QUERY = \"select channel_id, channel_desc from sh.channels where channel_desc = :1 fetch first 5 rows only\"\n",
|
||||
@@ -131,31 +161,28 @@
|
||||
" parameters=[\"Direct Sales\"],\n",
|
||||
")\n",
|
||||
"doc_4 = doc_loader_4.load()"
|
||||
],
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.6"
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
||||
334
docs/docs/integrations/document_loaders/oxylabs.ipynb
Normal file
334
docs/docs/integrations/document_loaders/oxylabs.ipynb
Normal file
@@ -0,0 +1,334 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Oxylabs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"[Oxylabs](https://oxylabs.io/) is a web intelligence collection platform that enables companies worldwide to unlock data-driven insights.\n",
|
||||
"\n",
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"Oxylabs document loader allows to load data from search engines, e-commerce sites, travel platforms, and any other website. It supports geolocation, browser rendering, data parsing, multiple user agents and many more parameters. Check out [Oxylabs documentation](https://developers.oxylabs.io/scraping-solutions/web-scraper-api) for more information.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Integration details\n",
|
||||
"\n",
|
||||
"| Class | Package | Local | Serializable | Pricing |\n",
|
||||
"|:--------------|:------------------------------------------------------------------|:-----:|:------------:|:-----------------------------:|\n",
|
||||
"| OxylabsLoader | [langchain-oxylabs](https://github.com/oxylabs/langchain-oxylabs) | ✅ | ❌ | Free 5,000 results for 1 week |\n",
|
||||
"\n",
|
||||
"### Loader features\n",
|
||||
"| Document Lazy Loading |\n",
|
||||
"|:---------------------:|\n",
|
||||
"| ✅ |\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Install the required dependencies.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install -U langchain-oxylabs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Credentials\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up the proper API keys and environment variables.\n",
|
||||
"Create your API user credentials: Sign up for a free trial or purchase the product\n",
|
||||
"in the [Oxylabs dashboard](https://dashboard.oxylabs.io/en/registration)\n",
|
||||
"to create your API user credentials (OXYLABS_USERNAME and OXYLABS_PASSWORD)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import getpass\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.environ[\"OXYLABS_USERNAME\"] = getpass.getpass(\"Enter your Oxylabs username: \")\n",
|
||||
"os.environ[\"OXYLABS_PASSWORD\"] = getpass.getpass(\"Enter your Oxylabs password: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Initialization"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-08-06T10:57:51.630011Z",
|
||||
"start_time": "2025-08-06T10:57:51.623814Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_oxylabs import OxylabsLoader"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-08-06T10:57:53.685413Z",
|
||||
"start_time": "2025-08-06T10:57:53.628859Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"loader = OxylabsLoader(\n",
|
||||
" urls=[\n",
|
||||
" \"https://sandbox.oxylabs.io/products/1\",\n",
|
||||
" \"https://sandbox.oxylabs.io/products/2\",\n",
|
||||
" ],\n",
|
||||
" params={\"markdown\": True},\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## Load"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-08-06T10:59:51.487327Z",
|
||||
"start_time": "2025-08-06T10:59:48.592743Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"2751\n",
|
||||
"[](/)\n",
|
||||
"\n",
|
||||
"Game platforms:\n",
|
||||
"\n",
|
||||
"* **All**\n",
|
||||
"\n",
|
||||
"* [Nintendo platform](/products/category/nintendo)\n",
|
||||
"\n",
|
||||
"+ wii\n",
|
||||
"+ wii-u\n",
|
||||
"+ nintendo-64\n",
|
||||
"+ switch\n",
|
||||
"+ gamecube\n",
|
||||
"+ game-boy-advance\n",
|
||||
"+ 3ds\n",
|
||||
"+ ds\n",
|
||||
"\n",
|
||||
"* [Xbox platform](/products/category/xbox-platform)\n",
|
||||
"\n",
|
||||
"* **Dreamcast**\n",
|
||||
"\n",
|
||||
"* [Playstation platform](/products/category/playstation-platform)\n",
|
||||
"\n",
|
||||
"* **Pc**\n",
|
||||
"\n",
|
||||
"* **Stadia**\n",
|
||||
"\n",
|
||||
"Go Back\n",
|
||||
"\n",
|
||||
"Note!This is a sandbox website used for web scraping. Information listed in this website does not have any real meaning and should not be associated with the actual products.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## The Legend of Zelda: Ocarina of Time\n",
|
||||
"\n",
|
||||
"**Developer:** Nintendo**Platform:****Type:** singleplayer\n",
|
||||
"\n",
|
||||
"As a young boy, Link is tricked by Ganondorf, the King of the Gerudo Thieves. The evil human uses Link to g\n",
|
||||
"5542\n",
|
||||
"[](/)\n",
|
||||
"\n",
|
||||
"Game platforms:\n",
|
||||
"\n",
|
||||
"* **All**\n",
|
||||
"\n",
|
||||
"* [Nintendo platform](/products/category/nintendo)\n",
|
||||
"\n",
|
||||
"+ wii\n",
|
||||
"+ wii-u\n",
|
||||
"+ nintendo-64\n",
|
||||
"+ switch\n",
|
||||
"+ gamecube\n",
|
||||
"+ game-boy-advance\n",
|
||||
"+ 3ds\n",
|
||||
"+ ds\n",
|
||||
"\n",
|
||||
"* [Xbox platform](/products/category/xbox-platform)\n",
|
||||
"\n",
|
||||
"* **Dreamcast**\n",
|
||||
"\n",
|
||||
"* [Playstation platform](/products/category/playstation-platform)\n",
|
||||
"\n",
|
||||
"* **Pc**\n",
|
||||
"\n",
|
||||
"* **Stadia**\n",
|
||||
"\n",
|
||||
"Go Back\n",
|
||||
"\n",
|
||||
"Note!This is a sandbox website used for web scraping. Information listed in this website does not have any real meaning and should not be associated with the actual products.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Super Mario Galaxy\n",
|
||||
"\n",
|
||||
"**Developer:** Nintendo**Platform:****Type:** singleplayer\n",
|
||||
"\n",
|
||||
"[Metacritic's 2007 Wii Game of the Year] The ultimate Nintendo hero is taking the ultimate step ... out into space. Join Mario as he ushers in a new era of video games, de\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for document in loader.load():\n",
|
||||
" print(document.page_content[:1000])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "markdown",
|
||||
"source": "## Lazy Load"
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "code",
|
||||
"outputs": [],
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"for document in loader.lazy_load():\n",
|
||||
" print(document.page_content[:1000])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Advanced examples\n",
|
||||
"\n",
|
||||
"The following examples show the usage of `OxylabsLoader` with geolocation, currency, pagination and user agent parameters for Amazon Search and Google Search sources."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-08-06T11:04:19.901122Z",
|
||||
"start_time": "2025-08-06T11:04:19.838933Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"loader = OxylabsLoader(\n",
|
||||
" queries=[\"gaming headset\", \"gaming chair\", \"computer mouse\"],\n",
|
||||
" params={\n",
|
||||
" \"source\": \"amazon_search\",\n",
|
||||
" \"parse\": True,\n",
|
||||
" \"geo_location\": \"DE\",\n",
|
||||
" \"currency\": \"EUR\",\n",
|
||||
" \"pages\": 3,\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-08-06T11:07:17.648142Z",
|
||||
"start_time": "2025-08-06T11:07:17.595629Z"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"loader = OxylabsLoader(\n",
|
||||
" queries=[\"europe gdp per capita\", \"us gdp per capita\"],\n",
|
||||
" params={\n",
|
||||
" \"source\": \"google_search\",\n",
|
||||
" \"parse\": True,\n",
|
||||
" \"geo_location\": \"Paris, France\",\n",
|
||||
" \"user_agent_type\": \"mobile\",\n",
|
||||
" },\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"[More information about this package.](https://github.com/oxylabs/langchain-oxylabs)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -U oci langchain-community"
|
||||
"!pip install -U langchain-oci"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -47,7 +47,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.llms.oci_generative_ai import OCIGenAI\n",
|
||||
"from langchain_oci.llms import OCIGenAI\n",
|
||||
"\n",
|
||||
"llm = OCIGenAI(\n",
|
||||
" model_id=\"cohere.command\",\n",
|
||||
|
||||
215
docs/docs/integrations/memory/recallio_memory.ipynb
Normal file
215
docs/docs/integrations/memory/recallio_memory.ipynb
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# RecallioMemory + LangChain Integration Demo\n",
|
||||
"A minimal notebook to show drop-in usage of RecallioMemory in LangChain (with scoped writes and recall)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install recallio langchain langchain-recallio openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup: API Keys & Imports"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_recallio.memory import RecallioMemory\n",
|
||||
"from langchain_openai import ChatOpenAI\n",
|
||||
"from langchain.prompts import ChatPromptTemplate\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# Set your keys here or use environment variables\n",
|
||||
"RECALLIO_API_KEY = os.getenv(\"RECALLIO_API_KEY\", \"YOUR_RECALLIO_API_KEY\")\n",
|
||||
"OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\", \"YOUR_OPENAI_API_KEY\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Initialize RecallioMemory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"memory = RecallioMemory(\n",
|
||||
" project_id=\"project_abc\",\n",
|
||||
" api_key=RECALLIO_API_KEY,\n",
|
||||
" session_id=\"demo-session-001\",\n",
|
||||
" user_id=\"demo-user-42\",\n",
|
||||
" default_tags=[\"test\", \"langchain\"],\n",
|
||||
" return_messages=True,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Build a LangChain ConversationChain with RecallioMemory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# You can swap in any supported LLM here\n",
|
||||
"llm = ChatOpenAI(api_key=OPENAI_API_KEY, temperature=0)\n",
|
||||
"prompt = ChatPromptTemplate.from_messages(\n",
|
||||
" [\n",
|
||||
" (\n",
|
||||
" \"system\",\n",
|
||||
" \"The following is a friendly conversation between a human and an AI. \"\n",
|
||||
" \"The AI is talkative and provides lots of specific details from its context. \"\n",
|
||||
" \"If the AI does not know the answer to a question, it truthfully says it does not know.\",\n",
|
||||
" ),\n",
|
||||
" (\"placeholder\", \"{history}\"), # RecallioMemory will fill this slot\n",
|
||||
" (\"human\", \"{input}\"),\n",
|
||||
" ]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# LCEL chain that returns an AIMessage\n",
|
||||
"base_chain = prompt | llm\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Create a stateful chain using RecallioMemory\n",
|
||||
"def chat_with_memory(user_input: str):\n",
|
||||
" # Load conversation history from memory\n",
|
||||
" memory_vars = memory.load_memory_variables({\"input\": user_input})\n",
|
||||
"\n",
|
||||
" # Run the chain with history and user input\n",
|
||||
" response = base_chain.invoke(\n",
|
||||
" {\"input\": user_input, \"history\": memory_vars.get(\"history\", \"\")}\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Save the conversation to memory\n",
|
||||
" memory.save_context({\"input\": user_input}, {\"output\": response.content})\n",
|
||||
"\n",
|
||||
" return response"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example: Chat with Memory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Bot: Hello Guillaume! It's nice to meet you. How can I assist you today?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# First user message – note the AI remembers the name\n",
|
||||
"resp1 = chat_with_memory(\"Hi! My name is Guillaume. Remember that.\")\n",
|
||||
"print(\"Bot:\", resp1.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Bot: Your name is Guillaume.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Second user message – AI should recall the name from memory\n",
|
||||
"resp2 = chat_with_memory(\"What is my name?\")\n",
|
||||
"print(\"Bot:\", resp2.content)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## See What Is Stored in Recallio\n",
|
||||
"This is for debugging/demo only; in production, you wouldn't do this on every run."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Current memory variables: {'history': [HumanMessage(content='Name is Guillaume', additional_kwargs={}, response_metadata={})]}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Current memory variables:\", memory.load_memory_variables({}))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Clear Memory (Optional Cleanup - Requires Manager level Key)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# memory.clear()\n",
|
||||
"# print(\"Memory cleared.\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.10"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -2,17 +2,10 @@
|
||||
|
||||
This will help you getting started with DigitalOcean Gradient [chat models](/docs/concepts/chat_models).
|
||||
|
||||
## Overview
|
||||
### Integration details
|
||||
|
||||
| Class | Package | Package downloads | Package latest |
|
||||
| :--- | :--- | :---: | :---: |
|
||||
| [ChatGradient](https://python.langchain.com/api_reference/langchain-gradient/chat_models/langchain_gradient.chat_models.ChatGradient.html) | [langchain-gradient](https://python.langchain.com/api_reference/langchain-gradient/) |  |  |
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
langchain-gradient uses DigitalOcean Gradient Platform.
|
||||
langchain-gradient uses DigitalOcean's Gradient™ AI Platform.
|
||||
|
||||
Create an account on DigitalOcean, acquire a `DIGITALOCEAN_INFERENCE_KEY` API key from the Gradient Platform, and install the `langchain-gradient` integration package.
|
||||
|
||||
|
||||
@@ -11,17 +11,17 @@ The `LangChain` integrations related to [Oracle Cloud Infrastructure](https://ww
|
||||
To use, you should have the latest `oci` python SDK and the langchain_community package installed.
|
||||
|
||||
```bash
|
||||
pip install -U oci langchain-community
|
||||
pip install -U langchain_oci
|
||||
```
|
||||
|
||||
See [chat](/docs/integrations/llms/oci_generative_ai), [complete](/docs/integrations/chat/oci_generative_ai), and [embedding](/docs/integrations/text_embedding/oci_generative_ai) usage examples.
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatOCIGenAI
|
||||
from langchain_oci.chat_models import ChatOCIGenAI
|
||||
|
||||
from langchain_community.llms import OCIGenAI
|
||||
from langchain_oci.llms import OCIGenAI
|
||||
|
||||
from langchain_community.embeddings import OCIGenAIEmbeddings
|
||||
from langchain_oci.embeddings import OCIGenAIEmbeddings
|
||||
```
|
||||
|
||||
## OCI Data Science Model Deployment Endpoint
|
||||
@@ -42,8 +42,8 @@ See [chat](/docs/integrations/chat/oci_data_science) and [complete](/docs/integr
|
||||
|
||||
|
||||
```python
|
||||
from langchain_community.chat_models import ChatOCIModelDeployment
|
||||
from langchain_oci.chat_models import ChatOCIModelDeployment
|
||||
|
||||
from langchain_community.llms import OCIModelDeploymentLLM
|
||||
from langchain_oci.llms import OCIModelDeploymentLLM
|
||||
```
|
||||
|
||||
|
||||
31
docs/docs/integrations/providers/recallio.ipynb
Normal file
31
docs/docs/integrations/providers/recallio.ipynb
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Recallio\n",
|
||||
"\n",
|
||||
"[Recallio](https://recallio.ai/) is a powerfull API allowing to store, index, and retrieve application “memories” with built-in fact extraction, dynamic summaries, reranked recall, and a full knowledge-graph layer.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"```bash\n",
|
||||
"pip install langchain-recallio\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"from langchain_recallio.memory import RecallioMemory\n",
|
||||
"```"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
43
docs/docs/integrations/providers/siliconflow.mdx
Normal file
43
docs/docs/integrations/providers/siliconflow.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
# langchain-siliconflow
|
||||
|
||||
This package contains the LangChain integration with SiliconFlow
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install -U langchain-siliconflow
|
||||
```
|
||||
|
||||
And you should configure credentials by setting the following environment variables:
|
||||
|
||||
```bash
|
||||
export SILICONFLOW_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
You can set the following environment variable to use the `.cn` endpoint:
|
||||
|
||||
```bash
|
||||
export SILICONFLOW_BASE_URL="https://api.siliconflow.cn/v1"
|
||||
```
|
||||
|
||||
## Chat Models
|
||||
|
||||
`ChatSiliconFlow` class exposes chat models from SiliconFlow.
|
||||
|
||||
```python
|
||||
from langchain_siliconflow import ChatSiliconFlow
|
||||
|
||||
llm = ChatSiliconFlow()
|
||||
llm.invoke("Sing a ballad of LangChain.")
|
||||
```
|
||||
|
||||
## Embeddings
|
||||
|
||||
`SiliconFlowEmbeddings` class exposes embeddings from SiliconFlow.
|
||||
|
||||
```python
|
||||
from langchain_siliconflow import SiliconFlowEmbeddings
|
||||
|
||||
embeddings = SiliconFlowEmbeddings()
|
||||
embeddings.embed_query("What is the meaning of life?")
|
||||
```
|
||||
101
docs/docs/integrations/providers/truefoundry.mdx
Normal file
101
docs/docs/integrations/providers/truefoundry.mdx
Normal file
@@ -0,0 +1,101 @@
|
||||
# TrueFoundry
|
||||
|
||||
TrueFoundry provides an enterprise-ready [AI Gateway](https://www.truefoundry.com/ai-gateway) to provide governance and observability to agentic frameworks like LangChain. TrueFoundry AI Gateway serves as a unified interface for LLM access, providing:
|
||||
|
||||
- **Unified API Access**: Connect to 250+ LLMs (OpenAI, Claude, Gemini, Groq, Mistral) through one API
|
||||
- **Low Latency**: Sub-3ms internal latency with intelligent routing and load balancing
|
||||
- **Enterprise Security**: SOC 2, HIPAA, GDPR compliance with RBAC and audit logging
|
||||
- **Quota and cost management**: Token-based quotas, rate limiting, and comprehensive usage tracking
|
||||
- **Observability**: Full request/response logging, metrics, and traces with customizable retention
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before integrating LangChain with TrueFoundry, ensure you have:
|
||||
|
||||
1. **TrueFoundry Account**: A [TrueFoundry account](https://www.truefoundry.com/register) with at least one model provider configured. Follow quick start guide [here](https://docs.truefoundry.com/gateway/quick-start)
|
||||
2. **Personal Access Token**: Generate a token by following the [TrueFoundry token generation guide](https://docs.truefoundry.com/gateway/authentication)
|
||||
|
||||
## Quickstart
|
||||
|
||||
You can connect to TrueFoundry's unified LLM gateway through the `ChatOpenAI` interface.
|
||||
|
||||
- Set the `base_url` to your TrueFoundry endpoint (explained below)
|
||||
- Set the `api_key` to your TrueFoundry [PAT (Personal Access Token)](https://docs.truefoundry.com/gateway/authentication#personal-access-token-pat)
|
||||
- Use the same `model-name` as shown in the unified code snippet
|
||||
|
||||

|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pip install langchain-openai
|
||||
```
|
||||
|
||||
### Basic Setup
|
||||
|
||||
Connect to TrueFoundry by updating the `ChatOpenAI` model in LangChain:
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
llm = ChatOpenAI(
|
||||
api_key=TRUEFOUNDRY_API_KEY,
|
||||
base_url=TRUEFOUNDRY_GATEWAY_BASE_URL,
|
||||
model="openai-main/gpt-4o" # Similarly you can call any model from any model provider
|
||||
)
|
||||
|
||||
llm.invoke("What is the meaning of life, universe and everything?")
|
||||
```
|
||||
|
||||
The request is routed through your TrueFoundry gateway to the specified model provider. TrueFoundry automatically handles rate limiting, load balancing, and observability.
|
||||
|
||||
### LangGraph Integration
|
||||
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import StateGraph, MessagesState
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
# Define your LangGraph workflow
|
||||
def call_model(state: MessagesState):
|
||||
model = ChatOpenAI(
|
||||
api_key=TRUEFOUNDRY_API_KEY,
|
||||
base_url=TRUEFOUNDRY_GATEWAY_BASE_URL,
|
||||
# Copy the exact model name from gateway
|
||||
model="openai-main/gpt-4o"
|
||||
)
|
||||
response = model.invoke(state["messages"])
|
||||
return {"messages": [response]}
|
||||
|
||||
# Build workflow
|
||||
workflow = StateGraph(MessagesState)
|
||||
workflow.add_node("agent", call_model)
|
||||
workflow.set_entry_point("agent")
|
||||
workflow.set_finish_point("agent")
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
# Run agent through TrueFoundry
|
||||
result = app.invoke({"messages": [HumanMessage(content="Hello!")]})
|
||||
```
|
||||
|
||||
|
||||
## Observability and Governance
|
||||
|
||||

|
||||
|
||||
With the Metrics Dashboard, you can monitor and analyze:
|
||||
|
||||
- **Performance Metrics**: Track key latency metrics like Request Latency, Time to First Token (TTFS), and Inter-Token Latency (ITL) with P99, P90, and P50 percentiles
|
||||
- **Cost and Token Usage**: Gain visibility into your application's costs with detailed breakdowns of input/output tokens and the associated expenses for each model
|
||||
- **Usage Patterns**: Understand how your application is being used with detailed analytics on user activity, model distribution, and team-based usage
|
||||
- **Rate Limiting & Load Balancing**: Configure limits, distribute traffic across models, and set up fallbacks
|
||||
|
||||
## Support
|
||||
|
||||
For questions, issues, or support:
|
||||
|
||||
- **Email**: [support@truefoundry.com](mailto:support@truefoundry.com)
|
||||
- **Documentation**: [https://docs.truefoundry.com/](https://docs.truefoundry.com/)
|
||||
@@ -31,7 +31,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install -U oci"
|
||||
"!pip install -U langchain_oci"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -71,7 +71,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_community.embeddings import OCIGenAIEmbeddings\n",
|
||||
"from langchain_oci.embeddings import OCIGenAIEmbeddings\n",
|
||||
"\n",
|
||||
"# use default authN method API-key\n",
|
||||
"embeddings = OCIGenAIEmbeddings(\n",
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
"from langgraph.prebuilt import create_react_agent\n",
|
||||
"\n",
|
||||
"llm = ChatAnthropic(\n",
|
||||
" model=\"claude-3-5-sonnet-20240620\",\n",
|
||||
" model=\"claude-3-5-sonnet-latest\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"langgraph_agent_executor = create_react_agent(llm, stripe_agent_toolkit.get_tools())\n",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -73,8 +73,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "72461be913bfaf2b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instantiation\n",
|
||||
"\n",
|
||||
@@ -83,26 +84,26 @@
|
||||
"Instantiation\n",
|
||||
"The tool accepts various parameters during instantiation:\n",
|
||||
"\n",
|
||||
"- max_results (optional, int): Maximum number of search results to return. Default is 5.\n",
|
||||
"- topic (optional, str): Category of the search. Can be \"general\", \"news\", or \"finance\". Default is \"general\".\n",
|
||||
"- include_answer (optional, bool): Include an answer to original query in results. Default is False.\n",
|
||||
"- include_raw_content (optional, bool): Include cleaned and parsed HTML of each search result. Default is False.\n",
|
||||
"- include_images (optional, bool): Include a list of query related images in the response. Default is False.\n",
|
||||
"- include_image_descriptions (optional, bool): Include descriptive text for each image. Default is False.\n",
|
||||
"- search_depth (optional, str): Depth of the search, either \"basic\" or \"advanced\". Default is \"basic\".\n",
|
||||
"- time_range (optional, str): The time range back from the current date to filter results - \"day\", \"week\", \"month\", or \"year\". Default is None.\n",
|
||||
"- include_domains (optional, List[str]): List of domains to specifically include. Default is None.\n",
|
||||
"- exclude_domains (optional, List[str]): List of domains to specifically exclude. Default is None.\n",
|
||||
"- `max_results` (optional, int): Maximum number of search results to return. Default is 5.\n",
|
||||
"- `topic` (optional, str): Category of the search. Can be `'general'`, `'news'`, or `'finance'`. Default is `'general'`.\n",
|
||||
"- `include_answer` (optional, bool): Include an answer to original query in results. Default is False.\n",
|
||||
"- `include_raw_content` (optional, bool): Include cleaned and parsed HTML of each search result. Default is False.\n",
|
||||
"- `include_images` (optional, bool): Include a list of query related images in the response. Default is False.\n",
|
||||
"- `include_image_descriptions` (optional, bool): Include descriptive text for each image. Default is False.\n",
|
||||
"- `search_depth` (optional, str): Depth of the search, either `'basic'` or `'advanced'`. Default is `'basic'`.\n",
|
||||
"- `time_range` (optional, str): The time range back from the current date to filter results - `'day'`, `'week'`, `'month'`, or `'year'`. Default is None.\n",
|
||||
"- `include_domains` (optional, List[str]): List of domains to specifically include. Default is None.\n",
|
||||
"- `exclude_domains` (optional, List[str]): List of domains to specifically exclude. Default is None.\n",
|
||||
"\n",
|
||||
"For a comprehensive overview of the available parameters, refer to the [Tavily Search API documentation](https://docs.tavily.com/documentation/api-reference/endpoint/search)"
|
||||
],
|
||||
"id": "72461be913bfaf2b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "code",
|
||||
"outputs": [],
|
||||
"execution_count": null,
|
||||
"id": "dc382e5426394836",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain_tavily import TavilySearch\n",
|
||||
"\n",
|
||||
@@ -118,12 +119,12 @@
|
||||
" # include_domains=None,\n",
|
||||
" # exclude_domains=None\n",
|
||||
")"
|
||||
],
|
||||
"id": "dc382e5426394836"
|
||||
]
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "f997d2733b63f655",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Invocation\n",
|
||||
"\n",
|
||||
@@ -134,18 +135,22 @@
|
||||
"- The following arguments can also be set during invocation : `include_images`, `search_depth` , `time_range`, `include_domains`, `exclude_domains`, `include_images`\n",
|
||||
"- For reliability and performance reasons, certain parameters that affect response size cannot be modified during invocation: `include_answer` and `include_raw_content`. These limitations prevent unexpected context window issues and ensure consistent results.\n",
|
||||
"\n",
|
||||
":::note\n",
|
||||
"\n",
|
||||
"NOTE: The optional arguments are available for agents to dynamically set, if you set an argument during instantiation and then invoke the tool with a different value, the tool will use the value you passed during invocation."
|
||||
],
|
||||
"id": "f997d2733b63f655"
|
||||
"The optional arguments are available for agents to dynamically set, if you set an argument during instantiation and then invoke the tool with a different value, the tool will use the value you passed during invocation.\n",
|
||||
"\n",
|
||||
":::"
|
||||
]
|
||||
},
|
||||
{
|
||||
"metadata": {},
|
||||
"cell_type": "code",
|
||||
"outputs": [],
|
||||
"execution_count": null,
|
||||
"source": "tool.invoke({\"query\": \"What happened at the last wimbledon\"})",
|
||||
"id": "5e75399230ab9fc1"
|
||||
"id": "5e75399230ab9fc1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tool.invoke({\"query\": \"What happened at the last wimbledon\"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
@@ -154,7 +159,7 @@
|
||||
"source": [
|
||||
"### [Invoke with ToolCall](/docs/concepts/tools)\n",
|
||||
"\n",
|
||||
"We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:"
|
||||
"We can also invoke the tool with a model-generated `ToolCall`, in which case a `ToolMessage` will be returned:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -233,7 +238,7 @@
|
||||
"id": "1020a506-473b-4e6a-a563-7aaf92c4d183",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We will need to install langgraph:"
|
||||
"We will need to install `langgraph`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -256,21 +261,21 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"================================\u001B[1m Human Message \u001B[0m=================================\n",
|
||||
"================================\u001b[1m Human Message \u001b[0m=================================\n",
|
||||
"\n",
|
||||
"What nation hosted the Euro 2024? Include only wikipedia sources.\n",
|
||||
"==================================\u001B[1m Ai Message \u001B[0m==================================\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"Tool Calls:\n",
|
||||
" tavily_search (call_yxmR4K2uadsQ8LKoyi8JyoLD)\n",
|
||||
" Call ID: call_yxmR4K2uadsQ8LKoyi8JyoLD\n",
|
||||
" Args:\n",
|
||||
" query: Euro 2024 host nation\n",
|
||||
" include_domains: ['wikipedia.org']\n",
|
||||
"=================================\u001B[1m Tool Message \u001B[0m=================================\n",
|
||||
"=================================\u001b[1m Tool Message \u001b[0m=================================\n",
|
||||
"Name: tavily_search\n",
|
||||
"\n",
|
||||
"{\"query\": \"Euro 2024 host nation\", \"follow_up_questions\": null, \"answer\": null, \"images\": [], \"results\": [{\"title\": \"UEFA Euro 2024 - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/UEFA_Euro_2024\", \"content\": \"Tournament details Host country Germany Dates 14 June – 14 July Teams 24 Venue(s) 10 (in 10 host cities) Final positions Champions Spain (4th title) Runners-up England Tournament statistics Matches played 51 Goals scored 117 (2.29 per match) Attendance 2,681,288 (52,574 per match) Top scorer(s) Harry Kane Georges Mikautadze Jamal Musiala Cody Gakpo Ivan Schranz Dani Olmo (3 goals each) Best player(s) Rodri Best young player Lamine Yamal ← 2020 2028 → The 2024 UEFA European Football Championship, commonly referred to as UEFA Euro 2024 (stylised as UEFA EURO 2024) or simply Euro 2024, was the 17th UEFA European Championship, the quadrennial international football championship organised by UEFA for the European men's national teams of their member associations. Germany hosted the tournament, which took place from 14 June to 14 July 2024. The tournament involved 24 teams, with Georgia making their European Championship debut. [4] Host nation Germany were eliminated by Spain in the quarter-finals; Spain went on to win the tournament for a record fourth time after defeating England 2–1 in the final.\", \"score\": 0.9104262, \"raw_content\": null}, {\"title\": \"UEFA Euro 2024 - Simple English Wikipedia, the free encyclopedia\", \"url\": \"https://simple.wikipedia.org/wiki/UEFA_Euro_2024\", \"content\": \"The 2024 UEFA European Football Championship, also known as UEFA Euro 2024 or simply Euro 2024, was the 17th edition of the UEFA European Championship. Germany was hosting the tournament. ... The UEFA Executive Committee voted for the host in a secret ballot, with only a simple majority (more than half of the valid votes) required to determine\", \"score\": 0.81418616, \"raw_content\": null}, {\"title\": \"Championnat d'Europe de football 2024 — Wikipédia\", \"url\": \"https://fr.wikipedia.org/wiki/Championnat_d'Europe_de_football_2024\", \"content\": \"Le Championnat d'Europe de l'UEFA de football 2024 est la 17 e édition du Championnat d'Europe de football, communément abrégé en Euro 2024, compétition organisée par l'UEFA et rassemblant les meilleures équipes nationales masculines européennes. L'Allemagne est désignée pays organisateur de la compétition le 27 septembre 2018. C'est la troisième fois que des matches du Championnat\", \"score\": 0.8055255, \"raw_content\": null}, {\"title\": \"UEFA Euro 2024 bids - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/UEFA_Euro_2024_bids\", \"content\": \"The bidding process of UEFA Euro 2024 ended on 27 September 2018 in Nyon, Switzerland, when Germany was announced to be the host. [1] Two bids came before the deadline, 3 March 2017, which were Germany and Turkey as single bids. ... Press agencies revealed on 24 October 2013, that the European football governing body UEFA would have decided on\", \"score\": 0.7882741, \"raw_content\": null}, {\"title\": \"2024 UEFA European Under-19 Championship - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/2024_UEFA_European_Under-19_Championship\", \"content\": \"The 2024 UEFA European Under-19 Championship (also known as UEFA Under-19 Euro 2024) was the 21st edition of the UEFA European Under-19 Championship (71st edition if the Under-18 and Junior eras are included), the annual international youth football championship organised by UEFA for the men's under-19 national teams of Europe. Northern Ireland hosted the tournament from 15 to 28 July 2024.\", \"score\": 0.7783298, \"raw_content\": null}], \"response_time\": 1.67}\n",
|
||||
"==================================\u001B[1m Ai Message \u001B[0m==================================\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"The nation that hosted Euro 2024 was Germany. You can find more information on the [Wikipedia page for UEFA Euro 2024](https://en.wikipedia.org/wiki/UEFA_Euro_2024).\n"
|
||||
]
|
||||
@@ -304,8 +309,14 @@
|
||||
"source": [
|
||||
"## API reference\n",
|
||||
"\n",
|
||||
"For detailed documentation of all Tavily Search API features and configurations head to the API reference: https://docs.tavily.com/documentation/api-reference/endpoint/search"
|
||||
"For detailed documentation of all Tavily Search API features and configurations head to the [API reference](https://docs.tavily.com/documentation/api-reference/endpoint/search)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "589ff839",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"\n",
|
||||
"This notebook covers how to get started with the `Chroma` vector store.\n",
|
||||
"\n",
|
||||
">[Chroma](https://docs.trychroma.com/getting-started) is a AI-native open-source vector database focused on developer productivity and happiness. Chroma is licensed under Apache 2.0. View the full docs of `Chroma` at [this page](https://docs.trychroma.com/reference/py-collection), and find the API reference for the LangChain integration at [this page](https://python.langchain.com/api_reference/chroma/vectorstores/langchain_chroma.vectorstores.Chroma.html).\n",
|
||||
">[Chroma](https://docs.trychroma.com/getting-started) is a AI-native open-source vector database focused on developer productivity and happiness. Chroma is licensed under Apache 2.0. View the full docs of `Chroma` at [this page](https://docs.trychroma.com/integrations/frameworks/langchain), and find the API reference for the LangChain integration at [this page](https://python.langchain.com/api_reference/chroma/vectorstores/langchain_chroma.vectorstores.Chroma.html).\n",
|
||||
"\n",
|
||||
":::info Chroma Cloud\n",
|
||||
"\n",
|
||||
@@ -522,6 +522,39 @@
|
||||
"vector_store.delete(ids=uuids[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "675b3708-b5ef-4298-b950-eac27096b456",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Fork a vector store\n",
|
||||
"\n",
|
||||
"Forking lets you create a new `Chroma` vector store from an existing one instantly, using copy-on-write under the hood. This means that your new `Chroma` store is identical to the origin, but any modifications to it will not affect the origin, and vice-versa.\n",
|
||||
"\n",
|
||||
"Forks are great for any use case that benefits from data versioning. You can learn more about forking in the [Chroma docs](https://docs.trychroma.com/cloud/collection-forking).\n",
|
||||
"\n",
|
||||
"Note: Forking is only avaiable on `Chroma` instances with a Chroma Cloud connection."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e08a0c79-4d2a-49ff-be63-d8591c268764",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"forked_store = vector_store.fork(new_name=\"my_forked_collection\")\n",
|
||||
"\n",
|
||||
"updated_document_2 = Document(\n",
|
||||
" page_content=\"The weather forecast for tomorrow is extrmeley hot, with a high of 100 degrees.\",\n",
|
||||
" metadata={\"source\": \"news\"},\n",
|
||||
" id=2,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Update does not affect 'vector_store'\n",
|
||||
"forked_store.update(ids=[\"2\"], documents=[updated_document_2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "213acf08",
|
||||
@@ -609,7 +642,7 @@
|
||||
"source": [
|
||||
"#### Other search methods\n",
|
||||
"\n",
|
||||
"There are a variety of other search methods that are not covered in this notebook, such as MMR search or searching by vector. For a full list of the search abilities available for `AstraDBVectorStore` check out the [API reference](https://python.langchain.com/api_reference/astradb/vectorstores/langchain_astradb.vectorstores.AstraDBVectorStore.html).\n",
|
||||
"There are a variety of other search methods that are not covered in this notebook. For a full list of the search abilities available for `Chroma` check out the [API reference](https://python.langchain.com/api_reference/chroma/vectorstores/langchain_chroma.vectorstores.Chroma.html).\n",
|
||||
"\n",
|
||||
"### Query by turning into retriever\n",
|
||||
"\n",
|
||||
@@ -670,7 +703,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.0"
|
||||
"version": "3.13.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! docker run -d -p 8123:8123 -p9000:9000 --name langchain-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server:24.7.6.8"
|
||||
"! docker run -d -p 8123:8123 -p 9000:9000 --name langchain-clickhouse-server --ulimit nofile=262144:262144 -e CLICKHOUSE_SKIP_USER_SETUP=1 clickhouse/clickhouse-server:25.7"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -310,7 +310,8 @@
|
||||
" where_str=f\"{meta}.source = 'tweet'\",\n",
|
||||
")\n",
|
||||
"for res in results:\n",
|
||||
" print(f\"* {res.page_content} [{res.metadata}]\")"
|
||||
" page_content, metadata = res\n",
|
||||
" print(f\"* {page_content} [{metadata}]\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ LangChain simplifies every stage of the LLM application lifecycle:
|
||||
- **Development**: Build your applications using LangChain's open-source [components](/docs/concepts) and [third-party integrations](/docs/integrations/providers/).
|
||||
Use [LangGraph](/docs/concepts/architecture/#langgraph) to build stateful agents with first-class streaming and human-in-the-loop support.
|
||||
- **Productionization**: Use [LangSmith](https://docs.smith.langchain.com/) to inspect, monitor and evaluate your applications, so that you can continuously optimize and deploy with confidence.
|
||||
- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/).
|
||||
- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Platform](https://docs.langchain.com/langgraph-platform).
|
||||
|
||||
import ThemedImage from '@theme/ThemedImage';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
@@ -104,7 +104,7 @@ Head to the reference section for full documentation of all classes and methods
|
||||
Trace and evaluate your language model applications and intelligent agents to help you move from prototype to production.
|
||||
|
||||
### [🦜🕸️ LangGraph](https://langchain-ai.github.io/langgraph)
|
||||
Build stateful, multi-actor applications with LLMs. Integrates smoothly with LangChain, but can be used without it. LangGraph powers production-grade agents, trusted by Linkedin, Uber, Klarna, GitLab, and many more.
|
||||
Build stateful, multi-actor applications with LLMs. Integrates smoothly with LangChain, but can be used without it. LangGraph powers production-grade agents, trusted by LinkedIn, Uber, Klarna, GitLab, and many more.
|
||||
|
||||
## Additional resources
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -74,7 +74,7 @@
|
||||
"\n",
|
||||
"uncoercible_message = {\"role\": \"HumanMessage\", \"random_field\": \"random value\"}\n",
|
||||
"\n",
|
||||
"model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n",
|
||||
"model = ChatAnthropic(model=\"claude-3-5-sonnet-latest\")\n",
|
||||
"\n",
|
||||
"model.invoke([uncoercible_message])"
|
||||
]
|
||||
@@ -88,7 +88,7 @@
|
||||
"The following may help resolve this error:\n",
|
||||
"\n",
|
||||
"- Ensure that all inputs to chat models are an array of LangChain message classes or a supported message-like.\n",
|
||||
" - Check that there is no stringification or other unexpected transformation occuring.\n",
|
||||
" - Check that there is no stringification or other unexpected transformation occurring.\n",
|
||||
"- Check the error's stack trace and add log or debugger statements."
|
||||
]
|
||||
},
|
||||
|
||||
@@ -720,7 +720,7 @@
|
||||
" AIMessage(content='yes!', additional_kwargs={}, response_metadata={})]"
|
||||
]
|
||||
},
|
||||
"execution_count": 23,
|
||||
"execution_count": 109,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -771,8 +771,13 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def call_model(state: State):\n",
|
||||
" print(f\"Messages before trimming: {len(state['messages'])}\")\n",
|
||||
" # highlight-start\n",
|
||||
" trimmed_messages = trimmer.invoke(state[\"messages\"])\n",
|
||||
" print(f\"Messages after trimming: {len(trimmed_messages)}\")\n",
|
||||
" print(\"Remaining messages:\")\n",
|
||||
" for msg in trimmed_messages:\n",
|
||||
" print(f\" {type(msg).__name__}: {msg.content}\")\n",
|
||||
" prompt = prompt_template.invoke(\n",
|
||||
" {\"messages\": trimmed_messages, \"language\": state[\"language\"]}\n",
|
||||
" )\n",
|
||||
@@ -792,7 +797,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now if we try asking the model our name, it won't know it since we trimmed that part of the chat history:"
|
||||
"Now if we try asking the model our name, it won't know it since we trimmed that part of the chat history. (By defining our trim stragegy as `'last'`, we are only keeping the most recent messages that fit within the `max_tokens`.)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -804,9 +809,20 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Messages before trimming: 12\n",
|
||||
"Messages after trimming: 8\n",
|
||||
"Remaining messages:\n",
|
||||
" SystemMessage: you're a good assistant\n",
|
||||
" HumanMessage: whats 2 + 2\n",
|
||||
" AIMessage: 4\n",
|
||||
" HumanMessage: thanks\n",
|
||||
" AIMessage: no problem!\n",
|
||||
" HumanMessage: having fun?\n",
|
||||
" AIMessage: yes!\n",
|
||||
" HumanMessage: What is my name?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"I don't know your name. You haven't told me yet!\n"
|
||||
"I don't know your name. If you'd like to share it, feel free!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -840,15 +856,27 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Messages before trimming: 12\n",
|
||||
"Messages after trimming: 8\n",
|
||||
"Remaining messages:\n",
|
||||
" SystemMessage: you're a good assistant\n",
|
||||
" HumanMessage: whats 2 + 2\n",
|
||||
" AIMessage: 4\n",
|
||||
" HumanMessage: thanks\n",
|
||||
" AIMessage: no problem!\n",
|
||||
" HumanMessage: having fun?\n",
|
||||
" AIMessage: yes!\n",
|
||||
" HumanMessage: What math problem was asked?\n",
|
||||
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
|
||||
"\n",
|
||||
"You asked what 2 + 2 equals.\n"
|
||||
"The math problem that was asked was \"what's 2 + 2.\"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"config = {\"configurable\": {\"thread_id\": \"abc678\"}}\n",
|
||||
"query = \"What math problem did I ask?\"\n",
|
||||
"\n",
|
||||
"query = \"What math problem was asked?\"\n",
|
||||
"language = \"English\"\n",
|
||||
"\n",
|
||||
"input_messages = messages + [HumanMessage(query)]\n",
|
||||
@@ -890,9 +918,9 @@
|
||||
"text": [
|
||||
"|Hi| Todd|!| Here|’s| a| joke| for| you|:\n",
|
||||
"\n",
|
||||
"|Why| don|’t| skeleton|s| fight| each| other|?\n",
|
||||
"|Why| don't| scientists| trust| atoms|?\n",
|
||||
"\n",
|
||||
"|Because| they| don|’t| have| the| guts|!||"
|
||||
"|Because| they| make| up| everything|!||"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pip install --upgrade --quiet langchain-core"
|
||||
"pip install -U langchain-core"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -89,7 +89,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 3,
|
||||
"id": "39f3ce3e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -124,7 +124,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 4,
|
||||
"id": "5509b6a6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -134,7 +134,7 @@
|
||||
"Classification(sentiment='positive', aggressiveness=1, language='Spanish')"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -157,17 +157,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 4,
|
||||
"id": "9154474c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'sentiment': 'enojado', 'aggressiveness': 8, 'language': 'es'}"
|
||||
"{'sentiment': 'angry', 'aggressiveness': 8, 'language': 'Spanish'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -218,7 +218,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 5,
|
||||
"id": "6a5f7961",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -237,7 +237,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"execution_count": 6,
|
||||
"id": "e5a5881f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@@ -268,17 +268,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 7,
|
||||
"id": "d9b9d53d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Classification(sentiment='positive', aggressiveness=1, language='Spanish')"
|
||||
"Classification(sentiment='happy', aggressiveness=1, language='spanish')"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -291,17 +291,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 8,
|
||||
"id": "1c12fa00",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Classification(sentiment='enojado', aggressiveness=8, language='es')"
|
||||
"Classification(sentiment='sad', aggressiveness=4, language='spanish')"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -314,17 +314,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 9,
|
||||
"id": "0bdfcb05",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"Classification(sentiment='neutral', aggressiveness=1, language='English')"
|
||||
"Classification(sentiment='happy', aggressiveness=1, language='english')"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -359,7 +359,7 @@
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"display_name": "langchain-monorepo",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
@@ -373,7 +373,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
"version": "3.12.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -44,4 +44,4 @@ You can peruse [LangSmith tutorials here](https://docs.smith.langchain.com/).
|
||||
|
||||
LangSmith helps you evaluate the performance of your LLM applications. The tutorial below is a great way to get started:
|
||||
|
||||
- [Evaluate your LLM application](https://docs.smith.langchain.com/tutorials/Developers/evaluation)
|
||||
- [Evaluate your LLM application](https://docs.langchain.com/langsmith/evaluate-llm-application)
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": null,
|
||||
"id": "1b2481f0",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -178,8 +178,8 @@
|
||||
"from langchain_core.messages import HumanMessage, SystemMessage\n",
|
||||
"\n",
|
||||
"messages = [\n",
|
||||
" SystemMessage(\"Translate the following from English into Italian\"),\n",
|
||||
" HumanMessage(\"hi!\"),\n",
|
||||
" SystemMessage(content=\"Translate the following from English into Italian\"),\n",
|
||||
" HumanMessage(content=\"hi!\"),\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"model.invoke(messages)"
|
||||
@@ -192,7 +192,7 @@
|
||||
"source": [
|
||||
":::tip\n",
|
||||
"\n",
|
||||
"If we've enabled LangSmith, we can see that this run is logged to LangSmith, and can see the [LangSmith trace](https://smith.langchain.com/public/88baa0b2-7c1a-4d09-ba30-a47985dde2ea/r). The LangSmith trace reports [token](/docs/concepts/tokens/) usage information, latency, [standard model parameters](/docs/concepts/chat_models/#standard-parameters) (such as temperature), and other information.\n",
|
||||
"If we've enabled LangSmith, we can see that this run is logged to LangSmith, and can see the [LangSmith trace](https://docs.smith.langchain.com/observability/concepts#traces). The LangSmith trace reports [token](/docs/concepts/tokens/) usage information, latency, [standard model parameters](/docs/concepts/chat_models/#standard-parameters) (such as temperature), and other information.\n",
|
||||
"\n",
|
||||
":::\n",
|
||||
"\n",
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
"We can use [create_stuff_documents_chain](https://python.langchain.com/api_reference/langchain/chains/langchain.chains.combine_documents.stuff.create_stuff_documents_chain.html), especially if using larger context window models such as:\n",
|
||||
"\n",
|
||||
"* 128k token OpenAI `gpt-4o` \n",
|
||||
"* 200k token Anthropic `claude-3-5-sonnet-20240620`\n",
|
||||
"* 200k token Anthropic `claude-3-5-sonnet-latest`\n",
|
||||
"\n",
|
||||
"The chain will take a list of documents, insert them all into a prompt, and pass that prompt to an LLM:"
|
||||
]
|
||||
|
||||
@@ -142,8 +142,7 @@ const config = {
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
announcementBar: {
|
||||
content:
|
||||
'<strong>Our <a href="https://academy.langchain.com/courses/ambient-agents/?utm_medium=internal&utm_source=docs&utm_campaign=q2-2025_ambient-agents_co" target="_blank">Building Ambient Agents with LangGraph</a> course is now available on LangChain Academy!</strong>',
|
||||
content: "Our new LangChain Academy Course Deep Research with LangGraph is now live! <a href='https://academy.langchain.com/courses/deep-research-with-langgraph/?utm_medium=internal&utm_source=docs&utm_campaign=q3-2025_deep-research-course_co' target='_blank'>Enroll for free</a>.",
|
||||
backgroundColor: "#d0c9fe",
|
||||
},
|
||||
prism: {
|
||||
|
||||
@@ -5,6 +5,14 @@ echo "VERCEL_GIT_COMMIT_REF: $VERCEL_GIT_COMMIT_REF"
|
||||
echo "VERCEL_GIT_REPO_OWNER: $VERCEL_GIT_REPO_OWNER"
|
||||
echo "VERCEL_GIT_REPO_SLUG: $VERCEL_GIT_REPO_SLUG"
|
||||
|
||||
echo "Checking for skip-preview tags..."
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=%B)
|
||||
echo "Commit message: $COMMIT_MESSAGE"
|
||||
if [[ "$COMMIT_MESSAGE" == *"[skip-preview]"* ]] || [[ "$COMMIT_MESSAGE" == *"[no-preview]"* ]] || [[ "$COMMIT_MESSAGE" == *"[skip-deploy]"* ]]; then
|
||||
echo "🛑 Skip-preview tag found in commit message - skipping preview deployment"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if { \
|
||||
[ "$VERCEL_ENV" == "production" ] || \
|
||||
@@ -13,10 +21,10 @@ if { \
|
||||
[ "$VERCEL_GIT_COMMIT_REF" == "v0.2" ] || \
|
||||
[ "$VERCEL_GIT_COMMIT_REF" == "v0.3rc" ]; \
|
||||
} && [ "$VERCEL_GIT_REPO_OWNER" == "langchain-ai" ]
|
||||
then
|
||||
then
|
||||
echo "✅ Production build - proceeding with build"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
echo "Checking for changes in docs/"
|
||||
|
||||
@@ -27,7 +27,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'tutorials/index'},
|
||||
link: { type: 'doc', id: 'tutorials/index' },
|
||||
label: "Tutorials",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
@@ -38,7 +38,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'how_to/index'},
|
||||
link: { type: 'doc', id: 'how_to/index' },
|
||||
label: "How-to guides",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
@@ -49,7 +49,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'concepts/index'},
|
||||
link: { type: 'doc', id: 'concepts/index' },
|
||||
label: "Conceptual guide",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
@@ -103,7 +103,7 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "Migrating from v0.0 chains",
|
||||
link: {type: 'doc', id: 'versions/migrating_chains/index'},
|
||||
link: { type: 'doc', id: 'versions/migrating_chains/index' },
|
||||
collapsible: false,
|
||||
collapsed: false,
|
||||
items: [{
|
||||
@@ -115,7 +115,7 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "Upgrading to LangGraph memory",
|
||||
link: {type: 'doc', id: 'versions/migrating_memory/index'},
|
||||
link: { type: 'doc', id: 'versions/migrating_memory/index' },
|
||||
collapsible: false,
|
||||
collapsed: false,
|
||||
items: [{
|
||||
@@ -418,7 +418,7 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
],
|
||||
link: {
|
||||
type: "generated-index",
|
||||
@@ -434,7 +434,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'contributing/tutorials/index'},
|
||||
link: { type: 'doc', id: 'contributing/tutorials/index' },
|
||||
label: "Tutorials",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
@@ -445,7 +445,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'contributing/how_to/index'},
|
||||
link: { type: 'doc', id: 'contributing/how_to/index' },
|
||||
label: "How-to guides",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
@@ -456,7 +456,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
link: {type: 'doc', id: 'contributing/reference/index'},
|
||||
link: { type: 'doc', id: 'contributing/reference/index' },
|
||||
label: "Reference & FAQ",
|
||||
collapsible: false,
|
||||
items: [{
|
||||
|
||||
@@ -118,7 +118,8 @@ export default function ChatModelTabs(props) {
|
||||
{
|
||||
value: "anthropic",
|
||||
label: "Anthropic",
|
||||
model: "claude-3-5-sonnet-latest",
|
||||
model: "claude-3-7-sonnet-20250219",
|
||||
comment: "# Note: Model versions may become outdated. Check https://docs.anthropic.com/en/docs/models-overview for latest versions",
|
||||
apiKeyName: "ANTHROPIC_API_KEY",
|
||||
packageName: "langchain[anthropic]",
|
||||
},
|
||||
@@ -269,6 +270,9 @@ if not os.environ.get("${selectedTabItem.apiKeyName}"):
|
||||
|
||||
${llmVarName} = init_chat_model("${selectedTabItem.model}", model_provider="${selectedTabItem.value}"${selectedTabItem?.kwargs ? `, ${selectedTabItem.kwargs}` : ""})`;
|
||||
|
||||
// Add comment if available
|
||||
const commentText = selectedTabItem?.comment ? selectedTabItem.comment + "\n\n" : "";
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CustomDropdown
|
||||
@@ -282,7 +286,7 @@ ${llmVarName} = init_chat_model("${selectedTabItem.model}", model_provider="${se
|
||||
{`pip install -qU "${selectedTabItem.packageName}"`}
|
||||
</CodeBlock>
|
||||
<CodeBlock language="python">
|
||||
{apiKeyText ? apiKeyText + "\n\n" + initModelText : initModelText}
|
||||
{apiKeyText ? apiKeyText + "\n\n" + commentText + initModelText : commentText + initModelText}
|
||||
</CodeBlock>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -856,6 +856,13 @@ const FEATURE_TABLES = {
|
||||
source: "Web interaction and structured data extraction from any web page using an AgentQL query or a Natural Language prompt",
|
||||
api: "API",
|
||||
apiLink: "https://python.langchain.com/docs/integrations/document_loaders/agentql/"
|
||||
},
|
||||
{
|
||||
name: "Oxylabs",
|
||||
link: "oxylabs",
|
||||
source: "Web intelligence platform enabling the access to various data sources.",
|
||||
api: "API",
|
||||
apiLink: "https://github.com/oxylabs/langchain-oxylabs"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
BIN
docs/static/img/gateway-metrics.png
vendored
Normal file
BIN
docs/static/img/gateway-metrics.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 530 KiB |
BIN
docs/static/img/unified-code-tfy.png
vendored
Normal file
BIN
docs/static/img/unified-code-tfy.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 408 KiB |
@@ -11,3 +11,5 @@ numpy>=1.26.0,<2.0.0
|
||||
simsimd>=5.0.0
|
||||
# Fix sentencepiece build error - use newer version that supports modern CMake
|
||||
sentencepiece>=0.2.1
|
||||
# Fix langchain-azure-ai dependency conflict with langchain-core
|
||||
langchain-core @ file:///home/runner/work/langchain/langchain/langchain/libs/core
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""LangChain CLI."""
|
||||
|
||||
from langchain_cli._version import __version__
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""LangChain CLI."""
|
||||
|
||||
from typing import Annotated, Optional
|
||||
|
||||
import typer
|
||||
@@ -34,20 +36,21 @@ app.command(
|
||||
)
|
||||
|
||||
|
||||
def version_callback(show_version: bool) -> None: # noqa: FBT001
|
||||
def _version_callback(*, show_version: bool) -> None:
|
||||
if show_version:
|
||||
typer.echo(f"langchain-cli {__version__}")
|
||||
raise typer.Exit
|
||||
|
||||
|
||||
@app.callback()
|
||||
def main(
|
||||
version: bool = typer.Option( # noqa: FBT001
|
||||
def _main(
|
||||
*,
|
||||
version: bool = typer.Option(
|
||||
False, # noqa: FBT003
|
||||
"--version",
|
||||
"-v",
|
||||
help="Print the current CLI version.",
|
||||
callback=version_callback,
|
||||
callback=_version_callback,
|
||||
is_eager=True,
|
||||
),
|
||||
) -> None:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""LangChain CLI constants."""
|
||||
|
||||
DEFAULT_GIT_REPO = "https://github.com/langchain-ai/langchain.git"
|
||||
DEFAULT_GIT_SUBDIRECTORY = "templates"
|
||||
DEFAULT_GIT_REF = "master"
|
||||
|
||||
@@ -13,7 +13,7 @@ def create_demo_server(
|
||||
*,
|
||||
config_keys: Sequence[str] = (),
|
||||
playground_type: Literal["default", "chat"] = "default",
|
||||
):
|
||||
) -> FastAPI:
|
||||
"""Create a demo server for the current template."""
|
||||
app = FastAPI()
|
||||
package_root = get_package_root()
|
||||
@@ -40,9 +40,11 @@ def create_demo_server(
|
||||
return app
|
||||
|
||||
|
||||
def create_demo_server_configurable():
|
||||
def create_demo_server_configurable() -> FastAPI:
|
||||
"""Create a configurable demo server."""
|
||||
return create_demo_server(config_keys=["configurable"])
|
||||
|
||||
|
||||
def create_demo_server_chat():
|
||||
def create_demo_server_chat() -> FastAPI:
|
||||
"""Create a chat demo server."""
|
||||
return create_demo_server(playground_type="chat")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Namespaces."""
|
||||
|
||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
||||
from typing import Annotated, Optional
|
||||
|
||||
import typer
|
||||
import uvicorn
|
||||
|
||||
from langchain_cli.utils.events import create_events
|
||||
from langchain_cli.utils.git import (
|
||||
@@ -71,9 +72,7 @@ def new(
|
||||
name_str = name
|
||||
pip_bool = bool(pip) # None should be false
|
||||
else:
|
||||
name_str = (
|
||||
name if name else typer.prompt("What folder would you like to create?")
|
||||
)
|
||||
name_str = name or typer.prompt("What folder would you like to create?")
|
||||
if not has_packages:
|
||||
package = []
|
||||
package_prompt = "What package would you like to add? (leave blank to skip)"
|
||||
@@ -261,7 +260,7 @@ def add(
|
||||
cmd = ["pip", "install", "-e", *installed_destination_strs]
|
||||
cmd_str = " \\\n ".join(installed_destination_strs)
|
||||
typer.echo(f"Running: pip install -e \\\n {cmd_str}")
|
||||
subprocess.run(cmd, cwd=cwd) # noqa: S603
|
||||
subprocess.run(cmd, cwd=cwd, check=True) # noqa: S603
|
||||
|
||||
chain_names = []
|
||||
for e in installed_exports:
|
||||
@@ -367,8 +366,6 @@ def serve(
|
||||
app_str = app if app is not None else "app.server:app"
|
||||
host_str = host if host is not None else "127.0.0.1"
|
||||
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
app_str,
|
||||
host=host_str,
|
||||
|
||||
@@ -15,6 +15,8 @@ integration_cli = typer.Typer(no_args_is_help=True, add_completion=False)
|
||||
|
||||
|
||||
class Replacements(TypedDict):
|
||||
"""Replacements."""
|
||||
|
||||
__package_name__: str
|
||||
__module_name__: str
|
||||
__ModuleName__: str
|
||||
@@ -127,6 +129,7 @@ def new(
|
||||
subprocess.run(
|
||||
["poetry", "install", "--with", "lint,test,typing,test_integration"], # noqa: S607
|
||||
cwd=destination_dir,
|
||||
check=True,
|
||||
)
|
||||
else:
|
||||
# confirm src and dst are the same length
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Migrations."""
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Generate migrations."""
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
from types import ModuleType
|
||||
|
||||
|
||||
def generate_raw_migrations(
|
||||
@@ -89,7 +90,7 @@ def generate_top_level_imports(pkg: str) -> list[tuple[str, str]]:
|
||||
items = []
|
||||
|
||||
# Function to handle importing from modules
|
||||
def handle_module(module, module_name) -> None:
|
||||
def handle_module(module: ModuleType, module_name: str) -> None:
|
||||
if hasattr(module, "__all__"):
|
||||
all_objects = module.__all__
|
||||
for name in all_objects:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
"""Migration as Grit file."""
|
||||
|
||||
|
||||
def split_package(package: str) -> tuple[str, str]:
|
||||
"""Split a package name into the containing package and the final name."""
|
||||
parts = package.split(".")
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
"""Generate migrations utilities."""
|
||||
|
||||
import ast
|
||||
import inspect
|
||||
import os
|
||||
import pathlib
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from types import ModuleType
|
||||
from typing import Optional
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
# Should bring us to [root]/src
|
||||
@@ -15,12 +20,15 @@ PARTNER_PKGS = PKGS_ROOT / "partners"
|
||||
|
||||
|
||||
class ImportExtractor(ast.NodeVisitor):
|
||||
"""Import extractor."""
|
||||
|
||||
def __init__(self, *, from_package: Optional[str] = None) -> None:
|
||||
"""Extract all imports from the given code, optionally filtering by package."""
|
||||
self.imports: list = []
|
||||
self.imports: list[tuple[str, str]] = []
|
||||
self.package = from_package
|
||||
|
||||
def visit_ImportFrom(self, node) -> None: # noqa: N802
|
||||
@override
|
||||
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
||||
if node.module and (
|
||||
self.package is None or str(node.module).startswith(self.package)
|
||||
):
|
||||
@@ -39,7 +47,8 @@ def _get_class_names(code: str) -> list[str]:
|
||||
|
||||
# Define a node visitor class to collect class names
|
||||
class ClassVisitor(ast.NodeVisitor):
|
||||
def visit_ClassDef(self, node) -> None: # noqa: N802
|
||||
@override
|
||||
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
||||
class_names.append(node.name)
|
||||
self.generic_visit(node)
|
||||
|
||||
@@ -49,7 +58,7 @@ def _get_class_names(code: str) -> list[str]:
|
||||
return class_names
|
||||
|
||||
|
||||
def is_subclass(class_obj: Any, classes_: list[type]) -> bool:
|
||||
def is_subclass(class_obj: type, classes_: list[type]) -> bool:
|
||||
"""Check if the given class object is a subclass of any class in list classes."""
|
||||
return any(
|
||||
issubclass(class_obj, kls)
|
||||
@@ -58,7 +67,7 @@ def is_subclass(class_obj: Any, classes_: list[type]) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def find_subclasses_in_module(module, classes_: list[type]) -> list[str]:
|
||||
def find_subclasses_in_module(module: ModuleType, classes_: list[type]) -> list[str]:
|
||||
"""Find all classes in the module that inherit from one of the classes."""
|
||||
subclasses = []
|
||||
# Iterate over all attributes of the module that are classes
|
||||
@@ -70,8 +79,7 @@ def find_subclasses_in_module(module, classes_: list[type]) -> list[str]:
|
||||
|
||||
def _get_all_classnames_from_file(file: Path, pkg: str) -> list[tuple[str, str]]:
|
||||
"""Extract all class names from a file."""
|
||||
with open(file, encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
code = Path(file).read_text(encoding="utf-8")
|
||||
module_name = _get_current_module(file, pkg)
|
||||
class_names = _get_class_names(code)
|
||||
|
||||
@@ -84,8 +92,7 @@ def identify_all_imports_in_file(
|
||||
from_package: Optional[str] = None,
|
||||
) -> list[tuple[str, str]]:
|
||||
"""Let's also identify all the imports in the given file."""
|
||||
with open(file, encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
code = Path(file).read_text(encoding="utf-8")
|
||||
return find_imports_from_package(code, from_package=from_package)
|
||||
|
||||
|
||||
@@ -143,6 +150,7 @@ def find_imports_from_package(
|
||||
*,
|
||||
from_package: Optional[str] = None,
|
||||
) -> list[tuple[str, str]]:
|
||||
"""Find imports in code."""
|
||||
# Parse the code into an AST
|
||||
tree = ast.parse(code)
|
||||
# Create an instance of the visitor
|
||||
@@ -154,8 +162,7 @@ def find_imports_from_package(
|
||||
|
||||
def _get_current_module(path: Path, pkg_root: str) -> str:
|
||||
"""Convert a path to a module name."""
|
||||
path_as_pathlib = pathlib.Path(os.path.abspath(path))
|
||||
relative_path = path_as_pathlib.relative_to(pkg_root).with_suffix("")
|
||||
relative_path = path.relative_to(pkg_root).with_suffix("")
|
||||
posix_path = relative_path.as_posix()
|
||||
norm_path = os.path.normpath(str(posix_path))
|
||||
fully_qualified_module = norm_path.replace("/", ".")
|
||||
|
||||
@@ -4,7 +4,7 @@ from pathlib import Path
|
||||
|
||||
import rich
|
||||
import typer
|
||||
from gritql import run # type: ignore[import]
|
||||
from gritql import run # type: ignore[import-untyped]
|
||||
from typer import Option
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ def migrate(
|
||||
final_code = run.apply_pattern(
|
||||
"langchain_all_migrations()",
|
||||
args,
|
||||
grit_dir=get_gritdir_path(),
|
||||
grit_dir=str(get_gritdir_path()),
|
||||
)
|
||||
|
||||
raise typer.Exit(code=final_code)
|
||||
|
||||
@@ -7,7 +7,9 @@ from pathlib import Path
|
||||
from typing import Annotated, Optional
|
||||
|
||||
import typer
|
||||
import uvicorn
|
||||
|
||||
from langchain_cli.utils.github import list_packages
|
||||
from langchain_cli.utils.packages import get_langserve_export, get_package_root
|
||||
|
||||
package_cli = typer.Typer(no_args_is_help=True, add_completion=False)
|
||||
@@ -32,7 +34,7 @@ def new(
|
||||
package_name_split = computed_name.split("/")
|
||||
package_name = (
|
||||
package_name_split[-2]
|
||||
if len(package_name_split) > 1 and package_name_split[-1] == ""
|
||||
if len(package_name_split) > 1 and not package_name_split[-1]
|
||||
else package_name_split[-1]
|
||||
)
|
||||
module_name = re.sub(
|
||||
@@ -79,7 +81,7 @@ def new(
|
||||
|
||||
# poetry install
|
||||
if with_poetry:
|
||||
subprocess.run(["poetry", "install"], cwd=destination_dir) # noqa: S607
|
||||
subprocess.run(["poetry", "install"], cwd=destination_dir, check=True) # noqa: S607
|
||||
|
||||
|
||||
@package_cli.command()
|
||||
@@ -128,8 +130,6 @@ def serve(
|
||||
)
|
||||
)
|
||||
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
script,
|
||||
factory=True,
|
||||
@@ -142,8 +142,6 @@ def serve(
|
||||
@package_cli.command()
|
||||
def list(contains: Annotated[Optional[str], typer.Argument()] = None) -> None: # noqa: A001
|
||||
"""List all or search for available templates."""
|
||||
from langchain_cli.utils.github import list_packages
|
||||
|
||||
packages = list_packages(contains=contains)
|
||||
for package in packages:
|
||||
typer.echo(package)
|
||||
|
||||
@@ -6,7 +6,7 @@ app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def redirect_root_to_docs():
|
||||
async def redirect_root_to_docs() -> RedirectResponse:
|
||||
return RedirectResponse("/docs")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Utilities."""
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Events utilities."""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
from typing import Any, Optional, TypedDict
|
||||
@@ -8,11 +10,19 @@ WRITE_KEY = "310apTK0HUFl4AOv"
|
||||
|
||||
|
||||
class EventDict(TypedDict):
|
||||
"""Event data structure for analytics tracking.
|
||||
|
||||
Attributes:
|
||||
event: The name of the event.
|
||||
properties: Optional dictionary of event properties.
|
||||
"""
|
||||
|
||||
event: str
|
||||
properties: Optional[dict[str, Any]]
|
||||
|
||||
|
||||
def create_events(events: list[EventDict]) -> Optional[Any]:
|
||||
def create_events(events: list[EventDict]) -> Optional[dict[str, Any]]:
|
||||
"""Create events."""
|
||||
try:
|
||||
data = {
|
||||
"events": [
|
||||
@@ -38,7 +48,8 @@ def create_events(events: list[EventDict]) -> Optional[Any]:
|
||||
|
||||
res = conn.getresponse()
|
||||
|
||||
return json.loads(res.read())
|
||||
response_data = json.loads(res.read())
|
||||
return response_data if isinstance(response_data, dict) else None
|
||||
except (http.client.HTTPException, OSError, json.JSONDecodeError) as exc:
|
||||
typer.echo(f"Error sending events: {exc}")
|
||||
return None
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"""Find and replace text in files."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def find_and_replace(source: str, replacements: dict[str, str]) -> str:
|
||||
"""Find and replace text in a string."""
|
||||
rtn = source
|
||||
|
||||
# replace keys in deterministic alphabetical order
|
||||
@@ -13,6 +16,7 @@ def find_and_replace(source: str, replacements: dict[str, str]) -> str:
|
||||
|
||||
|
||||
def replace_file(source: Path, replacements: dict[str, str]) -> None:
|
||||
"""Replace text in a file."""
|
||||
try:
|
||||
content = source.read_text()
|
||||
except UnicodeDecodeError:
|
||||
@@ -24,6 +28,7 @@ def replace_file(source: Path, replacements: dict[str, str]) -> None:
|
||||
|
||||
|
||||
def replace_glob(parent: Path, glob: str, replacements: dict[str, str]) -> None:
|
||||
"""Replace text in files matching a glob pattern."""
|
||||
for file in parent.glob(glob):
|
||||
if not file.is_file():
|
||||
continue
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""Git utilities."""
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Optional, TypedDict
|
||||
from typing import Any, Optional, TypedDict
|
||||
|
||||
from git import Repo
|
||||
|
||||
@@ -13,13 +16,17 @@ from langchain_cli.constants import (
|
||||
DEFAULT_GIT_SUBDIRECTORY,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DependencySource(TypedDict):
|
||||
"""Dependency source information."""
|
||||
|
||||
git: str
|
||||
ref: Optional[str]
|
||||
subdirectory: Optional[str]
|
||||
api_path: Optional[str]
|
||||
event_metadata: dict
|
||||
event_metadata: dict[str, Any]
|
||||
|
||||
|
||||
# use poetry dependency string format
|
||||
@@ -29,6 +36,7 @@ def parse_dependency_string(
|
||||
branch: Optional[str],
|
||||
api_path: Optional[str],
|
||||
) -> DependencySource:
|
||||
"""Parse a dependency string into a DependencySource."""
|
||||
if dep is not None and dep.startswith("git+"):
|
||||
if repo is not None or branch is not None:
|
||||
msg = (
|
||||
@@ -121,6 +129,7 @@ def parse_dependencies(
|
||||
branch: list[str],
|
||||
api_path: list[str],
|
||||
) -> list[DependencySource]:
|
||||
"""Parse dependencies."""
|
||||
num_deps = max(
|
||||
len(dependencies) if dependencies is not None else 0,
|
||||
len(repo),
|
||||
@@ -129,8 +138,8 @@ def parse_dependencies(
|
||||
if (
|
||||
(dependencies and len(dependencies) != num_deps)
|
||||
or (api_path and len(api_path) != num_deps)
|
||||
or (repo and len(repo) not in [1, num_deps])
|
||||
or (branch and len(branch) not in [1, num_deps])
|
||||
or (repo and len(repo) not in {1, num_deps})
|
||||
or (branch and len(branch) not in {1, num_deps})
|
||||
):
|
||||
msg = (
|
||||
"Number of defined repos/branches/api_paths did not match the "
|
||||
@@ -142,15 +151,15 @@ def parse_dependencies(
|
||||
inner_repos = _list_arg_to_length(repo, num_deps)
|
||||
inner_branches = _list_arg_to_length(branch, num_deps)
|
||||
|
||||
return [
|
||||
parse_dependency_string(iter_dep, iter_repo, iter_branch, iter_api_path)
|
||||
for iter_dep, iter_repo, iter_branch, iter_api_path in zip(
|
||||
return list(
|
||||
map(
|
||||
parse_dependency_string,
|
||||
inner_deps,
|
||||
inner_repos,
|
||||
inner_branches,
|
||||
inner_api_paths,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _get_repo_path(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
|
||||
@@ -158,7 +167,7 @@ def _get_repo_path(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
|
||||
ref_str = ref if ref is not None else ""
|
||||
hashed = hashlib.sha256((f"{gitstring}:{ref_str}").encode()).hexdigest()[:8]
|
||||
|
||||
removed_protocol = gitstring.split("://")[-1]
|
||||
removed_protocol = gitstring.split("://", maxsplit=1)[-1]
|
||||
removed_basename = re.split(r"[/:]", removed_protocol, maxsplit=1)[-1]
|
||||
removed_extras = removed_basename.split("#")[0]
|
||||
foldername = re.sub(r"\W", "_", removed_extras)
|
||||
@@ -168,22 +177,22 @@ def _get_repo_path(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
|
||||
|
||||
|
||||
def update_repo(gitstring: str, ref: Optional[str], repo_dir: Path) -> Path:
|
||||
"""Update a git repository to the specified ref."""
|
||||
# see if path already saved
|
||||
repo_path = _get_repo_path(gitstring, ref, repo_dir)
|
||||
if repo_path.exists():
|
||||
# try pulling
|
||||
try:
|
||||
repo = Repo(repo_path)
|
||||
if repo.active_branch.name != ref:
|
||||
raise ValueError
|
||||
repo.remotes.origin.pull()
|
||||
if repo.active_branch.name == ref:
|
||||
repo.remotes.origin.pull()
|
||||
return repo_path
|
||||
except Exception:
|
||||
# if it fails, delete and clone again
|
||||
shutil.rmtree(repo_path)
|
||||
Repo.clone_from(gitstring, repo_path, branch=ref, depth=1)
|
||||
else:
|
||||
Repo.clone_from(gitstring, repo_path, branch=ref, depth=1)
|
||||
logger.exception("Failed to pull existing repo")
|
||||
# if it fails, delete and clone again
|
||||
shutil.rmtree(repo_path)
|
||||
|
||||
Repo.clone_from(gitstring, repo_path, branch=ref, depth=1)
|
||||
return repo_path
|
||||
|
||||
|
||||
@@ -196,7 +205,7 @@ def copy_repo(
|
||||
Raises FileNotFound error if it can't find source
|
||||
"""
|
||||
|
||||
def ignore_func(_, files):
|
||||
def ignore_func(_: str, files: list[str]) -> list[str]:
|
||||
return [f for f in files if f == ".git"]
|
||||
|
||||
shutil.copytree(source, destination, ignore=ignore_func)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""GitHub utilities."""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def list_packages(*, contains: Optional[str] = None) -> list[str]:
|
||||
"""List all packages in the langchain repository templates directory."""
|
||||
conn = http.client.HTTPSConnection("api.github.com")
|
||||
try:
|
||||
headers = {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Packages utilities."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, TypedDict
|
||||
|
||||
@@ -5,6 +7,7 @@ from tomlkit import load
|
||||
|
||||
|
||||
def get_package_root(cwd: Optional[Path] = None) -> Path:
|
||||
"""Get package root directory."""
|
||||
# traverse path for routes to host (any directory holding a pyproject.toml file)
|
||||
package_root = Path.cwd() if cwd is None else cwd
|
||||
visited: set[Path] = set()
|
||||
@@ -35,7 +38,8 @@ class LangServeExport(TypedDict):
|
||||
|
||||
|
||||
def get_langserve_export(filepath: Path) -> LangServeExport:
|
||||
with open(filepath) as f:
|
||||
"""Get LangServe export information from a pyproject.toml file."""
|
||||
with filepath.open() as f:
|
||||
data: dict[str, Any] = load(f)
|
||||
try:
|
||||
module = data["tool"]["langserve"]["export_module"]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Pyproject.toml utilities."""
|
||||
|
||||
import contextlib
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
@@ -18,7 +20,7 @@ def add_dependencies_to_pyproject_toml(
|
||||
local_editable_dependencies: Iterable[tuple[str, Path]],
|
||||
) -> None:
|
||||
"""Add dependencies to pyproject.toml."""
|
||||
with open(pyproject_toml, encoding="utf-8") as f:
|
||||
with pyproject_toml.open(encoding="utf-8") as f:
|
||||
# tomlkit types aren't amazing - treat as Dict instead
|
||||
pyproject: dict[str, Any] = load(f)
|
||||
pyproject["tool"]["poetry"]["dependencies"].update(
|
||||
@@ -27,7 +29,7 @@ def add_dependencies_to_pyproject_toml(
|
||||
for name, loc in local_editable_dependencies
|
||||
},
|
||||
)
|
||||
with open(pyproject_toml, "w", encoding="utf-8") as f:
|
||||
with pyproject_toml.open("w", encoding="utf-8") as f:
|
||||
dump(pyproject, f)
|
||||
|
||||
|
||||
@@ -36,12 +38,13 @@ def remove_dependencies_from_pyproject_toml(
|
||||
local_editable_dependencies: Iterable[str],
|
||||
) -> None:
|
||||
"""Remove dependencies from pyproject.toml."""
|
||||
with open(pyproject_toml, encoding="utf-8") as f:
|
||||
with pyproject_toml.open(encoding="utf-8") as f:
|
||||
pyproject: dict[str, Any] = load(f)
|
||||
# tomlkit types aren't amazing - treat as Dict instead
|
||||
dependencies = pyproject["tool"]["poetry"]["dependencies"]
|
||||
for name in local_editable_dependencies:
|
||||
with contextlib.suppress(KeyError):
|
||||
del dependencies[name]
|
||||
with open(pyproject_toml, "w", encoding="utf-8") as f:
|
||||
|
||||
with pyproject_toml.open("w", encoding="utf-8") as f:
|
||||
dump(pyproject, f)
|
||||
|
||||
@@ -7,7 +7,7 @@ authors = [{ name = "Erick Friis", email = "erick@langchain.dev" }]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"typer[all]<1.0.0,>=0.9.0",
|
||||
"typer<1.0.0,>=0.9.0",
|
||||
"gitpython<4,>=3",
|
||||
"langserve[all]>=0.0.51",
|
||||
"uvicorn<1.0,>=0.23",
|
||||
@@ -15,7 +15,7 @@ dependencies = [
|
||||
"gritql<1.0.0,>=0.2.0",
|
||||
]
|
||||
name = "langchain-cli"
|
||||
version = "0.0.36"
|
||||
version = "0.0.37"
|
||||
description = "CLI for interacting with LangChain"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -29,8 +29,8 @@ langchain = "langchain_cli.cli:app"
|
||||
langchain-cli = "langchain_cli.cli:app"
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["pytest<8.0.0,>=7.4.2", "pytest-watcher<1.0.0,>=0.3.4"]
|
||||
lint = ["ruff<0.13,>=0.12.2", "mypy<2.0.0,>=1.13.0"]
|
||||
dev = ["pytest<9.0.0,>=7.4.2", "pytest-watcher<1.0.0,>=0.3.4"]
|
||||
lint = ["ruff<0.13,>=0.12.2", "mypy<1.18,>=1.17.1"]
|
||||
test = ["langchain-core", "langchain"]
|
||||
typing = ["langchain"]
|
||||
test_integration = []
|
||||
@@ -48,59 +48,45 @@ exclude = [
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"A", # flake8-builtins
|
||||
"B", # flake8-bugbear
|
||||
"ARG", # flake8-unused-arguments
|
||||
"ASYNC", # flake8-async
|
||||
"C4", # flake8-comprehensions
|
||||
"COM", # flake8-commas
|
||||
"D", # pydocstyle
|
||||
"E", # pycodestyle error
|
||||
"EM", # flake8-errmsg
|
||||
"F", # pyflakes
|
||||
"FA", # flake8-future-annotations
|
||||
"FBT", # flake8-boolean-trap
|
||||
"FLY", # flake8-flynt
|
||||
"I", # isort
|
||||
"ICN", # flake8-import-conventions
|
||||
"INT", # flake8-gettext
|
||||
"ISC", # isort-comprehensions
|
||||
"N", # pep8-naming
|
||||
"PT", # flake8-pytest-style
|
||||
"PGH", # pygrep-hooks
|
||||
"PIE", # flake8-pie
|
||||
"PERF", # flake8-perf
|
||||
"PYI", # flake8-pyi
|
||||
"Q", # flake8-quotes
|
||||
"RET", # flake8-return
|
||||
"RSE", # flake8-rst-docstrings
|
||||
"RUF", # ruff
|
||||
"S", # flake8-bandit
|
||||
"SLF", # flake8-self
|
||||
"SLOT", # flake8-slots
|
||||
"SIM", # flake8-simplify
|
||||
"T10", # flake8-debugger
|
||||
"T20", # flake8-print
|
||||
"TID", # flake8-tidy-imports
|
||||
"UP", # pyupgrade
|
||||
"W", # pycodestyle warning
|
||||
"YTT", # flake8-2020
|
||||
]
|
||||
select = [ "ALL",]
|
||||
ignore = [
|
||||
"D100", # pydocstyle: Missing docstring in public module
|
||||
"D101", # pydocstyle: Missing docstring in public class
|
||||
"D102", # pydocstyle: Missing docstring in public method
|
||||
"D103", # pydocstyle: Missing docstring in public function
|
||||
"D104", # pydocstyle: Missing docstring in public package
|
||||
"D105", # pydocstyle: Missing docstring in magic method
|
||||
"D107", # pydocstyle: Missing docstring in __init__
|
||||
"D407", # pydocstyle: Missing-dashed-underline-after-section
|
||||
"C90", # McCabe complexity
|
||||
"COM812", # Messes with the formatter
|
||||
"FIX002", # Line contains TODO
|
||||
"PERF203", # Rarely useful
|
||||
"PLR09", # Too many something (arg, statements, etc)
|
||||
"RUF012", # Doesn't play well with Pydantic
|
||||
"TC001", # Doesn't play well with Pydantic
|
||||
"TC002", # Doesn't play well with Pydantic
|
||||
"TC003", # Doesn't play well with Pydantic
|
||||
"TD002", # Missing author in TODO
|
||||
"TD003", # Missing issue link in TODO
|
||||
|
||||
# TODO rules
|
||||
"BLE",
|
||||
]
|
||||
unfixable = [
|
||||
"B028", # People should intentionally tune the stacklevel
|
||||
"PLW1510", # People should intentionally set the check argument
|
||||
]
|
||||
|
||||
flake8-annotations.allow-star-arg-any = true
|
||||
flake8-annotations.mypy-init-return = true
|
||||
flake8-type-checking.runtime-evaluated-base-classes = ["pydantic.BaseModel","langchain_core.load.serializable.Serializable","langchain_core.runnables.base.RunnableSerializable"]
|
||||
pep8-naming.classmethod-decorators = [ "classmethod", "langchain_core.utils.pydantic.pre_init", "pydantic.field_validator", "pydantic.v1.root_validator",]
|
||||
pydocstyle.convention = "google"
|
||||
pyupgrade.keep-runtime-typing = true
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/**" = [ "D1", "S", "SLF",]
|
||||
"scripts/**" = [ "INP", "S",]
|
||||
|
||||
[tool.mypy]
|
||||
plugins = ["pydantic.mypy"]
|
||||
strict = true
|
||||
enable_error_code = "deprecated"
|
||||
warn_unreachable = true
|
||||
|
||||
exclude = [
|
||||
"langchain_cli/integration_template",
|
||||
"langchain_cli/package_template",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Scripts."""
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Script to generate migrations for the migration script."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import pkgutil
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import click
|
||||
@@ -52,7 +52,7 @@ def cli() -> None:
|
||||
def generic(
|
||||
pkg1: str,
|
||||
pkg2: str,
|
||||
output: str,
|
||||
output: Optional[str],
|
||||
filter_by_all: bool, # noqa: FBT001
|
||||
format_: str,
|
||||
) -> None:
|
||||
@@ -73,19 +73,18 @@ def generic(
|
||||
else:
|
||||
dumped = dump_migrations_as_grit(name, migrations)
|
||||
|
||||
with open(output, "w") as f:
|
||||
f.write(dumped)
|
||||
Path(output).write_text(dumped, encoding="utf-8")
|
||||
|
||||
|
||||
def handle_partner(pkg: str, output: Optional[str] = None) -> None:
|
||||
"""Handle partner package migrations."""
|
||||
migrations = get_migrations_for_partner_package(pkg)
|
||||
# Run with python 3.9+
|
||||
name = pkg.removeprefix("langchain_")
|
||||
data = dump_migrations_as_grit(name, migrations)
|
||||
output_name = f"{name}.grit" if output is None else output
|
||||
if migrations:
|
||||
with open(output_name, "w") as f:
|
||||
f.write(data)
|
||||
Path(output_name).write_text(data, encoding="utf-8")
|
||||
click.secho(f"LangChain migration script saved to {output_name}")
|
||||
else:
|
||||
click.secho(f"No migrations found for {pkg}", fg="yellow")
|
||||
@@ -104,13 +103,13 @@ def partner(pkg: str, output: str) -> None:
|
||||
@click.argument("json_file")
|
||||
def json_to_grit(json_file: str) -> None:
|
||||
"""Generate a Grit migration from an old JSON migration file."""
|
||||
with open(json_file) as f:
|
||||
file = Path(json_file)
|
||||
with file.open() as f:
|
||||
migrations = json.load(f)
|
||||
name = os.path.basename(json_file).removesuffix(".json").removesuffix(".grit")
|
||||
name = file.stem
|
||||
data = dump_migrations_as_grit(name, migrations)
|
||||
output_name = f"{name}.grit"
|
||||
with open(output_name, "w") as f:
|
||||
f.write(data)
|
||||
Path(output_name).write_text(data, encoding="utf-8")
|
||||
click.secho(f"GritQL migration script saved to {output_name}")
|
||||
|
||||
|
||||
|
||||
0
libs/cli/tests/integration_tests/__init__.py
Normal file
0
libs/cli/tests/integration_tests/__init__.py
Normal file
@@ -14,3 +14,6 @@ class File:
|
||||
return False
|
||||
|
||||
return self.content == __value.content
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, self.content))
|
||||
|
||||
@@ -57,3 +57,6 @@ class Folder:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, tuple(self.files)))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -16,7 +16,7 @@ def _assert_dependency_equals(
|
||||
git: Optional[str] = None,
|
||||
ref: Optional[str] = None,
|
||||
subdirectory: Optional[str] = None,
|
||||
event_metadata: Optional[dict] = None,
|
||||
event_metadata: Optional[dict[str, Any]] = None,
|
||||
) -> None:
|
||||
if dep["git"] != git:
|
||||
msg = f"Expected git to be {git} but got {dep['git']}"
|
||||
|
||||
111
libs/cli/uv.lock
generated
111
libs/cli/uv.lock
generated
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
revision = 3
|
||||
requires-python = ">=3.9"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
@@ -425,15 +425,11 @@ dependencies = [
|
||||
requires-dist = [
|
||||
{ name = "async-timeout", marker = "python_full_version < '3.11'", specifier = ">=4.0.0,<5.0.0" },
|
||||
{ name = "langchain-anthropic", marker = "extra == 'anthropic'" },
|
||||
{ name = "langchain-aws", marker = "extra == 'aws'" },
|
||||
{ name = "langchain-azure-ai", marker = "extra == 'azure-ai'" },
|
||||
{ name = "langchain-cohere", marker = "extra == 'cohere'" },
|
||||
{ name = "langchain-community", marker = "extra == 'community'" },
|
||||
{ name = "langchain-core", editable = "../core" },
|
||||
{ name = "langchain-deepseek", marker = "extra == 'deepseek'" },
|
||||
{ name = "langchain-fireworks", marker = "extra == 'fireworks'" },
|
||||
{ name = "langchain-google-genai", marker = "extra == 'google-genai'" },
|
||||
{ name = "langchain-google-vertexai", marker = "extra == 'google-vertexai'" },
|
||||
{ name = "langchain-groq", marker = "extra == 'groq'" },
|
||||
{ name = "langchain-huggingface", marker = "extra == 'huggingface'" },
|
||||
{ name = "langchain-mistralai", marker = "extra == 'mistralai'" },
|
||||
@@ -442,14 +438,13 @@ requires-dist = [
|
||||
{ name = "langchain-perplexity", marker = "extra == 'perplexity'" },
|
||||
{ name = "langchain-text-splitters", editable = "../text-splitters" },
|
||||
{ name = "langchain-together", marker = "extra == 'together'" },
|
||||
{ name = "langchain-xai", marker = "extra == 'xai'" },
|
||||
{ name = "langsmith", specifier = ">=0.1.17" },
|
||||
{ name = "pydantic", specifier = ">=2.7.4,<3.0.0" },
|
||||
{ name = "pyyaml", specifier = ">=5.3" },
|
||||
{ name = "requests", specifier = ">=2,<3" },
|
||||
{ name = "sqlalchemy", specifier = ">=1.4,<3" },
|
||||
]
|
||||
provides-extras = ["community", "anthropic", "openai", "azure-ai", "cohere", "google-vertexai", "google-genai", "fireworks", "ollama", "together", "mistralai", "huggingface", "groq", "aws", "deepseek", "xai", "perplexity"]
|
||||
provides-extras = ["community", "anthropic", "openai", "google-genai", "fireworks", "ollama", "together", "mistralai", "huggingface", "groq", "deepseek", "perplexity"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
codespell = [{ name = "codespell", specifier = ">=2.2.0,<3.0.0" }]
|
||||
@@ -520,7 +515,7 @@ typing = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-cli"
|
||||
version = "0.0.36"
|
||||
version = "0.0.37"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "gitpython" },
|
||||
@@ -554,17 +549,17 @@ requires-dist = [
|
||||
{ name = "gritql", specifier = ">=0.2.0,<1.0.0" },
|
||||
{ name = "langserve", extras = ["all"], specifier = ">=0.0.51" },
|
||||
{ name = "tomlkit", specifier = ">=0.12" },
|
||||
{ name = "typer", extras = ["all"], specifier = ">=0.9.0,<1.0.0" },
|
||||
{ name = "typer", specifier = ">=0.9.0,<1.0.0" },
|
||||
{ name = "uvicorn", specifier = ">=0.23,<1.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "pytest", specifier = ">=7.4.2,<8.0.0" },
|
||||
{ name = "pytest", specifier = ">=7.4.2,<9.0.0" },
|
||||
{ name = "pytest-watcher", specifier = ">=0.3.4,<1.0.0" },
|
||||
]
|
||||
lint = [
|
||||
{ name = "mypy", specifier = ">=1.13.0,<2.0.0" },
|
||||
{ name = "mypy", specifier = ">=1.17.1,<1.18" },
|
||||
{ name = "ruff", specifier = ">=0.12.2,<0.13" },
|
||||
]
|
||||
test = [
|
||||
@@ -576,7 +571,7 @@ typing = [{ name = "langchain", editable = "../langchain" }]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.3.72"
|
||||
version = "0.3.75"
|
||||
source = { editable = "../core" }
|
||||
dependencies = [
|
||||
{ name = "jsonpatch" },
|
||||
@@ -627,14 +622,14 @@ test = [
|
||||
test-integration = []
|
||||
typing = [
|
||||
{ name = "langchain-text-splitters", directory = "../text-splitters" },
|
||||
{ name = "mypy", specifier = ">=1.15,<1.16" },
|
||||
{ name = "mypy", specifier = ">=1.17.1,<1.18" },
|
||||
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
|
||||
{ name = "types-requests", specifier = ">=2.28.11.5,<3.0.0.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "langchain-text-splitters"
|
||||
version = "0.3.9"
|
||||
version = "0.3.11"
|
||||
source = { editable = "../text-splitters" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -650,7 +645,7 @@ dev = [
|
||||
]
|
||||
lint = [
|
||||
{ name = "langchain-core", editable = "../core" },
|
||||
{ name = "ruff", specifier = ">=0.12.2,<0.13" },
|
||||
{ name = "ruff", specifier = ">=0.12.8,<0.13" },
|
||||
]
|
||||
test = [
|
||||
{ name = "freezegun", specifier = ">=1.2.2,<2.0.0" },
|
||||
@@ -663,15 +658,17 @@ test = [
|
||||
{ name = "pytest-xdist", specifier = ">=3.6.1,<4.0.0" },
|
||||
]
|
||||
test-integration = [
|
||||
{ name = "en-core-web-sm", url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl" },
|
||||
{ name = "nltk", specifier = ">=3.9.1,<4.0.0" },
|
||||
{ name = "sentence-transformers", specifier = ">=3.0.1" },
|
||||
{ name = "spacy", specifier = ">=3.8.7,<4.0.0" },
|
||||
{ name = "thinc", specifier = ">=8.3.6,<9.0.0" },
|
||||
{ name = "tiktoken", specifier = ">=0.8.0,<1.0.0" },
|
||||
{ name = "transformers", specifier = ">=4.51.3,<5.0.0" },
|
||||
]
|
||||
typing = [
|
||||
{ name = "lxml-stubs", specifier = ">=0.5.1,<1.0.0" },
|
||||
{ name = "mypy", specifier = ">=1.15,<2.0" },
|
||||
{ name = "mypy", specifier = ">=1.17.1,<1.18" },
|
||||
{ name = "tiktoken", specifier = ">=0.8.0,<1.0.0" },
|
||||
{ name = "types-requests", specifier = ">=2.31.0.20240218,<3.0.0.0" },
|
||||
]
|
||||
@@ -738,46 +735,53 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.14.1"
|
||||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051, upload-time = "2024-12-30T16:39:07.335Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002, upload-time = "2024-12-30T16:37:22.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400, upload-time = "2024-12-30T16:37:53.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172, upload-time = "2024-12-30T16:37:50.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732, upload-time = "2024-12-30T16:37:29.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197, upload-time = "2024-12-30T16:38:05.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836, upload-time = "2024-12-30T16:37:19.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432, upload-time = "2024-12-30T16:37:11.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515, upload-time = "2024-12-30T16:37:40.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791, upload-time = "2024-12-30T16:36:58.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203, upload-time = "2024-12-30T16:37:03.741Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900, upload-time = "2024-12-30T16:37:57.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869, upload-time = "2024-12-30T16:37:33.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668, upload-time = "2024-12-30T16:38:02.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060, upload-time = "2024-12-30T16:37:46.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167, upload-time = "2024-12-30T16:37:43.534Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341, upload-time = "2024-12-30T16:37:36.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991, upload-time = "2024-12-30T16:37:06.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016, upload-time = "2024-12-30T16:37:15.02Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097, upload-time = "2024-12-30T16:37:25.144Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728, upload-time = "2024-12-30T16:38:08.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965, upload-time = "2024-12-30T16:38:12.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660, upload-time = "2024-12-30T16:38:17.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198, upload-time = "2024-12-30T16:38:32.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276, upload-time = "2024-12-30T16:38:20.828Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/1f/186d133ae2514633f8558e78cd658070ba686c0e9275c5a5c24a1e1f0d67/mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", size = 11200493, upload-time = "2024-12-30T16:38:26.935Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/fc/4842485d034e38a4646cccd1369f6b1ccd7bc86989c52770d75d719a9941/mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", size = 10357702, upload-time = "2024-12-30T16:38:50.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/e6/457b83f2d701e23869cfec013a48a12638f75b9d37612a9ddf99072c1051/mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", size = 12091104, upload-time = "2024-12-30T16:38:53.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/bf/76a569158db678fee59f4fd30b8e7a0d75bcbaeef49edd882a0d63af6d66/mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", size = 12830167, upload-time = "2024-12-30T16:38:56.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/bc/0bc6b694b3103de9fed61867f1c8bd33336b913d16831431e7cb48ef1c92/mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", size = 13013834, upload-time = "2024-12-30T16:38:59.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/79/5f5ec47849b6df1e6943d5fd8e6632fbfc04b4fd4acfa5a5a9535d11b4e2/mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", size = 9781231, upload-time = "2024-12-30T16:39:05.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905, upload-time = "2024-12-30T16:38:42.021Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/cb/673e3d34e5d8de60b3a61f44f80150a738bff568cd6b7efb55742a605e98/mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9", size = 10992466, upload-time = "2025-07-31T07:53:57.574Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/d0/fe1895836eea3a33ab801561987a10569df92f2d3d4715abf2cfeaa29cb2/mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99", size = 10117638, upload-time = "2025-07-31T07:53:34.256Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/f3/514aa5532303aafb95b9ca400a31054a2bd9489de166558c2baaeea9c522/mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8", size = 11915673, upload-time = "2025-07-31T07:52:59.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/c3/c0805f0edec96fe8e2c048b03769a6291523d509be8ee7f56ae922fa3882/mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8", size = 12649022, upload-time = "2025-07-31T07:53:45.92Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/3e/d646b5a298ada21a8512fa7e5531f664535a495efa672601702398cea2b4/mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259", size = 12895536, upload-time = "2025-07-31T07:53:06.17Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/55/e13d0dcd276975927d1f4e9e2ec4fd409e199f01bdc671717e673cc63a22/mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d", size = 9512564, upload-time = "2025-07-31T07:53:12.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -871,6 +875,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.5.0"
|
||||
|
||||
@@ -21,13 +21,13 @@ For full documentation see the [API reference](https://python.langchain.com/api_
|
||||
|
||||
## 1️⃣ Core Interface: Runnables
|
||||
|
||||
The concept of a Runnable is central to LangChain Core – it is the interface that most LangChain Core components implement, giving them
|
||||
The concept of a `Runnable` is central to LangChain Core – it is the interface that most LangChain Core components implement, giving them
|
||||
|
||||
- a common invocation interface (invoke, batch, stream, etc.)
|
||||
- a common invocation interface (`invoke()`, `batch()`, `stream()`, etc.)
|
||||
- built-in utilities for retries, fallbacks, schemas and runtime configurability
|
||||
- easy deployment with [LangServe](https://github.com/langchain-ai/langserve)
|
||||
- easy deployment with [LangGraph](https://github.com/langchain-ai/langgraph)
|
||||
|
||||
For more check out the [runnable docs](https://python.langchain.com/docs/expression_language/interface). Examples of components that implement the interface include: LLMs, Chat Models, Prompts, Retrievers, Tools, Output Parsers.
|
||||
For more check out the [runnable docs](https://python.langchain.com/docs/concepts/runnables/). Examples of components that implement the interface include: LLMs, Chat Models, Prompts, Retrievers, Tools, Output Parsers.
|
||||
|
||||
You can use LangChain Core objects in two ways:
|
||||
|
||||
@@ -51,7 +51,7 @@ LangChain Expression Language (LCEL) is a _declarative language_ for composing L
|
||||
|
||||
LangChain Core compiles LCEL sequences to an _optimized execution plan_, with automatic parallelization, streaming, tracing, and async support.
|
||||
|
||||
For more check out the [LCEL docs](https://python.langchain.com/docs/expression_language/).
|
||||
For more check out the [LCEL docs](https://python.langchain.com/docs/concepts/lcel/).
|
||||
|
||||

|
||||
|
||||
@@ -59,8 +59,6 @@ For more advanced use cases, also check out [LangGraph](https://github.com/langc
|
||||
|
||||
## 📕 Releases & Versioning
|
||||
|
||||
`langchain-core` is currently on version `0.1.x`.
|
||||
|
||||
As `langchain-core` contains the base abstractions and runtime for the whole LangChain ecosystem, we will communicate any breaking changes with advance notice and version bumps. The exception for this is anything in `langchain_core.beta`. The reason for `langchain_core.beta` is that given the rate of change of the field, being able to move quickly is still a priority, and this module is our attempt to do so.
|
||||
|
||||
Minor version increases will occur for:
|
||||
|
||||
@@ -253,7 +253,7 @@ class ContextSet(RunnableSerializable):
|
||||
"""
|
||||
if key is not None:
|
||||
kwargs[key] = value
|
||||
super().__init__( # type: ignore[call-arg]
|
||||
super().__init__(
|
||||
keys={
|
||||
k: _coerce_set_value(v) if v is not None else None
|
||||
for k, v in kwargs.items()
|
||||
|
||||
@@ -277,7 +277,7 @@ class Document(BaseMedia):
|
||||
"""Pass page_content in as positional or named arg."""
|
||||
# my-py is complaining that page_content is not defined on the base class.
|
||||
# Here, we're relying on pydantic base class to handle the validation.
|
||||
super().__init__(page_content=page_content, **kwargs) # type: ignore[call-arg]
|
||||
super().__init__(page_content=page_content, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def is_lc_serializable(cls) -> bool:
|
||||
|
||||
@@ -78,6 +78,11 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
|
||||
if hasattr(error, "response"):
|
||||
response = error.response
|
||||
metadata: dict = {}
|
||||
if hasattr(response, "json"):
|
||||
try:
|
||||
metadata["body"] = response.json()
|
||||
except Exception:
|
||||
metadata["body"] = getattr(response, "text", None)
|
||||
if hasattr(response, "headers"):
|
||||
try:
|
||||
metadata["headers"] = dict(response.headers)
|
||||
@@ -533,7 +538,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
generations = [generations_with_error_metadata]
|
||||
run_manager.on_llm_error(
|
||||
e,
|
||||
response=LLMResult(generations=generations), # type: ignore[arg-type]
|
||||
response=LLMResult(generations=generations),
|
||||
)
|
||||
raise
|
||||
|
||||
@@ -627,7 +632,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
generations = [generations_with_error_metadata]
|
||||
await run_manager.on_llm_error(
|
||||
e,
|
||||
response=LLMResult(generations=generations), # type: ignore[arg-type]
|
||||
response=LLMResult(generations=generations),
|
||||
)
|
||||
raise
|
||||
|
||||
@@ -842,17 +847,17 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
run_managers[i].on_llm_error(
|
||||
e,
|
||||
response=LLMResult(
|
||||
generations=[generations_with_error_metadata] # type: ignore[list-item]
|
||||
generations=[generations_with_error_metadata]
|
||||
),
|
||||
)
|
||||
raise
|
||||
flattened_outputs = [
|
||||
LLMResult(generations=[res.generations], llm_output=res.llm_output) # type: ignore[list-item]
|
||||
LLMResult(generations=[res.generations], llm_output=res.llm_output)
|
||||
for res in results
|
||||
]
|
||||
llm_output = self._combine_llm_outputs([res.llm_output for res in results])
|
||||
generations = [res.generations for res in results]
|
||||
output = LLMResult(generations=generations, llm_output=llm_output) # type: ignore[arg-type]
|
||||
output = LLMResult(generations=generations, llm_output=llm_output)
|
||||
if run_managers:
|
||||
run_infos = []
|
||||
for manager, flattened_output in zip(run_managers, flattened_outputs):
|
||||
@@ -962,7 +967,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
await run_managers[i].on_llm_error(
|
||||
res,
|
||||
response=LLMResult(
|
||||
generations=[generations_with_error_metadata] # type: ignore[list-item]
|
||||
generations=[generations_with_error_metadata]
|
||||
),
|
||||
)
|
||||
exceptions.append(res)
|
||||
@@ -972,7 +977,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
*[
|
||||
run_manager.on_llm_end(
|
||||
LLMResult(
|
||||
generations=[res.generations], # type: ignore[list-item, union-attr]
|
||||
generations=[res.generations], # type: ignore[union-attr]
|
||||
llm_output=res.llm_output, # type: ignore[union-attr]
|
||||
)
|
||||
)
|
||||
@@ -982,12 +987,12 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
)
|
||||
raise exceptions[0]
|
||||
flattened_outputs = [
|
||||
LLMResult(generations=[res.generations], llm_output=res.llm_output) # type: ignore[list-item, union-attr]
|
||||
LLMResult(generations=[res.generations], llm_output=res.llm_output) # type: ignore[union-attr]
|
||||
for res in results
|
||||
]
|
||||
llm_output = self._combine_llm_outputs([res.llm_output for res in results]) # type: ignore[union-attr]
|
||||
generations = [res.generations for res in results] # type: ignore[union-attr]
|
||||
output = LLMResult(generations=generations, llm_output=llm_output) # type: ignore[arg-type]
|
||||
output = LLMResult(generations=generations, llm_output=llm_output)
|
||||
await asyncio.gather(
|
||||
*[
|
||||
run_manager.on_llm_end(flattened_output)
|
||||
|
||||
@@ -358,7 +358,10 @@ class AIMessageChunk(AIMessage, BaseMessageChunk):
|
||||
|
||||
for chunk in self.tool_call_chunks:
|
||||
try:
|
||||
args_ = parse_partial_json(chunk["args"]) if chunk["args"] != "" else {} # type: ignore[arg-type]
|
||||
if chunk["args"] is not None and chunk["args"] != "":
|
||||
args_ = parse_partial_json(chunk["args"])
|
||||
else:
|
||||
args_ = {}
|
||||
if isinstance(args_, dict):
|
||||
tool_calls.append(
|
||||
create_tool_call(
|
||||
|
||||
@@ -246,6 +246,8 @@ class JsonOutputKeyToolsParser(JsonOutputToolsParser):
|
||||
_ = tool_call.pop("id")
|
||||
else:
|
||||
try:
|
||||
# This exists purely for backward compatibility / cached messages
|
||||
# All new messages should use `message.tool_calls`
|
||||
raw_tool_calls = copy.deepcopy(message.additional_kwargs["tool_calls"])
|
||||
except KeyError:
|
||||
if self.first_tool_only:
|
||||
|
||||
@@ -155,9 +155,7 @@ class MessagesPlaceholder(BaseMessagePromptTemplate):
|
||||
"""
|
||||
# mypy can't detect the init which is defined in the parent class
|
||||
# b/c these are BaseModel classes.
|
||||
super().__init__( # type: ignore[call-arg]
|
||||
variable_name=variable_name, optional=optional, **kwargs
|
||||
)
|
||||
super().__init__(variable_name=variable_name, optional=optional, **kwargs)
|
||||
|
||||
def format_messages(self, **kwargs: Any) -> list[BaseMessage]:
|
||||
"""Format messages from kwargs.
|
||||
|
||||
@@ -2819,7 +2819,7 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
||||
if len(steps_flat) < 2:
|
||||
msg = f"RunnableSequence must have at least 2 steps, got {len(steps_flat)}"
|
||||
raise ValueError(msg)
|
||||
super().__init__( # type: ignore[call-arg]
|
||||
super().__init__(
|
||||
first=steps_flat[0],
|
||||
middle=list(steps_flat[1:-1]),
|
||||
last=steps_flat[-1],
|
||||
@@ -3612,7 +3612,7 @@ class RunnableParallel(RunnableSerializable[Input, dict[str, Any]]):
|
||||
"""
|
||||
merged = {**steps__} if steps__ is not None else {}
|
||||
merged.update(kwargs)
|
||||
super().__init__( # type: ignore[call-arg]
|
||||
super().__init__(
|
||||
steps__={key: coerce_to_runnable(r) for key, r in merged.items()}
|
||||
)
|
||||
|
||||
@@ -5325,7 +5325,7 @@ class RunnableEach(RunnableEachBase[Input, Output]):
|
||||
)
|
||||
|
||||
|
||||
class RunnableBindingBase(RunnableSerializable[Input, Output]):
|
||||
class RunnableBindingBase(RunnableSerializable[Input, Output]): # type: ignore[no-redef]
|
||||
"""``Runnable`` that delegates calls to another ``Runnable`` with a set of kwargs.
|
||||
|
||||
Use only if creating a new ``RunnableBinding`` subclass with different ``__init__``
|
||||
@@ -5404,7 +5404,7 @@ class RunnableBindingBase(RunnableSerializable[Input, Output]):
|
||||
``Runnable`` with a custom type. Defaults to None.
|
||||
**other_kwargs: Unpacked into the base class.
|
||||
""" # noqa: E501
|
||||
super().__init__( # type: ignore[call-arg]
|
||||
super().__init__(
|
||||
bound=bound,
|
||||
kwargs=kwargs or {},
|
||||
config=config or {},
|
||||
@@ -5729,7 +5729,7 @@ class RunnableBindingBase(RunnableSerializable[Input, Output]):
|
||||
yield item
|
||||
|
||||
|
||||
class RunnableBinding(RunnableBindingBase[Input, Output]):
|
||||
class RunnableBinding(RunnableBindingBase[Input, Output]): # type: ignore[no-redef]
|
||||
"""Wrap a ``Runnable`` with additional functionality.
|
||||
|
||||
A ``RunnableBinding`` can be thought of as a "runnable decorator" that
|
||||
|
||||
@@ -136,7 +136,7 @@ class RunnableBranch(RunnableSerializable[Input, Output]):
|
||||
super().__init__(
|
||||
branches=branches_,
|
||||
default=default_,
|
||||
) # type: ignore[call-arg]
|
||||
)
|
||||
|
||||
model_config = ConfigDict(
|
||||
arbitrary_types_allowed=True,
|
||||
|
||||
@@ -38,7 +38,7 @@ MessagesOrDictWithMessages = Union[Sequence["BaseMessage"], dict[str, Any]]
|
||||
GetSessionHistoryCallable = Callable[..., BaseChatMessageHistory]
|
||||
|
||||
|
||||
class RunnableWithMessageHistory(RunnableBindingBase):
|
||||
class RunnableWithMessageHistory(RunnableBindingBase): # type: ignore[no-redef]
|
||||
"""Runnable that manages chat message history for another Runnable.
|
||||
|
||||
A chat message history is a sequence of messages that represent a conversation.
|
||||
|
||||
@@ -186,7 +186,7 @@ class RunnablePassthrough(RunnableSerializable[Other, Other]):
|
||||
afunc = func
|
||||
func = None
|
||||
|
||||
super().__init__(func=func, afunc=afunc, input_type=input_type, **kwargs) # type: ignore[call-arg]
|
||||
super().__init__(func=func, afunc=afunc, input_type=input_type, **kwargs)
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
@@ -406,7 +406,7 @@ class RunnableAssign(RunnableSerializable[dict[str, Any], dict[str, Any]]):
|
||||
mapper: A ``RunnableParallel`` instance that will be used to transform the
|
||||
input dictionary.
|
||||
"""
|
||||
super().__init__(mapper=mapper, **kwargs) # type: ignore[call-arg]
|
||||
super().__init__(mapper=mapper, **kwargs)
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
@@ -710,7 +710,7 @@ class RunnablePick(RunnableSerializable[dict[str, Any], dict[str, Any]]):
|
||||
Args:
|
||||
keys: A single key or a list of keys to pick from the input dictionary.
|
||||
"""
|
||||
super().__init__(keys=keys, **kwargs) # type: ignore[call-arg]
|
||||
super().__init__(keys=keys, **kwargs)
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user