chore: formatting across codebase (#32456)

To prevent polluting future PRs
This commit is contained in:
Mason Daugherty
2025-08-07 22:09:26 -04:00
committed by GitHub
parent 11d68a0b9e
commit 5599c59d4a
62 changed files with 643 additions and 365 deletions

View File

@@ -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
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](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

View File

@@ -4,7 +4,7 @@ services:
build:
dockerfile: libs/langchain/dev.Dockerfile
context: ..
networks:
- langchain-network

View File

@@ -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

View File

@@ -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!')
```

View File

@@ -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

View File

@@ -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

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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(".")

View File

@@ -1,4 +1,5 @@
import sys
import tomllib
if __name__ == "__main__":

View File

@@ -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:

View File

@@ -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():

View File

@@ -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!")

View File

@@ -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'

View File

@@ -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'

View File

@@ -11,4 +11,4 @@
"MD046": {
"style": "fenced"
}
}
}

View File

@@ -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.

View File

@@ -35,6 +35,7 @@ embeddings.embed_query("What is the meaning of life?")
```
## LLMs
`__ModuleName__LLM` class exposes LLMs from __ModuleName__.
```python

View File

@@ -1,3 +1,3 @@
version: 0.0.1
patterns:
- name: github.com/getgrit/stdlib#*
- name: github.com/getgrit/stdlib#*

View File

@@ -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__")
```
```

View File

@@ -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

View File

@@ -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")

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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(

View File

@@ -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

View File

@@ -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.

View File

@@ -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):

View File

@@ -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.

View File

@@ -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("*"):

View File

@@ -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

View File

@@ -14,11 +14,10 @@
[![Dependency Status](https://img.shields.io/librariesio/github/langchain-ai/langchain)](https://libraries.io/github/langchain-ai/langchain)
[![Open Issues](https://img.shields.io/github/issues-raw/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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -14,11 +14,10 @@
[![Dependency Status](https://img.shields.io/librariesio/github/langchain-ai/langchain)](https://libraries.io/github/langchain-ai/langchain)
[![Open Issues](https://img.shields.io/github/issues-raw/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

View File

@@ -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 = ""

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.
"""

View File

@@ -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.
"""

View File

@@ -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:

View File

@@ -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"

View File

@@ -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)

View File

@@ -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")

View File

@@ -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]]

View File

@@ -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 %}

View File

@@ -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

View File

@@ -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

View File

@@ -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:**

View File

@@ -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
"""