mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-08 22:42:05 +00:00
chore: formatting across codebase (#32456)
To prevent polluting future PRs
This commit is contained in:
@@ -15,12 +15,12 @@ You may use the button above, or follow these steps to open this repo in a Codes
|
||||
1. Click **Create codespace on master**.
|
||||
|
||||
For more info, check out the [GitHub documentation](https://docs.github.com/en/free-pro-team@latest/github/developing-online-with-codespaces/creating-a-codespace#creating-a-codespace).
|
||||
|
||||
|
||||
## VS Code Dev Containers
|
||||
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/langchain-ai/langchain)
|
||||
|
||||
> [!NOTE]
|
||||
> [!NOTE]
|
||||
> If you click the link above you will open the main repo (`langchain-ai/langchain`) and *not* your local cloned repo. This is fine if you only want to run and test the library, but if you want to contribute you can use the link below and replace with your username and cloned repo name:
|
||||
|
||||
```txt
|
||||
|
@@ -4,7 +4,7 @@ services:
|
||||
build:
|
||||
dockerfile: libs/langchain/dev.Dockerfile
|
||||
context: ..
|
||||
|
||||
|
||||
networks:
|
||||
- langchain-network
|
||||
|
||||
|
2
.github/CODE_OF_CONDUCT.md
vendored
2
.github/CODE_OF_CONDUCT.md
vendored
@@ -129,4 +129,4 @@ For answers to common questions about this code of conduct, see the FAQ at
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
8
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
8
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -5,7 +5,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to file a bug report.
|
||||
Thank you for taking the time to file a bug report.
|
||||
|
||||
Use this to report BUGS in LangChain. For usage questions, feature requests and general design questions, please use the [LangChain Forum](https://forum.langchain.com/).
|
||||
|
||||
@@ -50,7 +50,7 @@ body:
|
||||
|
||||
If a maintainer can copy it, run it, and see it right away, there's a much higher chance that you'll be able to get help.
|
||||
|
||||
**Important!**
|
||||
**Important!**
|
||||
|
||||
* Avoid screenshots when possible, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.
|
||||
* Reduce your code to the minimum required to reproduce the issue if possible. This makes it much easier for others to help you.
|
||||
@@ -58,14 +58,14 @@ body:
|
||||
* INCLUDE the language label (e.g. `python`) after the first three backticks to enable syntax highlighting. (e.g., ```python rather than ```).
|
||||
|
||||
placeholder: |
|
||||
The following code:
|
||||
The following code:
|
||||
|
||||
```python
|
||||
from langchain_core.runnables import RunnableLambda
|
||||
|
||||
def bad_code(inputs) -> int:
|
||||
raise NotImplementedError('For demo purpose')
|
||||
|
||||
|
||||
chain = RunnableLambda(bad_code)
|
||||
chain.invoke('Hello!')
|
||||
```
|
||||
|
2
.github/ISSUE_TEMPLATE/documentation.yml
vendored
2
.github/ISSUE_TEMPLATE/documentation.yml
vendored
@@ -14,7 +14,7 @@ body:
|
||||
|
||||
Do **NOT** use this to ask usage questions or reporting issues with your code.
|
||||
|
||||
If you have usage questions or need help solving some problem,
|
||||
If you have usage questions or need help solving some problem,
|
||||
please use the [LangChain Forum](https://forum.langchain.com/).
|
||||
|
||||
If you're in the wrong place, here are some helpful links to find a better
|
||||
|
2
.github/ISSUE_TEMPLATE/privileged.yml
vendored
2
.github/ISSUE_TEMPLATE/privileged.yml
vendored
@@ -8,7 +8,7 @@ body:
|
||||
|
||||
If you are not a LangChain maintainer or were not asked directly by a maintainer to create an issue, then please start the conversation on the [LangChain Forum](https://forum.langchain.com/) instead.
|
||||
|
||||
You are a LangChain maintainer if you maintain any of the packages inside of the LangChain repository
|
||||
You are a LangChain maintainer if you maintain any of the packages inside of the LangChain repository
|
||||
or are a regular contributor to LangChain with previous merged pull requests.
|
||||
- type: checkboxes
|
||||
id: privileged
|
||||
|
2
.github/actions/people/Dockerfile
vendored
2
.github/actions/people/Dockerfile
vendored
@@ -4,4 +4,4 @@ RUN pip install httpx PyGithub "pydantic==2.0.2" pydantic-settings "pyyaml>=5.3.
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["python", "/app/main.py"]
|
||||
CMD ["python", "/app/main.py"]
|
||||
|
6
.github/actions/people/action.yml
vendored
6
.github/actions/people/action.yml
vendored
@@ -4,8 +4,8 @@ description: "Generate the data for the LangChain People page"
|
||||
author: "Jacob Lee <jacob@langchain.dev>"
|
||||
inputs:
|
||||
token:
|
||||
description: 'User token, to read the GitHub API. Can be passed in using {{ secrets.LANGCHAIN_PEOPLE_GITHUB_TOKEN }}'
|
||||
description: "User token, to read the GitHub API. Can be passed in using {{ secrets.LANGCHAIN_PEOPLE_GITHUB_TOKEN }}"
|
||||
required: true
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
using: "docker"
|
||||
image: "Dockerfile"
|
||||
|
25
.github/scripts/check_diff.py
vendored
25
.github/scripts/check_diff.py
vendored
@@ -3,14 +3,12 @@ import json
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Dict, List, Set
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set
|
||||
|
||||
import tomllib
|
||||
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
from get_min_versions import get_min_version_from_toml
|
||||
|
||||
from packaging.requirements import Requirement
|
||||
|
||||
LANGCHAIN_DIRS = [
|
||||
"libs/core",
|
||||
@@ -38,7 +36,7 @@ IGNORED_PARTNERS = [
|
||||
]
|
||||
|
||||
PY_312_MAX_PACKAGES = [
|
||||
"libs/partners/chroma", # https://github.com/chroma-core/chroma/issues/4382
|
||||
"libs/partners/chroma", # https://github.com/chroma-core/chroma/issues/4382
|
||||
]
|
||||
|
||||
|
||||
@@ -85,9 +83,9 @@ def dependents_graph() -> dict:
|
||||
for depline in extended_deps:
|
||||
if depline.startswith("-e "):
|
||||
# editable dependency
|
||||
assert depline.startswith(
|
||||
"-e ../partners/"
|
||||
), "Extended test deps should only editable install partner packages"
|
||||
assert depline.startswith("-e ../partners/"), (
|
||||
"Extended test deps should only editable install partner packages"
|
||||
)
|
||||
partner = depline.split("partners/")[1]
|
||||
dep = f"langchain-{partner}"
|
||||
else:
|
||||
@@ -271,7 +269,7 @@ if __name__ == "__main__":
|
||||
dirs_to_run["extended-test"].add(dir_)
|
||||
elif file.startswith("libs/standard-tests"):
|
||||
# TODO: update to include all packages that rely on standard-tests (all partner packages)
|
||||
# note: won't run on external repo partners
|
||||
# Note: won't run on external repo partners
|
||||
dirs_to_run["lint"].add("libs/standard-tests")
|
||||
dirs_to_run["test"].add("libs/standard-tests")
|
||||
dirs_to_run["lint"].add("libs/cli")
|
||||
@@ -285,7 +283,7 @@ if __name__ == "__main__":
|
||||
elif file.startswith("libs/cli"):
|
||||
dirs_to_run["lint"].add("libs/cli")
|
||||
dirs_to_run["test"].add("libs/cli")
|
||||
|
||||
|
||||
elif file.startswith("libs/partners"):
|
||||
partner_dir = file.split("/")[2]
|
||||
if os.path.isdir(f"libs/partners/{partner_dir}") and [
|
||||
@@ -303,7 +301,10 @@ if __name__ == "__main__":
|
||||
f"Unknown lib: {file}. check_diff.py likely needs "
|
||||
"an update for this new library!"
|
||||
)
|
||||
elif file.startswith("docs/") or file in ["pyproject.toml", "uv.lock"]: # docs or root uv files
|
||||
elif file.startswith("docs/") or file in [
|
||||
"pyproject.toml",
|
||||
"uv.lock",
|
||||
]: # docs or root uv files
|
||||
docs_edited = True
|
||||
dirs_to_run["lint"].add(".")
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
|
||||
import tomllib
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
26
.github/scripts/get_min_versions.py
vendored
26
.github/scripts/get_min_versions.py
vendored
@@ -1,5 +1,5 @@
|
||||
from collections import defaultdict
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
@@ -8,17 +8,13 @@ else:
|
||||
# for python 3.10 and below, which doesnt have stdlib tomllib
|
||||
import tomli as tomllib
|
||||
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.specifiers import SpecifierSet
|
||||
from packaging.version import Version
|
||||
|
||||
|
||||
import requests
|
||||
from packaging.version import parse
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
import re
|
||||
|
||||
import requests
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.specifiers import SpecifierSet
|
||||
from packaging.version import Version, parse
|
||||
|
||||
MIN_VERSION_LIBS = [
|
||||
"langchain-core",
|
||||
@@ -72,11 +68,13 @@ def get_minimum_version(package_name: str, spec_string: str) -> Optional[str]:
|
||||
spec_string = re.sub(r"\^0\.0\.(\d+)", r"0.0.\1", spec_string)
|
||||
# rewrite occurrences of ^0.y.z to >=0.y.z,<0.y+1 (can be anywhere in constraint string)
|
||||
for y in range(1, 10):
|
||||
spec_string = re.sub(rf"\^0\.{y}\.(\d+)", rf">=0.{y}.\1,<0.{y+1}", spec_string)
|
||||
spec_string = re.sub(
|
||||
rf"\^0\.{y}\.(\d+)", rf">=0.{y}.\1,<0.{y + 1}", spec_string
|
||||
)
|
||||
# rewrite occurrences of ^x.y.z to >=x.y.z,<x+1.0.0 (can be anywhere in constraint string)
|
||||
for x in range(1, 10):
|
||||
spec_string = re.sub(
|
||||
rf"\^{x}\.(\d+)\.(\d+)", rf">={x}.\1.\2,<{x+1}", spec_string
|
||||
rf"\^{x}\.(\d+)\.(\d+)", rf">={x}.\1.\2,<{x + 1}", spec_string
|
||||
)
|
||||
|
||||
spec_set = SpecifierSet(spec_string)
|
||||
@@ -169,12 +167,12 @@ def check_python_version(version_string, constraint_string):
|
||||
# rewrite occurrences of ^0.y.z to >=0.y.z,<0.y+1.0 (can be anywhere in constraint string)
|
||||
for y in range(1, 10):
|
||||
constraint_string = re.sub(
|
||||
rf"\^0\.{y}\.(\d+)", rf">=0.{y}.\1,<0.{y+1}.0", constraint_string
|
||||
rf"\^0\.{y}\.(\d+)", rf">=0.{y}.\1,<0.{y + 1}.0", constraint_string
|
||||
)
|
||||
# rewrite occurrences of ^x.y.z to >=x.y.z,<x+1.0.0 (can be anywhere in constraint string)
|
||||
for x in range(1, 10):
|
||||
constraint_string = re.sub(
|
||||
rf"\^{x}\.0\.(\d+)", rf">={x}.0.\1,<{x+1}.0.0", constraint_string
|
||||
rf"\^{x}\.0\.(\d+)", rf">={x}.0.\1,<{x + 1}.0.0", constraint_string
|
||||
)
|
||||
|
||||
try:
|
||||
|
47
.github/scripts/prep_api_docs_build.py
vendored
47
.github/scripts/prep_api_docs_build.py
vendored
@@ -3,9 +3,10 @@
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
from typing import Any, Dict
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def load_packages_yaml() -> Dict[str, Any]:
|
||||
@@ -28,7 +29,6 @@ def get_target_dir(package_name: str) -> Path:
|
||||
def clean_target_directories(packages: list) -> None:
|
||||
"""Remove old directories that will be replaced."""
|
||||
for package in packages:
|
||||
|
||||
target_dir = get_target_dir(package["name"])
|
||||
if target_dir.exists():
|
||||
print(f"Removing {target_dir}")
|
||||
@@ -38,7 +38,6 @@ def clean_target_directories(packages: list) -> None:
|
||||
def move_libraries(packages: list) -> None:
|
||||
"""Move libraries from their source locations to the target directories."""
|
||||
for package in packages:
|
||||
|
||||
repo_name = package["repo"].split("/")[1]
|
||||
source_path = package["path"]
|
||||
target_dir = get_target_dir(package["name"])
|
||||
@@ -68,23 +67,33 @@ def main():
|
||||
package_yaml = load_packages_yaml()
|
||||
|
||||
# Clean target directories
|
||||
clean_target_directories([
|
||||
p
|
||||
for p in package_yaml["packages"]
|
||||
if (p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref"))
|
||||
and p["repo"] != "langchain-ai/langchain"
|
||||
and p["name"] != "langchain-ai21" # Skip AI21 due to dependency conflicts
|
||||
])
|
||||
clean_target_directories(
|
||||
[
|
||||
p
|
||||
for p in package_yaml["packages"]
|
||||
if (
|
||||
p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref")
|
||||
)
|
||||
and p["repo"] != "langchain-ai/langchain"
|
||||
and p["name"]
|
||||
!= "langchain-ai21" # Skip AI21 due to dependency conflicts
|
||||
]
|
||||
)
|
||||
|
||||
# Move libraries to their new locations
|
||||
move_libraries([
|
||||
p
|
||||
for p in package_yaml["packages"]
|
||||
if not p.get("disabled", False)
|
||||
and (p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref"))
|
||||
and p["repo"] != "langchain-ai/langchain"
|
||||
and p["name"] != "langchain-ai21" # Skip AI21 due to dependency conflicts
|
||||
])
|
||||
move_libraries(
|
||||
[
|
||||
p
|
||||
for p in package_yaml["packages"]
|
||||
if not p.get("disabled", False)
|
||||
and (
|
||||
p["repo"].startswith("langchain-ai/") or p.get("include_in_api_ref")
|
||||
)
|
||||
and p["repo"] != "langchain-ai/langchain"
|
||||
and p["name"]
|
||||
!= "langchain-ai21" # Skip AI21 due to dependency conflicts
|
||||
]
|
||||
)
|
||||
|
||||
# Delete ones without a pyproject.toml
|
||||
for partner in Path("langchain/libs/partners").iterdir():
|
||||
|
416
.github/tools/git-restore-mtime
vendored
416
.github/tools/git-restore-mtime
vendored
@@ -81,56 +81,93 @@ import time
|
||||
__version__ = "2022.12+dev"
|
||||
|
||||
# Update symlinks only if the platform supports not following them
|
||||
UPDATE_SYMLINKS = bool(os.utime in getattr(os, 'supports_follow_symlinks', []))
|
||||
UPDATE_SYMLINKS = bool(os.utime in getattr(os, "supports_follow_symlinks", []))
|
||||
|
||||
# Call os.path.normpath() only if not in a POSIX platform (Windows)
|
||||
NORMALIZE_PATHS = (os.path.sep != '/')
|
||||
NORMALIZE_PATHS = os.path.sep != "/"
|
||||
|
||||
# How many files to process in each batch when re-trying merge commits
|
||||
STEPMISSING = 100
|
||||
|
||||
# (Extra) keywords for the os.utime() call performed by touch()
|
||||
UTIME_KWS = {} if not UPDATE_SYMLINKS else {'follow_symlinks': False}
|
||||
UTIME_KWS = {} if not UPDATE_SYMLINKS else {"follow_symlinks": False}
|
||||
|
||||
|
||||
# Command-line interface ######################################################
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__.split('\n---')[0])
|
||||
parser = argparse.ArgumentParser(description=__doc__.split("\n---")[0])
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('--quiet', '-q', dest='loglevel',
|
||||
action="store_const", const=logging.WARNING, default=logging.INFO,
|
||||
help="Suppress informative messages and summary statistics.")
|
||||
group.add_argument('--verbose', '-v', action="count", help="""
|
||||
group.add_argument(
|
||||
"--quiet",
|
||||
"-q",
|
||||
dest="loglevel",
|
||||
action="store_const",
|
||||
const=logging.WARNING,
|
||||
default=logging.INFO,
|
||||
help="Suppress informative messages and summary statistics.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--verbose",
|
||||
"-v",
|
||||
action="count",
|
||||
help="""
|
||||
Print additional information for each processed file.
|
||||
Specify twice to further increase verbosity.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--cwd', '-C', metavar="DIRECTORY", help="""
|
||||
parser.add_argument(
|
||||
"--cwd",
|
||||
"-C",
|
||||
metavar="DIRECTORY",
|
||||
help="""
|
||||
Run as if %(prog)s was started in directory %(metavar)s.
|
||||
This affects how --work-tree, --git-dir and PATHSPEC arguments are handled.
|
||||
See 'man 1 git' or 'git --help' for more information.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--git-dir', dest='gitdir', metavar="GITDIR", help="""
|
||||
parser.add_argument(
|
||||
"--git-dir",
|
||||
dest="gitdir",
|
||||
metavar="GITDIR",
|
||||
help="""
|
||||
Path to the git repository, by default auto-discovered by searching
|
||||
the current directory and its parents for a .git/ subdirectory.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--work-tree', dest='workdir', metavar="WORKTREE", help="""
|
||||
parser.add_argument(
|
||||
"--work-tree",
|
||||
dest="workdir",
|
||||
metavar="WORKTREE",
|
||||
help="""
|
||||
Path to the work tree root, by default the parent of GITDIR if it's
|
||||
automatically discovered, or the current directory if GITDIR is set.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--force', '-f', default=False, action="store_true", help="""
|
||||
parser.add_argument(
|
||||
"--force",
|
||||
"-f",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Force updating files with uncommitted modifications.
|
||||
Untracked files and uncommitted deletions, renames and additions are
|
||||
always ignored.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--merge', '-m', default=False, action="store_true", help="""
|
||||
parser.add_argument(
|
||||
"--merge",
|
||||
"-m",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Include merge commits.
|
||||
Leads to more recent times and more files per commit, thus with the same
|
||||
time, which may or may not be what you want.
|
||||
@@ -138,71 +175,130 @@ def parse_args():
|
||||
are found sooner, which can improve performance, sometimes substantially.
|
||||
But as merge commits are usually huge, processing them may also take longer.
|
||||
By default, merge commits are only used for files missing from regular commits.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--first-parent', default=False, action="store_true", help="""
|
||||
parser.add_argument(
|
||||
"--first-parent",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Consider only the first parent, the "main branch", when evaluating merge commits.
|
||||
Only effective when merge commits are processed, either when --merge is
|
||||
used or when finding missing files after the first regular log search.
|
||||
See --skip-missing.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--skip-missing', '-s', dest="missing", default=True,
|
||||
action="store_false", help="""
|
||||
parser.add_argument(
|
||||
"--skip-missing",
|
||||
"-s",
|
||||
dest="missing",
|
||||
default=True,
|
||||
action="store_false",
|
||||
help="""
|
||||
Do not try to find missing files.
|
||||
If merge commits were not evaluated with --merge and some files were
|
||||
not found in regular commits, by default %(prog)s searches for these
|
||||
files again in the merge commits.
|
||||
This option disables this retry, so files found only in merge commits
|
||||
will not have their timestamp updated.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--no-directories', '-D', dest='dirs', default=True,
|
||||
action="store_false", help="""
|
||||
parser.add_argument(
|
||||
"--no-directories",
|
||||
"-D",
|
||||
dest="dirs",
|
||||
default=True,
|
||||
action="store_false",
|
||||
help="""
|
||||
Do not update directory timestamps.
|
||||
By default, use the time of its most recently created, renamed or deleted file.
|
||||
Note that just modifying a file will NOT update its directory time.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--test', '-t', default=False, action="store_true",
|
||||
help="Test run: do not actually update any file timestamp.")
|
||||
parser.add_argument(
|
||||
"--test",
|
||||
"-t",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Test run: do not actually update any file timestamp.",
|
||||
)
|
||||
|
||||
parser.add_argument('--commit-time', '-c', dest='commit_time', default=False,
|
||||
action='store_true', help="Use commit time instead of author time.")
|
||||
parser.add_argument(
|
||||
"--commit-time",
|
||||
"-c",
|
||||
dest="commit_time",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Use commit time instead of author time.",
|
||||
)
|
||||
|
||||
parser.add_argument('--oldest-time', '-o', dest='reverse_order', default=False,
|
||||
action='store_true', help="""
|
||||
parser.add_argument(
|
||||
"--oldest-time",
|
||||
"-o",
|
||||
dest="reverse_order",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Update times based on the oldest, instead of the most recent commit of a file.
|
||||
This reverses the order in which the git log is processed to emulate a
|
||||
file "creation" date. Note this will be inaccurate for files deleted and
|
||||
re-created at later dates.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--skip-older-than', metavar='SECONDS', type=int, help="""
|
||||
parser.add_argument(
|
||||
"--skip-older-than",
|
||||
metavar="SECONDS",
|
||||
type=int,
|
||||
help="""
|
||||
Ignore files that are currently older than %(metavar)s.
|
||||
Useful in workflows that assume such files already have a correct timestamp,
|
||||
as it may improve performance by processing fewer files.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--skip-older-than-commit', '-N', default=False,
|
||||
action='store_true', help="""
|
||||
parser.add_argument(
|
||||
"--skip-older-than-commit",
|
||||
"-N",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Ignore files older than the timestamp it would be updated to.
|
||||
Such files may be considered "original", likely in the author's repository.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--unique-times', default=False, action="store_true", help="""
|
||||
parser.add_argument(
|
||||
"--unique-times",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""
|
||||
Set the microseconds to a unique value per commit.
|
||||
Allows telling apart changes that would otherwise have identical timestamps,
|
||||
as git's time accuracy is in seconds.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('pathspec', nargs='*', metavar='PATHSPEC', help="""
|
||||
parser.add_argument(
|
||||
"pathspec",
|
||||
nargs="*",
|
||||
metavar="PATHSPEC",
|
||||
help="""
|
||||
Only modify paths matching %(metavar)s, relative to current directory.
|
||||
By default, update all but untracked files and submodules.
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument('--version', '-V', action='version',
|
||||
version='%(prog)s version {version}'.format(version=get_version()))
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
"-V",
|
||||
action="version",
|
||||
version="%(prog)s version {version}".format(version=get_version()),
|
||||
)
|
||||
|
||||
args_ = parser.parse_args()
|
||||
if args_.verbose:
|
||||
@@ -212,17 +308,18 @@ def parse_args():
|
||||
|
||||
|
||||
def get_version(version=__version__):
|
||||
if not version.endswith('+dev'):
|
||||
if not version.endswith("+dev"):
|
||||
return version
|
||||
try:
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
return Git(cwd=cwd, errors=False).describe().lstrip('v')
|
||||
return Git(cwd=cwd, errors=False).describe().lstrip("v")
|
||||
except Git.Error:
|
||||
return '-'.join((version, "unknown"))
|
||||
return "-".join((version, "unknown"))
|
||||
|
||||
|
||||
# Helper functions ############################################################
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""Add TRACE logging level and corresponding method, return the root logger"""
|
||||
logging.TRACE = TRACE = logging.DEBUG // 2
|
||||
@@ -255,11 +352,13 @@ def normalize(path):
|
||||
if path and path[0] == '"':
|
||||
# Python 2: path = path[1:-1].decode("string-escape")
|
||||
# Python 3: https://stackoverflow.com/a/46650050/624066
|
||||
path = (path[1:-1] # Remove enclosing double quotes
|
||||
.encode('latin1') # Convert to bytes, required by 'unicode-escape'
|
||||
.decode('unicode-escape') # Perform the actual octal-escaping decode
|
||||
.encode('latin1') # 1:1 mapping to bytes, UTF-8 encoded
|
||||
.decode('utf8', 'surrogateescape')) # Decode from UTF-8
|
||||
path = (
|
||||
path[1:-1] # Remove enclosing double quotes
|
||||
.encode("latin1") # Convert to bytes, required by 'unicode-escape'
|
||||
.decode("unicode-escape") # Perform the actual octal-escaping decode
|
||||
.encode("latin1") # 1:1 mapping to bytes, UTF-8 encoded
|
||||
.decode("utf8", "surrogateescape")
|
||||
) # Decode from UTF-8
|
||||
if NORMALIZE_PATHS:
|
||||
# Make sure the slash matches the OS; for Windows we need a backslash
|
||||
path = os.path.normpath(path)
|
||||
@@ -282,12 +381,12 @@ def touch_ns(path, mtime_ns):
|
||||
|
||||
def isodate(secs: int):
|
||||
# time.localtime() accepts floats, but discards fractional part
|
||||
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(secs))
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(secs))
|
||||
|
||||
|
||||
def isodate_ns(ns: int):
|
||||
# for integers fromtimestamp() is equivalent and ~16% slower than isodate()
|
||||
return datetime.datetime.fromtimestamp(ns / 1000000000).isoformat(sep=' ')
|
||||
return datetime.datetime.fromtimestamp(ns / 1000000000).isoformat(sep=" ")
|
||||
|
||||
|
||||
def get_mtime_ns(secs: int, idx: int):
|
||||
@@ -305,35 +404,49 @@ def get_mtime_path(path):
|
||||
|
||||
# Git class and parse_log(), the heart of the script ##########################
|
||||
|
||||
|
||||
class Git:
|
||||
def __init__(self, workdir=None, gitdir=None, cwd=None, errors=True):
|
||||
self.gitcmd = ['git']
|
||||
self.gitcmd = ["git"]
|
||||
self.errors = errors
|
||||
self._proc = None
|
||||
if workdir: self.gitcmd.extend(('--work-tree', workdir))
|
||||
if gitdir: self.gitcmd.extend(('--git-dir', gitdir))
|
||||
if cwd: self.gitcmd.extend(('-C', cwd))
|
||||
if workdir:
|
||||
self.gitcmd.extend(("--work-tree", workdir))
|
||||
if gitdir:
|
||||
self.gitcmd.extend(("--git-dir", gitdir))
|
||||
if cwd:
|
||||
self.gitcmd.extend(("-C", cwd))
|
||||
self.workdir, self.gitdir = self._get_repo_dirs()
|
||||
|
||||
def ls_files(self, paths: list = None):
|
||||
return (normalize(_) for _ in self._run('ls-files --full-name', paths))
|
||||
return (normalize(_) for _ in self._run("ls-files --full-name", paths))
|
||||
|
||||
def ls_dirty(self, force=False):
|
||||
return (normalize(_[3:].split(' -> ', 1)[-1])
|
||||
for _ in self._run('status --porcelain')
|
||||
if _[:2] != '??' and (not force or (_[0] in ('R', 'A')
|
||||
or _[1] == 'D')))
|
||||
return (
|
||||
normalize(_[3:].split(" -> ", 1)[-1])
|
||||
for _ in self._run("status --porcelain")
|
||||
if _[:2] != "??" and (not force or (_[0] in ("R", "A") or _[1] == "D"))
|
||||
)
|
||||
|
||||
def log(self, merge=False, first_parent=False, commit_time=False,
|
||||
reverse_order=False, paths: list = None):
|
||||
cmd = 'whatchanged --pretty={}'.format('%ct' if commit_time else '%at')
|
||||
if merge: cmd += ' -m'
|
||||
if first_parent: cmd += ' --first-parent'
|
||||
if reverse_order: cmd += ' --reverse'
|
||||
def log(
|
||||
self,
|
||||
merge=False,
|
||||
first_parent=False,
|
||||
commit_time=False,
|
||||
reverse_order=False,
|
||||
paths: list = None,
|
||||
):
|
||||
cmd = "whatchanged --pretty={}".format("%ct" if commit_time else "%at")
|
||||
if merge:
|
||||
cmd += " -m"
|
||||
if first_parent:
|
||||
cmd += " --first-parent"
|
||||
if reverse_order:
|
||||
cmd += " --reverse"
|
||||
return self._run(cmd, paths)
|
||||
|
||||
def describe(self):
|
||||
return self._run('describe --tags', check=True)[0]
|
||||
return self._run("describe --tags", check=True)[0]
|
||||
|
||||
def terminate(self):
|
||||
if self._proc is None:
|
||||
@@ -345,18 +458,22 @@ class Git:
|
||||
pass
|
||||
|
||||
def _get_repo_dirs(self):
|
||||
return (os.path.normpath(_) for _ in
|
||||
self._run('rev-parse --show-toplevel --absolute-git-dir', check=True))
|
||||
return (
|
||||
os.path.normpath(_)
|
||||
for _ in self._run(
|
||||
"rev-parse --show-toplevel --absolute-git-dir", check=True
|
||||
)
|
||||
)
|
||||
|
||||
def _run(self, cmdstr: str, paths: list = None, output=True, check=False):
|
||||
cmdlist = self.gitcmd + shlex.split(cmdstr)
|
||||
if paths:
|
||||
cmdlist.append('--')
|
||||
cmdlist.append("--")
|
||||
cmdlist.extend(paths)
|
||||
popen_args = dict(universal_newlines=True, encoding='utf8')
|
||||
popen_args = dict(universal_newlines=True, encoding="utf8")
|
||||
if not self.errors:
|
||||
popen_args['stderr'] = subprocess.DEVNULL
|
||||
log.trace("Executing: %s", ' '.join(cmdlist))
|
||||
popen_args["stderr"] = subprocess.DEVNULL
|
||||
log.trace("Executing: %s", " ".join(cmdlist))
|
||||
if not output:
|
||||
return subprocess.call(cmdlist, **popen_args)
|
||||
if check:
|
||||
@@ -379,30 +496,26 @@ def parse_log(filelist, dirlist, stats, git, merge=False, filterlist=None):
|
||||
mtime = 0
|
||||
datestr = isodate(0)
|
||||
for line in git.log(
|
||||
merge,
|
||||
args.first_parent,
|
||||
args.commit_time,
|
||||
args.reverse_order,
|
||||
filterlist
|
||||
merge, args.first_parent, args.commit_time, args.reverse_order, filterlist
|
||||
):
|
||||
stats['loglines'] += 1
|
||||
stats["loglines"] += 1
|
||||
|
||||
# Blank line between Date and list of files
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Date line
|
||||
if line[0] != ':': # Faster than `not line.startswith(':')`
|
||||
stats['commits'] += 1
|
||||
if line[0] != ":": # Faster than `not line.startswith(':')`
|
||||
stats["commits"] += 1
|
||||
mtime = int(line)
|
||||
if args.unique_times:
|
||||
mtime = get_mtime_ns(mtime, stats['commits'])
|
||||
mtime = get_mtime_ns(mtime, stats["commits"])
|
||||
if args.debug:
|
||||
datestr = isodate(mtime)
|
||||
continue
|
||||
|
||||
# File line: three tokens if it describes a renaming, otherwise two
|
||||
tokens = line.split('\t')
|
||||
tokens = line.split("\t")
|
||||
|
||||
# Possible statuses:
|
||||
# M: Modified (content changed)
|
||||
@@ -411,7 +524,7 @@ def parse_log(filelist, dirlist, stats, git, merge=False, filterlist=None):
|
||||
# T: Type changed: to/from regular file, symlinks, submodules
|
||||
# R099: Renamed (moved), with % of unchanged content. 100 = pure rename
|
||||
# Not possible in log: C=Copied, U=Unmerged, X=Unknown, B=pairing Broken
|
||||
status = tokens[0].split(' ')[-1]
|
||||
status = tokens[0].split(" ")[-1]
|
||||
file = tokens[-1]
|
||||
|
||||
# Handles non-ASCII chars and OS path separator
|
||||
@@ -419,56 +532,76 @@ def parse_log(filelist, dirlist, stats, git, merge=False, filterlist=None):
|
||||
|
||||
def do_file():
|
||||
if args.skip_older_than_commit and get_mtime_path(file) <= mtime:
|
||||
stats['skip'] += 1
|
||||
stats["skip"] += 1
|
||||
return
|
||||
if args.debug:
|
||||
log.debug("%d\t%d\t%d\t%s\t%s",
|
||||
stats['loglines'], stats['commits'], stats['files'],
|
||||
datestr, file)
|
||||
log.debug(
|
||||
"%d\t%d\t%d\t%s\t%s",
|
||||
stats["loglines"],
|
||||
stats["commits"],
|
||||
stats["files"],
|
||||
datestr,
|
||||
file,
|
||||
)
|
||||
try:
|
||||
touch(os.path.join(git.workdir, file), mtime)
|
||||
stats['touches'] += 1
|
||||
stats["touches"] += 1
|
||||
except Exception as e:
|
||||
log.error("ERROR: %s: %s", e, file)
|
||||
stats['errors'] += 1
|
||||
stats["errors"] += 1
|
||||
|
||||
def do_dir():
|
||||
if args.debug:
|
||||
log.debug("%d\t%d\t-\t%s\t%s",
|
||||
stats['loglines'], stats['commits'],
|
||||
datestr, "{}/".format(dirname or '.'))
|
||||
log.debug(
|
||||
"%d\t%d\t-\t%s\t%s",
|
||||
stats["loglines"],
|
||||
stats["commits"],
|
||||
datestr,
|
||||
"{}/".format(dirname or "."),
|
||||
)
|
||||
try:
|
||||
touch(os.path.join(git.workdir, dirname), mtime)
|
||||
stats['dirtouches'] += 1
|
||||
stats["dirtouches"] += 1
|
||||
except Exception as e:
|
||||
log.error("ERROR: %s: %s", e, dirname)
|
||||
stats['direrrors'] += 1
|
||||
stats["direrrors"] += 1
|
||||
|
||||
if file in filelist:
|
||||
stats['files'] -= 1
|
||||
stats["files"] -= 1
|
||||
filelist.remove(file)
|
||||
do_file()
|
||||
|
||||
if args.dirs and status in ('A', 'D'):
|
||||
if args.dirs and status in ("A", "D"):
|
||||
dirname = os.path.dirname(file)
|
||||
if dirname in dirlist:
|
||||
dirlist.remove(dirname)
|
||||
do_dir()
|
||||
|
||||
# All files done?
|
||||
if not stats['files']:
|
||||
if not stats["files"]:
|
||||
git.terminate()
|
||||
return
|
||||
|
||||
|
||||
# Main Logic ##################################################################
|
||||
|
||||
|
||||
def main():
|
||||
start = time.time() # yes, Wall time. CPU time is not realistic for users.
|
||||
stats = {_: 0 for _ in ('loglines', 'commits', 'touches', 'skip', 'errors',
|
||||
'dirtouches', 'direrrors')}
|
||||
stats = {
|
||||
_: 0
|
||||
for _ in (
|
||||
"loglines",
|
||||
"commits",
|
||||
"touches",
|
||||
"skip",
|
||||
"errors",
|
||||
"dirtouches",
|
||||
"direrrors",
|
||||
)
|
||||
}
|
||||
|
||||
logging.basicConfig(level=args.loglevel, format='%(message)s')
|
||||
logging.basicConfig(level=args.loglevel, format="%(message)s")
|
||||
log.trace("Arguments: %s", args)
|
||||
|
||||
# First things first: Where and Who are we?
|
||||
@@ -499,13 +632,16 @@ def main():
|
||||
|
||||
# Symlink (to file, to dir or broken - git handles the same way)
|
||||
if not UPDATE_SYMLINKS and os.path.islink(fullpath):
|
||||
log.warning("WARNING: Skipping symlink, no OS support for updates: %s",
|
||||
path)
|
||||
log.warning(
|
||||
"WARNING: Skipping symlink, no OS support for updates: %s", path
|
||||
)
|
||||
continue
|
||||
|
||||
# skip files which are older than given threshold
|
||||
if (args.skip_older_than
|
||||
and start - get_mtime_path(fullpath) > args.skip_older_than):
|
||||
if (
|
||||
args.skip_older_than
|
||||
and start - get_mtime_path(fullpath) > args.skip_older_than
|
||||
):
|
||||
continue
|
||||
|
||||
# Always add files relative to worktree root
|
||||
@@ -519,15 +655,17 @@ def main():
|
||||
else:
|
||||
dirty = set(git.ls_dirty())
|
||||
if dirty:
|
||||
log.warning("WARNING: Modified files in the working directory were ignored."
|
||||
"\nTo include such files, commit your changes or use --force.")
|
||||
log.warning(
|
||||
"WARNING: Modified files in the working directory were ignored."
|
||||
"\nTo include such files, commit your changes or use --force."
|
||||
)
|
||||
filelist -= dirty
|
||||
|
||||
# Build dir list to be processed
|
||||
dirlist = set(os.path.dirname(_) for _ in filelist) if args.dirs else set()
|
||||
|
||||
stats['totalfiles'] = stats['files'] = len(filelist)
|
||||
log.info("{0:,} files to be processed in work dir".format(stats['totalfiles']))
|
||||
stats["totalfiles"] = stats["files"] = len(filelist)
|
||||
log.info("{0:,} files to be processed in work dir".format(stats["totalfiles"]))
|
||||
|
||||
if not filelist:
|
||||
# Nothing to do. Exit silently and without errors, just like git does
|
||||
@@ -544,10 +682,18 @@ def main():
|
||||
if args.missing and not args.merge:
|
||||
filterlist = list(filelist)
|
||||
missing = len(filterlist)
|
||||
log.info("{0:,} files not found in log, trying merge commits".format(missing))
|
||||
log.info(
|
||||
"{0:,} files not found in log, trying merge commits".format(missing)
|
||||
)
|
||||
for i in range(0, missing, STEPMISSING):
|
||||
parse_log(filelist, dirlist, stats, git,
|
||||
merge=True, filterlist=filterlist[i:i + STEPMISSING])
|
||||
parse_log(
|
||||
filelist,
|
||||
dirlist,
|
||||
stats,
|
||||
git,
|
||||
merge=True,
|
||||
filterlist=filterlist[i : i + STEPMISSING],
|
||||
)
|
||||
|
||||
# Still missing some?
|
||||
for file in filelist:
|
||||
@@ -556,29 +702,33 @@ def main():
|
||||
# Final statistics
|
||||
# Suggestion: use git-log --before=mtime to brag about skipped log entries
|
||||
def log_info(msg, *a, width=13):
|
||||
ifmt = '{:%d,}' % (width,) # not using 'n' for consistency with ffmt
|
||||
ffmt = '{:%d,.2f}' % (width,)
|
||||
ifmt = "{:%d,}" % (width,) # not using 'n' for consistency with ffmt
|
||||
ffmt = "{:%d,.2f}" % (width,)
|
||||
# %-formatting lacks a thousand separator, must pre-render with .format()
|
||||
log.info(msg.replace('%d', ifmt).replace('%f', ffmt).format(*a))
|
||||
log.info(msg.replace("%d", ifmt).replace("%f", ffmt).format(*a))
|
||||
|
||||
log_info(
|
||||
"Statistics:\n"
|
||||
"%f seconds\n"
|
||||
"%d log lines processed\n"
|
||||
"%d commits evaluated",
|
||||
time.time() - start, stats['loglines'], stats['commits'])
|
||||
"Statistics:\n%f seconds\n%d log lines processed\n%d commits evaluated",
|
||||
time.time() - start,
|
||||
stats["loglines"],
|
||||
stats["commits"],
|
||||
)
|
||||
|
||||
if args.dirs:
|
||||
if stats['direrrors']: log_info("%d directory update errors", stats['direrrors'])
|
||||
log_info("%d directories updated", stats['dirtouches'])
|
||||
if stats["direrrors"]:
|
||||
log_info("%d directory update errors", stats["direrrors"])
|
||||
log_info("%d directories updated", stats["dirtouches"])
|
||||
|
||||
if stats['touches'] != stats['totalfiles']:
|
||||
log_info("%d files", stats['totalfiles'])
|
||||
if stats['skip']: log_info("%d files skipped", stats['skip'])
|
||||
if stats['files']: log_info("%d files missing", stats['files'])
|
||||
if stats['errors']: log_info("%d file update errors", stats['errors'])
|
||||
if stats["touches"] != stats["totalfiles"]:
|
||||
log_info("%d files", stats["totalfiles"])
|
||||
if stats["skip"]:
|
||||
log_info("%d files skipped", stats["skip"])
|
||||
if stats["files"]:
|
||||
log_info("%d files missing", stats["files"])
|
||||
if stats["errors"]:
|
||||
log_info("%d file update errors", stats["errors"])
|
||||
|
||||
log_info("%d files updated", stats['touches'])
|
||||
log_info("%d files updated", stats["touches"])
|
||||
|
||||
if args.test:
|
||||
log.info("TEST RUN - No files modified!")
|
||||
|
2
.github/workflows/_test.yml
vendored
2
.github/workflows/_test.yml
vendored
@@ -79,4 +79,4 @@ jobs:
|
||||
# grep will exit non-zero if the target message isn't found,
|
||||
# and `set -e` above will cause the step to fail.
|
||||
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
||||
|
||||
|
||||
|
2
.github/workflows/_test_pydantic.yml
vendored
2
.github/workflows/_test_pydantic.yml
vendored
@@ -64,4 +64,4 @@ jobs:
|
||||
|
||||
# grep will exit non-zero if the target message isn't found,
|
||||
# and `set -e` above will cause the step to fail.
|
||||
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
||||
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
||||
|
@@ -11,4 +11,4 @@
|
||||
"MD046": {
|
||||
"style": "fenced"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -63,4 +63,4 @@ Notebook | Description
|
||||
[rag-locally-on-intel-cpu.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/rag-locally-on-intel-cpu.ipynb) | Perform Retrieval-Augmented-Generation (RAG) on locally downloaded open-source models using langchain and open source tools and execute it on Intel Xeon CPU. We showed an example of how to apply RAG on Llama 2 model and enable it to answer the queries related to Intel Q1 2024 earnings release.
|
||||
[visual_RAG_vdms.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/visual_RAG_vdms.ipynb) | Performs Visual Retrieval-Augmented-Generation (RAG) using videos and scene descriptions generated by open source models.
|
||||
[contextual_rag.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/contextual_rag.ipynb) | Performs contextual retrieval-augmented generation (RAG) prepending chunk-specific explanatory context to each chunk before embedding.
|
||||
[rag-agents-locally-on-intel-cpu.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/local_rag_agents_intel_cpu.ipynb) | Build a RAG agent locally with open source models that routes questions through one of two paths to find answers. The agent generates answers based on documents retrieved from either the vector database or retrieved from web search. If the vector database lacks relevant information, the agent opts for web search. Open-source models for LLM and embeddings are used locally on an Intel Xeon CPU to execute this pipeline.
|
||||
[rag-agents-locally-on-intel-cpu.ipynb](https://github.com/langchain-ai/langchain/tree/master/cookbook/local_rag_agents_intel_cpu.ipynb) | Build a RAG agent locally with open source models that routes questions through one of two paths to find answers. The agent generates answers based on documents retrieved from either the vector database or retrieved from web search. If the vector database lacks relevant information, the agent opts for web search. Open-source models for LLM and embeddings are used locally on an Intel Xeon CPU to execute this pipeline.
|
||||
|
@@ -35,6 +35,7 @@ embeddings.embed_query("What is the meaning of life?")
|
||||
```
|
||||
|
||||
## LLMs
|
||||
|
||||
`__ModuleName__LLM` class exposes LLMs from __ModuleName__.
|
||||
|
||||
```python
|
||||
|
@@ -1,3 +1,3 @@
|
||||
version: 0.0.1
|
||||
patterns:
|
||||
- name: github.com/getgrit/stdlib#*
|
||||
- name: github.com/getgrit/stdlib#*
|
||||
|
@@ -27,16 +27,16 @@ langchain app add __package_name__
|
||||
```
|
||||
|
||||
And add the following code to your `server.py` file:
|
||||
|
||||
```python
|
||||
__app_route_code__
|
||||
```
|
||||
|
||||
(Optional) Let's now configure LangSmith.
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
(Optional) Let's now configure LangSmith.
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
If you don't have access, you can skip this section
|
||||
|
||||
|
||||
```shell
|
||||
export LANGSMITH_TRACING=true
|
||||
export LANGSMITH_API_KEY=<your-api-key>
|
||||
@@ -49,11 +49,11 @@ If you are inside this directory, then you can spin up a LangServe instance dire
|
||||
langchain serve
|
||||
```
|
||||
|
||||
This will start the FastAPI app with a server is running locally at
|
||||
This will start the FastAPI app with a server is running locally at
|
||||
[http://localhost:8000](http://localhost:8000)
|
||||
|
||||
We can see all templates at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
|
||||
We can access the playground at [http://127.0.0.1:8000/__package_name__/playground](http://127.0.0.1:8000/__package_name__/playground)
|
||||
We can access the playground at [http://127.0.0.1:8000/__package_name__/playground](http://127.0.0.1:8000/__package_name__/playground)
|
||||
|
||||
We can access the template from code with:
|
||||
|
||||
@@ -61,4 +61,4 @@ We can access the template from code with:
|
||||
from langserve.client import RemoteRunnable
|
||||
|
||||
runnable = RemoteRunnable("http://localhost:8000/__package_name__")
|
||||
```
|
||||
```
|
||||
|
@@ -11,7 +11,7 @@ pip install -U langchain-cli
|
||||
## Adding packages
|
||||
|
||||
```bash
|
||||
# adding packages from
|
||||
# adding packages from
|
||||
# https://github.com/langchain-ai/langchain/tree/master/templates
|
||||
langchain app add $PROJECT_NAME
|
||||
|
||||
@@ -31,10 +31,10 @@ langchain app remove my/custom/path/rag
|
||||
```
|
||||
|
||||
## Setup LangSmith (Optional)
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
If you don't have access, you can skip this section
|
||||
|
||||
LangSmith will help us trace, monitor and debug LangChain applications.
|
||||
You can sign up for LangSmith [here](https://smith.langchain.com/).
|
||||
If you don't have access, you can skip this section
|
||||
|
||||
```shell
|
||||
export LANGSMITH_TRACING=true
|
||||
|
@@ -49,7 +49,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
mode: The file open mode. Defaults to ``'a'`` (append).
|
||||
color: Default color for text output. Defaults to ``None``.
|
||||
|
||||
Note:
|
||||
.. note::
|
||||
When not used as a context manager, a deprecation warning will be issued
|
||||
on first use. The file will be opened immediately in ``__init__`` and closed
|
||||
in ``__del__`` or when ``close()`` is called explicitly.
|
||||
@@ -65,6 +65,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
filename: Path to the output file.
|
||||
mode: File open mode (e.g., ``'w'``, ``'a'``, ``'x'``). Defaults to ``'a'``.
|
||||
color: Default text color for output. Defaults to ``None``.
|
||||
|
||||
"""
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
@@ -82,9 +83,10 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
Returns:
|
||||
The FileCallbackHandler instance.
|
||||
|
||||
Note:
|
||||
.. note::
|
||||
The file is already opened in ``__init__``, so this just marks that
|
||||
the handler is being used as a context manager.
|
||||
|
||||
"""
|
||||
self._file_opened_in_context = True
|
||||
return self
|
||||
@@ -101,6 +103,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
exc_type: Exception type if an exception occurred.
|
||||
exc_val: Exception value if an exception occurred.
|
||||
exc_tb: Exception traceback if an exception occurred.
|
||||
|
||||
"""
|
||||
self.close()
|
||||
|
||||
@@ -113,6 +116,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
|
||||
This method is safe to call multiple times and will only close
|
||||
the file if it's currently open.
|
||||
|
||||
"""
|
||||
if hasattr(self, "file") and self.file and not self.file.closed:
|
||||
self.file.close()
|
||||
@@ -133,6 +137,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the file is closed or not available.
|
||||
|
||||
"""
|
||||
global _GLOBAL_DEPRECATION_WARNED # noqa: PLW0603
|
||||
if not self._file_opened_in_context and not _GLOBAL_DEPRECATION_WARNED:
|
||||
@@ -163,6 +168,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
serialized: The serialized chain information.
|
||||
inputs: The inputs to the chain.
|
||||
**kwargs: Additional keyword arguments that may contain ``'name'``.
|
||||
|
||||
"""
|
||||
name = (
|
||||
kwargs.get("name")
|
||||
@@ -178,6 +184,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
Args:
|
||||
outputs: The outputs of the chain.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self._write("\n> Finished chain.", end="\n")
|
||||
|
||||
@@ -192,6 +199,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
color: Color override for this specific output. If ``None``, uses
|
||||
``self.color``.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self._write(action.log, color=color or self.color)
|
||||
|
||||
@@ -213,6 +221,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
observation_prefix: Optional prefix to write before the output.
|
||||
llm_prefix: Optional prefix to write after the output.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if observation_prefix is not None:
|
||||
self._write(f"\n{observation_prefix}")
|
||||
@@ -232,6 +241,7 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
``self.color``.
|
||||
end: String appended after the text. Defaults to ``""``.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self._write(text, color=color or self.color, end=end)
|
||||
|
||||
@@ -246,5 +256,6 @@ class FileCallbackHandler(BaseCallbackHandler):
|
||||
color: Color override for this specific output. If ``None``, uses
|
||||
``self.color``.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self._write(finish.log, color=color or self.color, end="\n")
|
||||
|
@@ -100,7 +100,8 @@ def trace_as_chain_group(
|
||||
metadata (dict[str, Any], optional): The metadata to apply to all runs.
|
||||
Defaults to None.
|
||||
|
||||
Note: must have LANGCHAIN_TRACING_V2 env var set to true to see the trace in LangSmith.
|
||||
.. note:
|
||||
Must have ``LANGCHAIN_TRACING_V2`` env var set to true to see the trace in LangSmith.
|
||||
|
||||
Returns:
|
||||
CallbackManagerForChainGroup: The callback manager for the chain group.
|
||||
@@ -185,7 +186,8 @@ async def atrace_as_chain_group(
|
||||
Returns:
|
||||
AsyncCallbackManager: The async callback manager for the chain group.
|
||||
|
||||
Note: must have LANGCHAIN_TRACING_V2 env var set to true to see the trace in LangSmith.
|
||||
.. note:
|
||||
Must have ``LANGCHAIN_TRACING_V2`` env var set to true to see the trace in LangSmith.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
@@ -242,6 +244,7 @@ def shielded(func: Func) -> Func:
|
||||
|
||||
Returns:
|
||||
Callable: The shielded function
|
||||
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
@@ -300,15 +303,17 @@ def handle_event(
|
||||
) -> None:
|
||||
"""Generic event handler for CallbackManager.
|
||||
|
||||
Note: This function is used by LangServe to handle events.
|
||||
.. note::
|
||||
This function is used by ``LangServe`` to handle events.
|
||||
|
||||
Args:
|
||||
handlers: The list of handlers that will handle the event.
|
||||
event_name: The name of the event (e.g., "on_llm_start").
|
||||
event_name: The name of the event (e.g., ``'on_llm_start'``).
|
||||
ignore_condition_name: Name of the attribute defined on handler
|
||||
that if True will cause the handler to be skipped for the given event.
|
||||
*args: The arguments to pass to the event handler.
|
||||
**kwargs: The keyword arguments to pass to the event handler
|
||||
|
||||
"""
|
||||
coros: list[Coroutine[Any, Any, Any]] = []
|
||||
|
||||
@@ -467,17 +472,19 @@ async def ahandle_event(
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Async generic event handler for AsyncCallbackManager.
|
||||
"""Async generic event handler for ``AsyncCallbackManager``.
|
||||
|
||||
Note: This function is used by LangServe to handle events.
|
||||
.. note::
|
||||
This function is used by ``LangServe`` to handle events.
|
||||
|
||||
Args:
|
||||
handlers: The list of handlers that will handle the event.
|
||||
event_name: The name of the event (e.g., "on_llm_start").
|
||||
event_name: The name of the event (e.g., ``'on_llm_start'``).
|
||||
ignore_condition_name: Name of the attribute defined on handler
|
||||
that if True will cause the handler to be skipped for the given event.
|
||||
*args: The arguments to pass to the event handler.
|
||||
**kwargs: The keyword arguments to pass to the event handler.
|
||||
|
||||
"""
|
||||
for handler in [h for h in handlers if h.run_inline]:
|
||||
await _ahandle_event_for_handler(
|
||||
@@ -529,6 +536,7 @@ class BaseRunManager(RunManagerMixin):
|
||||
Defaults to None.
|
||||
inheritable_metadata (Optional[dict[str, Any]]): The inheritable metadata.
|
||||
Defaults to None.
|
||||
|
||||
"""
|
||||
self.run_id = run_id
|
||||
self.handlers = handlers
|
||||
@@ -545,6 +553,7 @@ class BaseRunManager(RunManagerMixin):
|
||||
|
||||
Returns:
|
||||
BaseRunManager: The noop manager.
|
||||
|
||||
"""
|
||||
return cls(
|
||||
run_id=uuid.uuid4(),
|
||||
@@ -597,6 +606,7 @@ class RunManager(BaseRunManager):
|
||||
Args:
|
||||
retry_state (RetryCallState): The retry state.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -624,6 +634,7 @@ class ParentRunManager(RunManager):
|
||||
|
||||
Returns:
|
||||
CallbackManager: The child callback manager.
|
||||
|
||||
"""
|
||||
manager = CallbackManager(handlers=[], parent_run_id=self.run_id)
|
||||
manager.set_handlers(self.inheritable_handlers)
|
||||
@@ -643,6 +654,7 @@ class AsyncRunManager(BaseRunManager, ABC):
|
||||
|
||||
Returns:
|
||||
RunManager: The sync RunManager.
|
||||
|
||||
"""
|
||||
|
||||
async def on_text(
|
||||
@@ -658,6 +670,7 @@ class AsyncRunManager(BaseRunManager, ABC):
|
||||
|
||||
Returns:
|
||||
Any: The result of the callback.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -682,6 +695,7 @@ class AsyncRunManager(BaseRunManager, ABC):
|
||||
Args:
|
||||
retry_state (RetryCallState): The retry state.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -709,6 +723,7 @@ class AsyncParentRunManager(AsyncRunManager):
|
||||
|
||||
Returns:
|
||||
AsyncCallbackManager: The child callback manager.
|
||||
|
||||
"""
|
||||
manager = AsyncCallbackManager(handlers=[], parent_run_id=self.run_id)
|
||||
manager.set_handlers(self.inheritable_handlers)
|
||||
@@ -738,6 +753,7 @@ class CallbackManagerForLLMRun(RunManager, LLMManagerMixin):
|
||||
chunk (Optional[Union[GenerationChunk, ChatGenerationChunk]], optional):
|
||||
The chunk. Defaults to None.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -759,6 +775,7 @@ class CallbackManagerForLLMRun(RunManager, LLMManagerMixin):
|
||||
Args:
|
||||
response (LLMResult | AIMessage): The LLM result.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -785,6 +802,7 @@ class CallbackManagerForLLMRun(RunManager, LLMManagerMixin):
|
||||
kwargs (Any): Additional keyword arguments.
|
||||
- response (LLMResult | AIMessage): The response which was generated
|
||||
before the error occurred.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -808,6 +826,7 @@ class AsyncCallbackManagerForLLMRun(AsyncRunManager, LLMManagerMixin):
|
||||
|
||||
Returns:
|
||||
CallbackManagerForLLMRun: The sync RunManager.
|
||||
|
||||
"""
|
||||
return CallbackManagerForLLMRun(
|
||||
run_id=self.run_id,
|
||||
@@ -836,6 +855,7 @@ class AsyncCallbackManagerForLLMRun(AsyncRunManager, LLMManagerMixin):
|
||||
chunk (Optional[Union[GenerationChunk, ChatGenerationChunk]], optional):
|
||||
The chunk. Defaults to None.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -860,6 +880,7 @@ class AsyncCallbackManagerForLLMRun(AsyncRunManager, LLMManagerMixin):
|
||||
Args:
|
||||
response (LLMResult | AIMessage): The LLM result.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -887,6 +908,7 @@ class AsyncCallbackManagerForLLMRun(AsyncRunManager, LLMManagerMixin):
|
||||
kwargs (Any): Additional keyword arguments.
|
||||
- response (LLMResult | AIMessage): The response which was generated
|
||||
before the error occurred.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -911,6 +933,7 @@ class CallbackManagerForChainRun(ParentRunManager, ChainManagerMixin):
|
||||
Args:
|
||||
outputs (Union[dict[str, Any], Any]): The outputs of the chain.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -935,6 +958,7 @@ class CallbackManagerForChainRun(ParentRunManager, ChainManagerMixin):
|
||||
Args:
|
||||
error (Exception or KeyboardInterrupt): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -958,6 +982,7 @@ class CallbackManagerForChainRun(ParentRunManager, ChainManagerMixin):
|
||||
|
||||
Returns:
|
||||
Any: The result of the callback.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -981,6 +1006,7 @@ class CallbackManagerForChainRun(ParentRunManager, ChainManagerMixin):
|
||||
|
||||
Returns:
|
||||
Any: The result of the callback.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1025,6 +1051,7 @@ class AsyncCallbackManagerForChainRun(AsyncParentRunManager, ChainManagerMixin):
|
||||
Args:
|
||||
outputs (Union[dict[str, Any], Any]): The outputs of the chain.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1050,6 +1077,7 @@ class AsyncCallbackManagerForChainRun(AsyncParentRunManager, ChainManagerMixin):
|
||||
Args:
|
||||
error (Exception or KeyboardInterrupt): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1073,6 +1101,7 @@ class AsyncCallbackManagerForChainRun(AsyncParentRunManager, ChainManagerMixin):
|
||||
|
||||
Returns:
|
||||
Any: The result of the callback.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1096,6 +1125,7 @@ class AsyncCallbackManagerForChainRun(AsyncParentRunManager, ChainManagerMixin):
|
||||
|
||||
Returns:
|
||||
Any: The result of the callback.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1124,6 +1154,7 @@ class CallbackManagerForToolRun(ParentRunManager, ToolManagerMixin):
|
||||
Args:
|
||||
output (Any): The output of the tool.
|
||||
**kwargs (Any): The keyword arguments to pass to the event handler
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1148,6 +1179,7 @@ class CallbackManagerForToolRun(ParentRunManager, ToolManagerMixin):
|
||||
Args:
|
||||
error (Exception or KeyboardInterrupt): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1189,6 +1221,7 @@ class AsyncCallbackManagerForToolRun(AsyncParentRunManager, ToolManagerMixin):
|
||||
Args:
|
||||
output (Any): The output of the tool.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1213,6 +1246,7 @@ class AsyncCallbackManagerForToolRun(AsyncParentRunManager, ToolManagerMixin):
|
||||
Args:
|
||||
error (Exception or KeyboardInterrupt): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1241,6 +1275,7 @@ class CallbackManagerForRetrieverRun(ParentRunManager, RetrieverManagerMixin):
|
||||
Args:
|
||||
documents (Sequence[Document]): The retrieved documents.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1265,6 +1300,7 @@ class CallbackManagerForRetrieverRun(ParentRunManager, RetrieverManagerMixin):
|
||||
Args:
|
||||
error (BaseException): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1291,6 +1327,7 @@ class AsyncCallbackManagerForRetrieverRun(
|
||||
|
||||
Returns:
|
||||
CallbackManagerForRetrieverRun: The sync RunManager.
|
||||
|
||||
"""
|
||||
return CallbackManagerForRetrieverRun(
|
||||
run_id=self.run_id,
|
||||
@@ -1312,6 +1349,7 @@ class AsyncCallbackManagerForRetrieverRun(
|
||||
Args:
|
||||
documents (Sequence[Document]): The retrieved documents.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1337,6 +1375,7 @@ class AsyncCallbackManagerForRetrieverRun(
|
||||
Args:
|
||||
error (BaseException): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1373,6 +1412,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
Returns:
|
||||
list[CallbackManagerForLLMRun]: A callback manager for each
|
||||
prompt as an LLM run.
|
||||
|
||||
"""
|
||||
managers = []
|
||||
for i, prompt in enumerate(prompts):
|
||||
@@ -1424,6 +1464,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
Returns:
|
||||
list[CallbackManagerForLLMRun]: A callback manager for each
|
||||
list of messages as an LLM run.
|
||||
|
||||
"""
|
||||
if messages and isinstance(messages[0], MessageV1Types):
|
||||
run_id_ = run_id if run_id is not None else uuid.uuid4()
|
||||
@@ -1503,6 +1544,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
|
||||
Returns:
|
||||
CallbackManagerForChainRun: The callback manager for the chain run.
|
||||
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
@@ -1557,6 +1599,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
|
||||
Returns:
|
||||
CallbackManagerForToolRun: The callback manager for the tool run.
|
||||
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
@@ -1603,6 +1646,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
run_id (UUID, optional): The ID of the run. Defaults to None.
|
||||
parent_run_id (UUID, optional): The ID of the parent run. Defaults to None.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
@@ -1650,6 +1694,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
run_id: The ID of the run. Defaults to None.
|
||||
|
||||
.. versionadded:: 0.2.14
|
||||
|
||||
"""
|
||||
if not self.handlers:
|
||||
return
|
||||
@@ -1704,6 +1749,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
|
||||
Returns:
|
||||
CallbackManager: The configured callback manager.
|
||||
|
||||
"""
|
||||
return _configure(
|
||||
cls,
|
||||
@@ -1738,6 +1784,7 @@ class CallbackManagerForChainGroup(CallbackManager):
|
||||
parent_run_id (Optional[UUID]): The ID of the parent run. Defaults to None.
|
||||
parent_run_manager (CallbackManagerForChainRun): The parent run manager.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
super().__init__(
|
||||
handlers,
|
||||
@@ -1826,6 +1873,7 @@ class CallbackManagerForChainGroup(CallbackManager):
|
||||
Args:
|
||||
outputs (Union[dict[str, Any], Any]): The outputs of the chain.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self.ended = True
|
||||
return self.parent_run_manager.on_chain_end(outputs, **kwargs)
|
||||
@@ -1840,6 +1888,7 @@ class CallbackManagerForChainGroup(CallbackManager):
|
||||
Args:
|
||||
error (Exception or KeyboardInterrupt): The error.
|
||||
**kwargs (Any): Additional keyword arguments.
|
||||
|
||||
"""
|
||||
self.ended = True
|
||||
return self.parent_run_manager.on_chain_error(error, **kwargs)
|
||||
|
@@ -117,9 +117,9 @@ class BaseChatMessageHistory(ABC):
|
||||
def add_user_message(self, message: Union[HumanMessage, str]) -> None:
|
||||
"""Convenience method for adding a human message string to the store.
|
||||
|
||||
Please note that this is a convenience method. Code should favor the
|
||||
bulk add_messages interface instead to save on round-trips to the underlying
|
||||
persistence layer.
|
||||
.. note::
|
||||
This is a convenience method. Code should favor the bulk ``add_messages``
|
||||
interface instead to save on round-trips to the persistence layer.
|
||||
|
||||
This method may be deprecated in a future release.
|
||||
|
||||
@@ -134,9 +134,9 @@ class BaseChatMessageHistory(ABC):
|
||||
def add_ai_message(self, message: Union[AIMessage, str]) -> None:
|
||||
"""Convenience method for adding an AI message string to the store.
|
||||
|
||||
Please note that this is a convenience method. Code should favor the bulk
|
||||
add_messages interface instead to save on round-trips to the underlying
|
||||
persistence layer.
|
||||
.. note::
|
||||
This is a convenience method. Code should favor the bulk ``add_messages``
|
||||
interface instead to save on round-trips to the persistence layer.
|
||||
|
||||
This method may be deprecated in a future release.
|
||||
|
||||
|
@@ -19,17 +19,18 @@ if TYPE_CHECKING:
|
||||
class BaseDocumentCompressor(BaseModel, ABC):
|
||||
"""Base class for document compressors.
|
||||
|
||||
This abstraction is primarily used for
|
||||
post-processing of retrieved documents.
|
||||
This abstraction is primarily used for post-processing of retrieved documents.
|
||||
|
||||
Documents matching a given query are first retrieved.
|
||||
|
||||
Then the list of documents can be further processed.
|
||||
|
||||
For example, one could re-rank the retrieved documents
|
||||
using an LLM.
|
||||
For example, one could re-rank the retrieved documents using an LLM.
|
||||
|
||||
.. note::
|
||||
Users should favor using a RunnableLambda instead of sub-classing from this
|
||||
interface.
|
||||
|
||||
**Note** users should favor using a RunnableLambda
|
||||
instead of sub-classing from this interface.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
@@ -48,6 +49,7 @@ class BaseDocumentCompressor(BaseModel, ABC):
|
||||
|
||||
Returns:
|
||||
The compressed documents.
|
||||
|
||||
"""
|
||||
|
||||
async def acompress_documents(
|
||||
@@ -65,6 +67,7 @@ class BaseDocumentCompressor(BaseModel, ABC):
|
||||
|
||||
Returns:
|
||||
The compressed documents.
|
||||
|
||||
"""
|
||||
return await run_in_executor(
|
||||
None, self.compress_documents, documents, query, callbacks
|
||||
|
@@ -488,8 +488,8 @@ class DeleteResponse(TypedDict, total=False):
|
||||
failed: Sequence[str]
|
||||
"""The IDs that failed to be deleted.
|
||||
|
||||
Please note that deleting an ID that
|
||||
does not exist is **NOT** considered a failure.
|
||||
.. warning::
|
||||
Deleting an ID that does not exist is **NOT** considered a failure.
|
||||
"""
|
||||
|
||||
num_failed: int
|
||||
|
@@ -58,8 +58,8 @@ class LangSmithParams(TypedDict, total=False):
|
||||
def get_tokenizer() -> Any:
|
||||
"""Get a GPT-2 tokenizer instance.
|
||||
|
||||
This function is cached to avoid re-loading the tokenizer
|
||||
every time it is called.
|
||||
This function is cached to avoid re-loading the tokenizer every time it is called.
|
||||
|
||||
"""
|
||||
try:
|
||||
from transformers import GPT2TokenizerFast # type: ignore[import-not-found]
|
||||
@@ -102,7 +102,8 @@ class BaseLanguageModel(
|
||||
):
|
||||
"""Abstract base class for interfacing with language models.
|
||||
|
||||
All language model wrappers inherited from BaseLanguageModel.
|
||||
All language model wrappers inherited from ``BaseLanguageModel``.
|
||||
|
||||
"""
|
||||
|
||||
cache: Union[BaseCache, bool, None] = Field(default=None, exclude=True)
|
||||
@@ -111,9 +112,10 @@ class BaseLanguageModel(
|
||||
* If true, will use the global cache.
|
||||
* If false, will not use a cache
|
||||
* If None, will use the global cache if it's set, otherwise no cache.
|
||||
* If instance of BaseCache, will use the provided cache.
|
||||
* If instance of ``BaseCache``, will use the provided cache.
|
||||
|
||||
Caching is not currently supported for streaming methods of models.
|
||||
|
||||
"""
|
||||
verbose: bool = Field(default_factory=_get_verbosity, exclude=True, repr=False)
|
||||
"""Whether to print out response text."""
|
||||
@@ -143,6 +145,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
The verbosity setting to use.
|
||||
|
||||
"""
|
||||
if verbose is None:
|
||||
return _get_verbosity()
|
||||
@@ -198,7 +201,8 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
An LLMResult, which contains a list of candidate Generations for each input
|
||||
prompt and additional model provider-specific output.
|
||||
prompt and additional model provider-specific output.
|
||||
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
@@ -232,8 +236,9 @@ class BaseLanguageModel(
|
||||
to the model provider API call.
|
||||
|
||||
Returns:
|
||||
An LLMResult, which contains a list of candidate Generations for each input
|
||||
prompt and additional model provider-specific output.
|
||||
An ``LLMResult``, which contains a list of candidate Generations for each
|
||||
input prompt and additional model provider-specific output.
|
||||
|
||||
"""
|
||||
|
||||
def with_structured_output(
|
||||
@@ -251,8 +256,8 @@ class BaseLanguageModel(
|
||||
) -> str:
|
||||
"""Pass a single string input to the model and return a string.
|
||||
|
||||
Use this method when passing in raw text. If you want to pass in specific
|
||||
types of chat messages, use predict_messages.
|
||||
Use this method when passing in raw text. If you want to pass in specific types
|
||||
of chat messages, use predict_messages.
|
||||
|
||||
Args:
|
||||
text: String input to pass to the model.
|
||||
@@ -263,6 +268,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
Top model prediction as a string.
|
||||
|
||||
"""
|
||||
|
||||
@deprecated("0.1.7", alternative="invoke", removal="1.0")
|
||||
@@ -277,7 +283,7 @@ class BaseLanguageModel(
|
||||
"""Pass a message sequence to the model and return a message.
|
||||
|
||||
Use this method when passing in chat messages. If you want to pass in raw text,
|
||||
use predict.
|
||||
use predict.
|
||||
|
||||
Args:
|
||||
messages: A sequence of chat messages corresponding to a single model input.
|
||||
@@ -288,6 +294,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
Top model prediction as a message.
|
||||
|
||||
"""
|
||||
|
||||
@deprecated("0.1.7", alternative="ainvoke", removal="1.0")
|
||||
@@ -298,7 +305,7 @@ class BaseLanguageModel(
|
||||
"""Asynchronously pass a string to the model and return a string.
|
||||
|
||||
Use this method when calling pure text generation models and only the top
|
||||
candidate generation is needed.
|
||||
candidate generation is needed.
|
||||
|
||||
Args:
|
||||
text: String input to pass to the model.
|
||||
@@ -309,6 +316,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
Top model prediction as a string.
|
||||
|
||||
"""
|
||||
|
||||
@deprecated("0.1.7", alternative="ainvoke", removal="1.0")
|
||||
@@ -322,8 +330,8 @@ class BaseLanguageModel(
|
||||
) -> BaseMessage:
|
||||
"""Asynchronously pass messages to the model and return a message.
|
||||
|
||||
Use this method when calling chat models and only the top
|
||||
candidate generation is needed.
|
||||
Use this method when calling chat models and only the top candidate generation
|
||||
is needed.
|
||||
|
||||
Args:
|
||||
messages: A sequence of chat messages corresponding to a single model input.
|
||||
@@ -334,6 +342,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
Top model prediction as a message.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
@@ -349,7 +358,8 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
A list of ids corresponding to the tokens in the text, in order they occur
|
||||
in the text.
|
||||
in the text.
|
||||
|
||||
"""
|
||||
if self.custom_get_token_ids is not None:
|
||||
return self.custom_get_token_ids(text)
|
||||
@@ -365,6 +375,7 @@ class BaseLanguageModel(
|
||||
|
||||
Returns:
|
||||
The integer number of tokens in the text.
|
||||
|
||||
"""
|
||||
return len(self.get_token_ids(text))
|
||||
|
||||
@@ -377,16 +388,18 @@ class BaseLanguageModel(
|
||||
|
||||
Useful for checking if an input fits in a model's context window.
|
||||
|
||||
**Note**: the base implementation of get_num_tokens_from_messages ignores
|
||||
tool schemas.
|
||||
.. note::
|
||||
The base implementation of ``get_num_tokens_from_messages`` ignores tool
|
||||
schemas.
|
||||
|
||||
Args:
|
||||
messages: The message inputs to tokenize.
|
||||
tools: If provided, sequence of dict, BaseModel, function, or BaseTools
|
||||
to be converted to tool schemas.
|
||||
tools: If provided, sequence of dict, ``BaseModel``, function, or
|
||||
``BaseTools`` to be converted to tool schemas.
|
||||
|
||||
Returns:
|
||||
The sum of the number of tokens across the messages.
|
||||
|
||||
"""
|
||||
if tools is not None:
|
||||
warnings.warn(
|
||||
@@ -399,6 +412,7 @@ class BaseLanguageModel(
|
||||
def _all_required_field_names(cls) -> set:
|
||||
"""DEPRECATED: Kept for backwards compatibility.
|
||||
|
||||
Use get_pydantic_field_names.
|
||||
Use ``get_pydantic_field_names``.
|
||||
|
||||
"""
|
||||
return get_pydantic_field_names(cls)
|
||||
|
@@ -97,17 +97,18 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
|
||||
|
||||
|
||||
def _format_for_tracing(messages: list[BaseMessage]) -> list[BaseMessage]:
|
||||
"""Format messages for tracing in on_chat_model_start.
|
||||
"""Format messages for tracing in ``on_chat_model_start``.
|
||||
|
||||
- Update image content blocks to OpenAI Chat Completions format (backward
|
||||
compatibility).
|
||||
- Add "type" key to content blocks that have a single key.
|
||||
- Add ``type`` key to content blocks that have a single key.
|
||||
|
||||
Args:
|
||||
messages: List of messages to format.
|
||||
|
||||
Returns:
|
||||
List of messages formatted for tracing.
|
||||
|
||||
"""
|
||||
messages_to_trace = []
|
||||
for message in messages:
|
||||
@@ -153,10 +154,11 @@ def generate_from_stream(stream: Iterator[ChatGenerationChunk]) -> ChatResult:
|
||||
"""Generate from a stream.
|
||||
|
||||
Args:
|
||||
stream: Iterator of ChatGenerationChunk.
|
||||
stream: Iterator of ``ChatGenerationChunk``.
|
||||
|
||||
Returns:
|
||||
ChatResult: Chat result.
|
||||
|
||||
"""
|
||||
generation = next(stream, None)
|
||||
if generation:
|
||||
@@ -180,10 +182,11 @@ async def agenerate_from_stream(
|
||||
"""Async generate from a stream.
|
||||
|
||||
Args:
|
||||
stream: Iterator of ChatGenerationChunk.
|
||||
stream: Iterator of ``ChatGenerationChunk``.
|
||||
|
||||
Returns:
|
||||
ChatResult: Chat result.
|
||||
|
||||
"""
|
||||
chunks = [chunk async for chunk in stream]
|
||||
return await run_in_executor(None, generate_from_stream, iter(chunks))
|
||||
@@ -311,15 +314,16 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
provided. This offers the best of both worlds.
|
||||
- If False (default), will always use streaming case if available.
|
||||
|
||||
The main reason for this flag is that code might be written using ``.stream()`` and
|
||||
The main reason for this flag is that code might be written using ``stream()`` and
|
||||
a user may want to swap out a given model for another model whose the implementation
|
||||
does not properly support streaming.
|
||||
|
||||
"""
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def raise_deprecation(cls, values: dict) -> Any:
|
||||
"""Raise deprecation warning if callback_manager is used.
|
||||
"""Raise deprecation warning if ``callback_manager`` is used.
|
||||
|
||||
Args:
|
||||
values (Dict): Values to validate.
|
||||
@@ -328,7 +332,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
Dict: Validated values.
|
||||
|
||||
Raises:
|
||||
DeprecationWarning: If callback_manager is used.
|
||||
DeprecationWarning: If ``callback_manager`` is used.
|
||||
|
||||
"""
|
||||
if values.get("callback_manager") is not None:
|
||||
warnings.warn(
|
||||
@@ -653,6 +658,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
List of ChatGeneration objects.
|
||||
|
||||
"""
|
||||
converted_generations = []
|
||||
for gen in cache_val:
|
||||
@@ -778,7 +784,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
An LLMResult, which contains a list of candidate Generations for each input
|
||||
prompt and additional model provider-specific output.
|
||||
prompt and additional model provider-specific output.
|
||||
|
||||
"""
|
||||
ls_structured_output_format = kwargs.pop(
|
||||
"ls_structured_output_format", None
|
||||
@@ -892,7 +899,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
An LLMResult, which contains a list of candidate Generations for each input
|
||||
prompt and additional model provider-specific output.
|
||||
prompt and additional model provider-specific output.
|
||||
|
||||
"""
|
||||
ls_structured_output_format = kwargs.pop(
|
||||
"ls_structured_output_format", None
|
||||
@@ -1248,6 +1256,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
The model output message.
|
||||
|
||||
"""
|
||||
generation = self.generate(
|
||||
[messages], stop=stop, callbacks=callbacks, **kwargs
|
||||
@@ -1288,6 +1297,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
The model output string.
|
||||
|
||||
"""
|
||||
return self.predict(message, stop=stop, **kwargs)
|
||||
|
||||
@@ -1307,6 +1317,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
The predicted output string.
|
||||
|
||||
"""
|
||||
stop_ = None if stop is None else list(stop)
|
||||
result = self([HumanMessage(content=text)], stop=stop_, **kwargs)
|
||||
@@ -1382,6 +1393,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
|
||||
Returns:
|
||||
A Runnable that returns a message.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -1544,8 +1556,10 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
||||
class SimpleChatModel(BaseChatModel):
|
||||
"""Simplified implementation for a chat model to inherit from.
|
||||
|
||||
**Note** This implementation is primarily here for backwards compatibility.
|
||||
For new implementations, please use `BaseChatModel` directly.
|
||||
.. note::
|
||||
This implementation is primarily here for backwards compatibility. For new
|
||||
implementations, please use ``BaseChatModel`` directly.
|
||||
|
||||
"""
|
||||
|
||||
def _generate(
|
||||
|
@@ -227,11 +227,12 @@ class GenericFakeChatModel(BaseChatModel):
|
||||
This can be expanded to accept other types like Callables / dicts / strings
|
||||
to make the interface more generic if needed.
|
||||
|
||||
Note: if you want to pass a list, you can use `iter` to convert it to an iterator.
|
||||
.. note::
|
||||
if you want to pass a list, you can use ``iter`` to convert it to an iterator.
|
||||
|
||||
Please note that streaming is not implemented yet. We should try to implement it
|
||||
in the future by delegating to invoke and then breaking the resulting output
|
||||
into message chunks.
|
||||
.. warning::
|
||||
Streaming is not implemented yet. We should try to implement it in the future by
|
||||
delegating to invoke and then breaking the resulting output into message chunks.
|
||||
"""
|
||||
|
||||
@override
|
||||
|
@@ -94,10 +94,9 @@ def dumps(obj: Any, *, pretty: bool = False, **kwargs: Any) -> str:
|
||||
def dumpd(obj: Any) -> Any:
|
||||
"""Return a dict representation of an object.
|
||||
|
||||
Note:
|
||||
Unfortunately this function is not as efficient as it could be
|
||||
because it first dumps the object to a json string and then loads it
|
||||
back into a dictionary.
|
||||
.. note::
|
||||
Unfortunately this function is not as efficient as it could be because it first
|
||||
dumps the object to a json string and then loads it back into a dictionary.
|
||||
|
||||
Args:
|
||||
obj: The object to dump.
|
||||
|
@@ -943,22 +943,23 @@ def trim_messages(
|
||||
properties:
|
||||
|
||||
1. The resulting chat history should be valid. Most chat models expect that chat
|
||||
history starts with either (1) a `HumanMessage` or (2) a `SystemMessage` followed
|
||||
by a `HumanMessage`. To achieve this, set `start_on="human"`.
|
||||
In addition, generally a `ToolMessage` can only appear after an `AIMessage`
|
||||
history starts with either (1) a ``HumanMessage`` or (2) a ``SystemMessage`` followed
|
||||
by a ``HumanMessage``. To achieve this, set ``start_on="human"``.
|
||||
In addition, generally a ``ToolMessage`` can only appear after an ``AIMessage``
|
||||
that involved a tool call.
|
||||
Please see the following link for more information about messages:
|
||||
https://python.langchain.com/docs/concepts/#messages
|
||||
2. It includes recent messages and drops old messages in the chat history.
|
||||
To achieve this set the `strategy="last"`.
|
||||
3. Usually, the new chat history should include the `SystemMessage` if it
|
||||
was present in the original chat history since the `SystemMessage` includes
|
||||
special instructions to the chat model. The `SystemMessage` is almost always
|
||||
To achieve this set the ``strategy="last"``.
|
||||
3. Usually, the new chat history should include the ``SystemMessage`` if it
|
||||
was present in the original chat history since the ``SystemMessage`` includes
|
||||
special instructions to the chat model. The ``SystemMessage`` is almost always
|
||||
the first message in the history if present. To achieve this set the
|
||||
`include_system=True`.
|
||||
``include_system=True``.
|
||||
|
||||
**Note** The examples below show how to configure `trim_messages` to achieve
|
||||
a behavior consistent with the above properties.
|
||||
.. note::
|
||||
The examples below show how to configure ``trim_messages`` to achieve a behavior
|
||||
consistent with the above properties.
|
||||
|
||||
Args:
|
||||
messages: Sequence of Message-like objects to trim.
|
||||
@@ -1868,26 +1869,26 @@ def count_tokens_approximately(
|
||||
chars_per_token: Number of characters per token to use for the approximation.
|
||||
Default is 4 (one token corresponds to ~4 chars for common English text).
|
||||
You can also specify float values for more fine-grained control.
|
||||
See more here: https://platform.openai.com/tokenizer
|
||||
`See more here. <https://platform.openai.com/tokenizer>`__
|
||||
extra_tokens_per_message: Number of extra tokens to add per message.
|
||||
Default is 3 (special tokens, including beginning/end of message).
|
||||
You can also specify float values for more fine-grained control.
|
||||
See more here:
|
||||
https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
|
||||
`See more here. <https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb>`__
|
||||
count_name: Whether to include message names in the count.
|
||||
Enabled by default.
|
||||
|
||||
Returns:
|
||||
Approximate number of tokens in the messages.
|
||||
|
||||
Note:
|
||||
This is a simple approximation that may not match the exact token count
|
||||
used by specific models. For accurate counts, use model-specific tokenizers.
|
||||
.. note::
|
||||
This is a simple approximation that may not match the exact token count used by
|
||||
specific models. For accurate counts, use model-specific tokenizers.
|
||||
|
||||
Warning:
|
||||
This function does not currently support counting image tokens.
|
||||
|
||||
.. versionadded:: 0.3.46
|
||||
|
||||
"""
|
||||
token_count = 0.0
|
||||
for message in convert_to_messages(messages):
|
||||
|
@@ -2729,10 +2729,10 @@ class RunnableSequence(RunnableSerializable[Input, Output]):
|
||||
streaming will only begin after this component is run. If there are
|
||||
multiple blocking components, streaming begins after the last one.
|
||||
|
||||
Please note: ``RunnableLambdas`` do not support ``transform`` by default! So if
|
||||
you need to use a ``RunnableLambdas`` be careful about where you place them in a
|
||||
``RunnableSequence`` (if you need to use the ``stream``/``astream``
|
||||
methods).
|
||||
.. note::
|
||||
``RunnableLambdas`` do not support ``transform`` by default! So if you need to
|
||||
use a ``RunnableLambdas`` be careful about where you place them in a
|
||||
``RunnableSequence`` (if you need to use the ``stream``/``astream`` methods).
|
||||
|
||||
If you need arbitrary logic and need streaming, you can subclass
|
||||
Runnable, and implement ``transform`` for whatever logic you need.
|
||||
|
@@ -30,8 +30,9 @@ def try_to_import(module_name: str) -> tuple[int, str]:
|
||||
def test_importable_all_via_subprocess() -> None:
|
||||
"""Test import in isolation.
|
||||
|
||||
Note: ImportErrors due to circular imports can be raised
|
||||
for one sequence of imports but not another.
|
||||
.. note::
|
||||
ImportErrors due to circular imports can be raised for one sequence of imports
|
||||
but not another.
|
||||
"""
|
||||
module_names = []
|
||||
for path in Path("../core/langchain_core/").glob("*"):
|
||||
|
@@ -2018,6 +2018,7 @@ def test_args_schema_explicitly_typed() -> None:
|
||||
|
||||
Please note that this will test using pydantic 2 even though BaseTool
|
||||
is a pydantic 1 model!
|
||||
|
||||
"""
|
||||
# Check with whatever pydantic model is passed in and not via v1 namespace
|
||||
from pydantic import BaseModel
|
||||
|
@@ -14,11 +14,10 @@
|
||||
[](https://libraries.io/github/langchain-ai/langchain)
|
||||
[](https://github.com/langchain-ai/langchain/issues)
|
||||
|
||||
|
||||
Looking for the JS/TS version? Check out [LangChain.js](https://github.com/langchain-ai/langchainjs).
|
||||
|
||||
To help you ship LangChain apps to production faster, check out [LangSmith](https://smith.langchain.com).
|
||||
[LangSmith](https://smith.langchain.com) is a unified developer platform for building, testing, and monitoring LLM applications.
|
||||
To help you ship LangChain apps to production faster, check out [LangSmith](https://smith.langchain.com).
|
||||
[LangSmith](https://smith.langchain.com) is a unified developer platform for building, testing, and monitoring LLM applications.
|
||||
Fill out [this form](https://www.langchain.com/contact-sales) to speak with our sales team.
|
||||
|
||||
## Quick Install
|
||||
|
@@ -41,8 +41,9 @@ def create_vectorstore_agent(
|
||||
) -> AgentExecutor:
|
||||
"""Construct a VectorStore agent from an LLM and tools.
|
||||
|
||||
Note: this class is deprecated. See below for a replacement that uses tool
|
||||
calling methods and LangGraph. Install LangGraph with:
|
||||
.. note::
|
||||
This class is deprecated. See below for a replacement that uses tool
|
||||
calling methods and LangGraph. Install LangGraph with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -135,8 +136,9 @@ def create_vectorstore_router_agent(
|
||||
) -> AgentExecutor:
|
||||
"""Construct a VectorStore router agent from an LLM and tools.
|
||||
|
||||
Note: this class is deprecated. See below for a replacement that uses tool
|
||||
calling methods and LangGraph. Install LangGraph with:
|
||||
.. note::
|
||||
This class is deprecated. See below for a replacement that uses tool calling
|
||||
methods and LangGraph. Install LangGraph with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@@ -46,11 +46,11 @@ class XMLAgentOutputParser(AgentOutputParser):
|
||||
Final answer (returns AgentFinish):
|
||||
<final_answer>The answer is 4</final_answer>
|
||||
|
||||
Note:
|
||||
Minimal escaping allows tool names containing XML tags to be safely
|
||||
represented. For example, a tool named "search<tool>nested</tool>" would be
|
||||
escaped as "search[[tool]]nested[[/tool]]" in the XML and automatically
|
||||
unescaped during parsing.
|
||||
.. note::
|
||||
Minimal escaping allows tool names containing XML tags to be safely represented.
|
||||
For example, a tool named ``search<tool>nested</tool>`` would be escaped as
|
||||
``search[[tool]]nested[[/tool]]`` in the XML and automatically unescaped during
|
||||
parsing.
|
||||
|
||||
Raises:
|
||||
ValueError: If the input doesn't match either expected XML format or
|
||||
|
@@ -82,8 +82,9 @@ try:
|
||||
|
||||
See https://python.langchain.com/docs/security for more information.
|
||||
|
||||
Note: this class is deprecated. See below for a replacement implementation
|
||||
using LangGraph. The benefits of this implementation are:
|
||||
.. note::
|
||||
This class is deprecated. See below for a replacement implementation using
|
||||
LangGraph. The benefits of this implementation are:
|
||||
|
||||
- Uses LLM tool calling features to encourage properly-formatted API requests;
|
||||
- Support for both token-by-token and step-by-step streaming;
|
||||
|
@@ -26,8 +26,9 @@ from langchain.chains.llm import LLMChain
|
||||
class ConstitutionalChain(Chain):
|
||||
"""Chain for applying constitutional principles.
|
||||
|
||||
Note: this class is deprecated. See below for a replacement implementation
|
||||
using LangGraph. The benefits of this implementation are:
|
||||
.. note::
|
||||
This class is deprecated. See below for a replacement implementation using
|
||||
LangGraph. The benefits of this implementation are:
|
||||
|
||||
- Uses LLM tool calling features instead of parsing string responses;
|
||||
- Support for both token-by-token and step-by-step streaming;
|
||||
|
@@ -33,8 +33,9 @@ from langchain.chains.llm_math.prompt import PROMPT
|
||||
class LLMMathChain(Chain):
|
||||
"""Chain that interprets a prompt and executes python code to do math.
|
||||
|
||||
Note: this class is deprecated. See below for a replacement implementation
|
||||
using LangGraph. The benefits of this implementation are:
|
||||
.. note::
|
||||
This class is deprecated. See below for a replacement implementation using
|
||||
LangGraph. The benefits of this implementation are:
|
||||
|
||||
- Uses LLM tool calling features;
|
||||
- Support for both token-by-token and step-by-step streaming;
|
||||
|
@@ -58,7 +58,7 @@ class GenericFakeChatModel(BaseChatModel):
|
||||
"""A generic fake chat model that can be used to test the chat model interface.
|
||||
|
||||
* Chat model should be usable in both sync and async tests
|
||||
* Invokes on_llm_new_token to allow for testing of callback related code for new
|
||||
* Invokes ``on_llm_new_token`` to allow for testing of callback related code for new
|
||||
tokens.
|
||||
* Includes logic to break messages into message chunk to facilitate testing of
|
||||
streaming.
|
||||
@@ -67,14 +67,16 @@ class GenericFakeChatModel(BaseChatModel):
|
||||
messages: Iterator[AIMessage]
|
||||
"""Get an iterator over messages.
|
||||
|
||||
This can be expanded to accept other types like Callables / dicts / strings
|
||||
This can be expanded to accept other types like ``Callables`` / dicts / strings
|
||||
to make the interface more generic if needed.
|
||||
|
||||
Note: if you want to pass a list, you can use `iter` to convert it to an iterator.
|
||||
.. note::
|
||||
If you want to pass a list, you can use ``iter`` to convert it to an iterator.
|
||||
|
||||
.. warning::
|
||||
Streaming is not implemented yet. We should try to implement it in the future by
|
||||
delegating to invoke and then breaking the resulting output into message chunks.
|
||||
|
||||
Please note that streaming is not implemented yet. We should try to implement it
|
||||
in the future by delegating to invoke and then breaking the resulting output
|
||||
into message chunks.
|
||||
"""
|
||||
|
||||
@override
|
||||
|
@@ -14,11 +14,10 @@
|
||||
[](https://libraries.io/github/langchain-ai/langchain)
|
||||
[](https://github.com/langchain-ai/langchain/issues)
|
||||
|
||||
|
||||
Looking for the JS/TS version? Check out [LangChain.js](https://github.com/langchain-ai/langchainjs).
|
||||
|
||||
To help you ship LangChain apps to production faster, check out [LangSmith](https://smith.langchain.com).
|
||||
[LangSmith](https://smith.langchain.com) is a unified developer platform for building, testing, and monitoring LLM applications.
|
||||
To help you ship LangChain apps to production faster, check out [LangSmith](https://smith.langchain.com).
|
||||
[LangSmith](https://smith.langchain.com) is a unified developer platform for building, testing, and monitoring LLM applications.
|
||||
Fill out [this form](https://www.langchain.com/contact-sales) to speak with our sales team.
|
||||
|
||||
## Quick Install
|
||||
|
@@ -22,9 +22,10 @@ def format_document_xml(doc: Document) -> str:
|
||||
<metadata>...</metadata>
|
||||
</document>
|
||||
|
||||
Note:
|
||||
Does not generate valid XML or escape special characters.
|
||||
Intended for semi-structured LLM input only.
|
||||
.. note::
|
||||
Does not generate valid XML or escape special characters. Intended for
|
||||
semi-structured LLM input only.
|
||||
|
||||
"""
|
||||
id_str = f"<id>{doc.id}</id>" if doc.id is not None else "<id></id>"
|
||||
metadata_str = ""
|
||||
|
@@ -6,6 +6,7 @@ and asynchronous prompt resolution with automatic detection of callable types.
|
||||
|
||||
The module is designed to handle common prompt patterns across LangChain components,
|
||||
particularly for summarization chains and other document processing workflows.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -65,10 +66,10 @@ def resolve_prompt(
|
||||
messages = resolve_prompt(None, state, runtime, "content", "Default")
|
||||
```
|
||||
|
||||
Note:
|
||||
Callable prompts have full control over message structure and content
|
||||
parameter is ignored. String/None prompts create standard system + user
|
||||
structure.
|
||||
.. note::
|
||||
Callable prompts have full control over message structure and content parameter
|
||||
is ignored. String/None prompts create standard system + user structure.
|
||||
|
||||
"""
|
||||
if callable(prompt):
|
||||
return prompt(state, runtime)
|
||||
@@ -141,10 +142,10 @@ async def aresolve_prompt(
|
||||
messages = await aresolve_prompt("Custom", state, runtime, "content", "default")
|
||||
```
|
||||
|
||||
Note:
|
||||
Callable prompts have full control over message structure and content
|
||||
parameter is ignored. Automatically detects and handles async
|
||||
callables.
|
||||
.. note::
|
||||
Callable prompts have full control over message structure and content parameter
|
||||
is ignored. Automatically detects and handles async callables.
|
||||
|
||||
"""
|
||||
if callable(prompt):
|
||||
result = prompt(state, runtime)
|
||||
|
@@ -13,9 +13,10 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class TypedDictLikeV1(Protocol):
|
||||
"""Protocol to represent types that behave like TypedDicts.
|
||||
"""Protocol to represent types that behave like ``TypedDict``s.
|
||||
|
||||
Version 1: using ``ClassVar`` for keys.
|
||||
|
||||
Version 1: using `ClassVar` for keys.
|
||||
"""
|
||||
|
||||
__required_keys__: ClassVar[frozenset[str]]
|
||||
@@ -23,9 +24,10 @@ class TypedDictLikeV1(Protocol):
|
||||
|
||||
|
||||
class TypedDictLikeV2(Protocol):
|
||||
"""Protocol to represent types that behave like TypedDicts.
|
||||
"""Protocol to represent types that behave like ``TypedDict``s.
|
||||
|
||||
Version 2: not using ``ClassVar`` for keys.
|
||||
|
||||
Version 2: not using `ClassVar` for keys.
|
||||
"""
|
||||
|
||||
__required_keys__: frozenset[str]
|
||||
@@ -35,8 +37,9 @@ class TypedDictLikeV2(Protocol):
|
||||
class DataclassLike(Protocol):
|
||||
"""Protocol to represent types that behave like dataclasses.
|
||||
|
||||
Inspired by the private _DataclassT from dataclasses that uses a similar
|
||||
Inspired by the private ``_DataclassT`` from dataclasses that uses a similar
|
||||
protocol as a bound.
|
||||
|
||||
"""
|
||||
|
||||
__dataclass_fields__: ClassVar[dict[str, Field[Any]]]
|
||||
@@ -45,9 +48,12 @@ class DataclassLike(Protocol):
|
||||
StateLike: TypeAlias = Union[TypedDictLikeV1, TypedDictLikeV2, DataclassLike, BaseModel]
|
||||
"""Type alias for state-like types.
|
||||
|
||||
It can either be a `TypedDict`, `dataclass`, or Pydantic `BaseModel`.
|
||||
Note: we cannot use either `TypedDict` or `dataclass` directly due to limitations in
|
||||
type checking.
|
||||
It can either be a ``TypedDict``, ``dataclass``, or Pydantic ``BaseModel``.
|
||||
|
||||
.. note::
|
||||
We cannot use either ``TypedDict`` or ``dataclass`` directly due to limitations in
|
||||
type checking.
|
||||
|
||||
"""
|
||||
|
||||
StateT = TypeVar("StateT", bound=StateLike)
|
||||
|
@@ -567,7 +567,7 @@ def create_map_reduce_chain(
|
||||
A LangGraph that can be invoked with documents to get map-reduce
|
||||
extraction results.
|
||||
|
||||
Note:
|
||||
.. note::
|
||||
This implementation is well-suited for large document collections as it
|
||||
processes documents in parallel during the map phase. The Send API enables
|
||||
efficient parallelization while maintaining clean state management.
|
||||
|
@@ -454,7 +454,7 @@ def create_stuff_documents_chain(
|
||||
Returns:
|
||||
A LangGraph that can be invoked with documents to extract information.
|
||||
|
||||
Note:
|
||||
.. note::
|
||||
This is a "stuff" documents chain that puts all documents into the context
|
||||
window and processes them together. It supports refining existing results.
|
||||
Default prompts are optimized for summarization but can be customized for
|
||||
|
@@ -1,3 +1,3 @@
|
||||
This package has moved!
|
||||
|
||||
https://github.com/langchain-ai/langchain-ai21/tree/main/libs/ai21
|
||||
https://github.com/langchain-ai/langchain-ai21/tree/main/libs/ai21
|
||||
|
@@ -1,3 +1,3 @@
|
||||
This package has moved!
|
||||
|
||||
https://github.com/langchain-ai/langchain-datastax/tree/main/libs/astradb
|
||||
https://github.com/langchain-ai/langchain-datastax/tree/main/libs/astradb
|
||||
|
@@ -1,3 +1,3 @@
|
||||
This package has moved!
|
||||
|
||||
https://github.com/langchain-ai/langchain-ibm/tree/main/libs/ibm
|
||||
https://github.com/langchain-ai/langchain-ibm/tree/main/libs/ibm
|
||||
|
@@ -115,7 +115,10 @@ class AzureOpenAIEmbeddings(OpenAIEmbeddings): # type: ignore[override]
|
||||
"""A model deployment.
|
||||
|
||||
If given sets the base client URL to include `/deployments/{azure_deployment}`.
|
||||
Note: this means you won't be able to use non-deployment endpoints.
|
||||
|
||||
.. note::
|
||||
This means you won't be able to use non-deployment endpoints.
|
||||
|
||||
"""
|
||||
# Check OPENAI_KEY for backwards compatibility.
|
||||
# TODO: Remove OPENAI_API_KEY support to avoid possible conflict when using
|
||||
@@ -132,7 +135,7 @@ class AzureOpenAIEmbeddings(OpenAIEmbeddings): # type: ignore[override]
|
||||
alias="api_version",
|
||||
)
|
||||
"""Automatically inferred from env var ``OPENAI_API_VERSION`` if not provided.
|
||||
|
||||
|
||||
Set to ``'2023-05-15'`` by default if env variable ``OPENAI_API_VERSION`` is not
|
||||
set.
|
||||
"""
|
||||
|
@@ -43,10 +43,13 @@ class AzureOpenAI(BaseOpenAI):
|
||||
Example: ``'https://example-resource.azure.openai.com/'``
|
||||
"""
|
||||
deployment_name: Union[str, None] = Field(default=None, alias="azure_deployment")
|
||||
"""A model deployment.
|
||||
"""A model deployment.
|
||||
|
||||
If given sets the base client URL to include `/deployments/{azure_deployment}`.
|
||||
Note: this means you won't be able to use non-deployment endpoints.
|
||||
|
||||
.. note::
|
||||
This means you won't be able to use non-deployment endpoints.
|
||||
|
||||
"""
|
||||
openai_api_version: Optional[str] = Field(
|
||||
alias="api_version",
|
||||
@@ -87,7 +90,7 @@ class AzureOpenAI(BaseOpenAI):
|
||||
)
|
||||
"""Legacy, for ``openai<1.0.0`` support."""
|
||||
validate_base_url: bool = True
|
||||
"""For backwards compatibility. If legacy val openai_api_base is passed in, try to
|
||||
"""For backwards compatibility. If legacy val openai_api_base is passed in, try to
|
||||
infer if it is a base_url or azure_endpoint and update accordingly.
|
||||
"""
|
||||
|
||||
|
@@ -2549,8 +2549,9 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override]
|
||||
If schema is specified via TypedDict or JSON schema, ``strict`` is not
|
||||
enabled by default. Pass ``strict=True`` to enable it.
|
||||
|
||||
Note: ``strict`` can only be non-null if ``method`` is
|
||||
``"json_schema"`` or ``"function_calling"``.
|
||||
.. note:
|
||||
``strict`` can only be non-null if ``method`` is ``"json_schema"``
|
||||
or ``"function_calling"``.
|
||||
tools:
|
||||
A list of tool-like objects to bind to the chat model. Requires that:
|
||||
|
||||
|
@@ -8,7 +8,7 @@ license = { text = "MIT" }
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"langchain-core<1.0.0,>=0.4.0.dev0",
|
||||
"openai<2.0.0,>=1.86.0",
|
||||
"openai<2.0.0,>=1.99.3",
|
||||
"tiktoken<1,>=0.7",
|
||||
]
|
||||
name = "langchain-openai"
|
||||
|
@@ -66,7 +66,7 @@ class TestOpenAIStandard(ChatModelIntegrationTests):
|
||||
readme = f.read()
|
||||
|
||||
input_ = f"""What's langchain? Here's the langchain README:
|
||||
|
||||
|
||||
{readme}
|
||||
"""
|
||||
llm = ChatOpenAI(model="gpt-4o-mini", stream_usage=True)
|
||||
|
@@ -31,7 +31,7 @@ class TestOpenAIResponses(TestOpenAIStandard):
|
||||
readme = f.read()
|
||||
|
||||
input_ = f"""What's langchain? Here's the langchain README:
|
||||
|
||||
|
||||
{readme}
|
||||
"""
|
||||
llm = ChatOpenAI(model="gpt-4.1-mini", output_version="responses/v1")
|
||||
|
8
libs/partners/openai/uv.lock
generated
8
libs/partners/openai/uv.lock
generated
@@ -588,7 +588,7 @@ typing = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "langchain-core", editable = "../../core" },
|
||||
{ name = "openai", specifier = ">=1.86.0,<2.0.0" },
|
||||
{ name = "openai", specifier = ">=1.99.3,<2.0.0" },
|
||||
{ name = "tiktoken", specifier = ">=0.7,<1" },
|
||||
]
|
||||
|
||||
@@ -995,7 +995,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.99.2"
|
||||
version = "1.99.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -1007,9 +1007,9 @@ dependencies = [
|
||||
{ name = "tqdm" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/de/2c/8cd1684364551237a5e6db24ce25c25ff54efcf1805b39110ec713dc2972/openai-1.99.2.tar.gz", hash = "sha256:118075b48109aa237636607b1346cf03b37cb9d74b0414cb11095850a0a22c96", size = 504752, upload-time = "2025-08-07T17:16:14.668Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/d3/c372420c8ca1c60e785fd8c19e536cea8f16b0cfdcdad6458e1d8884f2ea/openai-1.99.3.tar.gz", hash = "sha256:1a0e2910e4545d828c14218f2ac3276827c94a043f5353e43b9413b38b497897", size = 504932, upload-time = "2025-08-07T20:35:15.893Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/06/f3338c1b8685dc1634fa5174dc5ba2d3eecc7887c9fc539bb5da6f75ebb3/openai-1.99.2-py3-none-any.whl", hash = "sha256:110d85b8ed400e1d7b02f8db8e245bd757bcde347cb6923155f42cd66a10aa0b", size = 785594, upload-time = "2025-08-07T17:16:13.083Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/bc/e52f49940b4e320629da7db09c90a2407a48c612cff397b4b41b7e58cdf9/openai-1.99.3-py3-none-any.whl", hash = "sha256:c786a03f6cddadb5ee42c6d749aa4f6134fe14fdd7d69a667e5e7ce7fd29a719", size = 785776, upload-time = "2025-08-07T20:35:13.653Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@@ -41,7 +41,7 @@ sample:
|
||||
---
|
||||
system:
|
||||
You are an AI assistant who helps people find information.
|
||||
As the assistant, you answer questions briefly, succinctly,
|
||||
As the assistant, you answer questions briefly, succinctly,
|
||||
and in a personable manner using markdown and even add some personal flair with appropriate emojis.
|
||||
|
||||
{% for item in chat_history %}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
This package has moved!
|
||||
|
||||
https://github.com/langchain-ai/langchain-together/tree/main/libs/together
|
||||
https://github.com/langchain-ai/langchain-together/tree/main/libs/together
|
||||
|
@@ -264,8 +264,10 @@ class TestFakeChatModelV1(ChatModelV1UnitTests):
|
||||
This file demonstrates how to use the new content blocks v1 integration test suite
|
||||
for testing real chat models that support the enhanced content blocks system.
|
||||
|
||||
Note: This is a template/example. Real implementations should replace
|
||||
FakeChatModelV1 with actual chat model classes.
|
||||
.. note::
|
||||
This is a template/example. Real implementations should replace ``FakeChatModelV1``
|
||||
with actual chat model classes.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@@ -7,7 +7,9 @@
|
||||
BaseDocumentTransformer --> TextSplitter --> <name>TextSplitter # Example: CharacterTextSplitter
|
||||
RecursiveCharacterTextSplitter --> <name>TextSplitter
|
||||
|
||||
Note: **MarkdownHeaderTextSplitter** and **HTMLHeaderTextSplitter do not derive from TextSplitter.
|
||||
|
||||
.. note::
|
||||
**MarkdownHeaderTextSplitter** and **HTMLHeaderTextSplitter do not derive from TextSplitter.
|
||||
|
||||
|
||||
**Main helpers:**
|
||||
|
@@ -3053,18 +3053,18 @@ End Function
|
||||
Public Sub Main()
|
||||
Dim i As Integer
|
||||
Dim limit As Integer
|
||||
|
||||
|
||||
i = 0
|
||||
limit = 50
|
||||
|
||||
|
||||
While i < limit
|
||||
i = SumTwoIntegers(i, 1)
|
||||
|
||||
|
||||
If i = limit \\ 2 Then
|
||||
MsgBox "Halfway there! i = " & i
|
||||
End If
|
||||
Wend
|
||||
|
||||
|
||||
MsgBox "Done! Final value of i: " & i
|
||||
End Sub
|
||||
"""
|
||||
|
Reference in New Issue
Block a user