Skip to content

Commit

Permalink
cli: create specific files from template (#28556)
Browse files Browse the repository at this point in the history
  • Loading branch information
efriis authored Dec 6, 2024
1 parent a197e0b commit 7ecf38f
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 54 deletions.
4 changes: 3 additions & 1 deletion libs/cli/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ _e2e_test:
$(PYTHON) -m pip install --upgrade poetry && \
$(PYTHON) -m pip install -e .. && \
$(PYTHON) -m langchain_cli.cli integration new --name parrot-link --name-class ParrotLink && \
$(PYTHON) -m langchain_cli.cli integration new --name parrot-link --name-class ParrotLinkB --src=integration_template/chat_models.py --dst=langchain-parrot-link/langchain_parrot_link/chat_models_b.py && \
$(PYTHON) -m langchain_cli.cli integration create-doc --name parrot-link --name-class ParrotLinkB --component-type ChatModel --destination-dir langchain-parrot-link/docs && \
cd langchain-parrot-link && \
poetry install --with lint,typing,test && \
poetry run pip install -e ../../../standard-tests && \
make format lint tests && \
poetry install --with test_integration && \
rm tests/integration_tests/test_vectorstores.py && \
make integration_test
make integration_test
132 changes: 79 additions & 53 deletions libs/cli/langchain_cli/namespaces/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ def new(
" This is used to name classes like `MyIntegrationVectorStore`"
),
] = None,
src: Annotated[
Optional[list[str]],
typer.Option(
help="The name of the single template file to copy."
" e.g. `--src integration_template/chat_models.py "
"--dst my_integration/chat_models.py`. Can be used multiple times.",
),
] = None,
dst: Annotated[
Optional[list[str]],
typer.Option(
help="The relative path to the integration package to place the new file in"
". e.g. `my-integration/my_integration.py`",
),
] = None,
):
"""
Creates a new integration package.
Expand All @@ -93,27 +108,66 @@ def new(
"Name of integration in PascalCase", default=replacements["__ModuleName__"]
)

project_template_dir = Path(__file__).parents[1] / "integration_template"
destination_dir = Path.cwd() / replacements["__package_name__"]
if destination_dir.exists():
typer.echo(f"Folder {destination_dir} exists.")
raise typer.Exit(code=1)
if not src and not dst:
if destination_dir.exists():
typer.echo(f"Folder {destination_dir} exists.")
raise typer.Exit(code=1)

# copy over template from ../integration_template
project_template_dir = Path(__file__).parents[1] / "integration_template"
shutil.copytree(project_template_dir, destination_dir, dirs_exist_ok=False)
# copy over template from ../integration_template
shutil.copytree(project_template_dir, destination_dir, dirs_exist_ok=False)

# folder movement
package_dir = destination_dir / replacements["__module_name__"]
shutil.move(destination_dir / "integration_template", package_dir)
# folder movement
package_dir = destination_dir / replacements["__module_name__"]
shutil.move(destination_dir / "integration_template", package_dir)

# replacements in files
replace_glob(destination_dir, "**/*", cast(Dict[str, str], replacements))
# replacements in files
replace_glob(destination_dir, "**/*", cast(Dict[str, str], replacements))

# poetry install
subprocess.run(
["poetry", "install", "--with", "lint,test,typing,test_integration"],
cwd=destination_dir,
)
# poetry install
subprocess.run(
["poetry", "install", "--with", "lint,test,typing,test_integration"],
cwd=destination_dir,
)
else:
# confirm src and dst are the same length
if not src:
typer.echo("Cannot provide --dst without --src.")
raise typer.Exit(code=1)
src_paths = [project_template_dir / p for p in src]
if dst and len(src) != len(dst):
typer.echo("Number of --src and --dst arguments must match.")
raise typer.Exit(code=1)
if not dst:
# assume we're in a package dir, copy to equivalent path
dst_paths = [destination_dir / p for p in src]
else:
dst_paths = [Path.cwd() / p for p in dst]
dst_paths = [
p / f"{replacements['__package_name_short_snake__']}.ipynb"
if not p.suffix
else p
for p in dst_paths
]

# confirm no duplicate dst_paths
if len(dst_paths) != len(set(dst_paths)):
typer.echo(
"Duplicate destination paths provided or computed - please "
"specify them explicitly with --dst."
)
raise typer.Exit(code=1)

# confirm no files exist at dst_paths
for dst_path in dst_paths:
if dst_path.exists():
typer.echo(f"File {dst_path} exists.")
raise typer.Exit(code=1)

for src_path, dst_path in zip(src_paths, dst_paths):
shutil.copy(src_path, dst_path)
replace_file(dst_path, cast(Dict[str, str], replacements))


TEMPLATE_MAP: dict[str, str] = {
Expand Down Expand Up @@ -176,43 +230,15 @@ def create_doc(
"""
Creates a new integration doc.
"""
try:
replacements = _process_name(name, community=component_type == "Tool")
except ValueError as e:
typer.echo(e)
raise typer.Exit(code=1)

if name_class:
if not re.match(r"^[A-Z][a-zA-Z0-9]*$", name_class):
typer.echo(
"Name should only contain letters (a-z, A-Z), numbers, and underscores"
", and start with a capital letter."
)
raise typer.Exit(code=1)
replacements["__ModuleName__"] = name_class
else:
replacements["__ModuleName__"] = typer.prompt(
(
"The PascalCase name of the integration (e.g. `OpenAI`, `VertexAI`). "
"Do not include a 'Chat', 'VectorStore', etc. prefix/suffix."
),
default=replacements["__ModuleName__"],
)
destination_path = (
Path.cwd()
/ destination_dir
/ (replacements["__package_name_short_snake__"] + ".ipynb")
)

# copy over template from ../integration_template
template_dir = Path(__file__).parents[1] / "integration_template" / "docs"
if component_type in TEMPLATE_MAP:
docs_template = template_dir / TEMPLATE_MAP[component_type]
else:
raise ValueError(
if component_type not in TEMPLATE_MAP:
typer.echo(
f"Unrecognized {component_type=}. Expected one of {_component_types_str}."
)
shutil.copy(docs_template, destination_path)
raise typer.Exit(code=1)

# replacements in file
replace_file(destination_path, cast(Dict[str, str], replacements))
new(
name=name,
name_class=name_class,
src=[f"docs/{TEMPLATE_MAP[component_type]}"],
dst=[destination_dir],
)

0 comments on commit 7ecf38f

Please sign in to comment.