Skip to content

Commit

Permalink
Merge pull request langchain-ai#10 from langchain-ai/wfh/use_invoke
Browse files Browse the repository at this point in the history
Wfh/use invoke
  • Loading branch information
hinthornw authored Aug 16, 2023
2 parents ecdbf29 + cb01812 commit a8c3776
Show file tree
Hide file tree
Showing 20 changed files with 4,050 additions and 1,652 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ yarn-error.log*

# ESLint
.eslintcache
__pycache__
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
build
.docusaurus
docs/api
85 changes: 85 additions & 0 deletions _scripts/extract_python_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import re
from pathlib import Path


def extract_code_blocks(mdx_file: str) -> list[str]:
"""
Extract all code blocks from an MDX file.
:param mdx_file: Path to the MDX file
:return: A list of code blocks
"""
with open(mdx_file, "r") as file:
content = file.read()
# Pattern to match code inside PythonBlock(\``) from code tabs
code_blocks = re.findall(r"PythonBlock\(`(.*?)`(?:,|\))", content, re.DOTALL)
# Assuming code blocks are enclosed between triple backticks
code_blocks += [
c.strip() for c in re.findall(r"```python\n(.*?)\n```", content, re.DOTALL)
]
code_blocks = [
code_block.replace("\\n", "\n").replace("\\\\", "\\")
for code_block in code_blocks
# Skip because we don't have the actual UUID to run in the test.
if "<run_id>" not in code_block
and "<your_project>" not in code_block
]
return code_blocks


def write_pytest_tests(
code_blocks: list[str], output_file: str, boilerplate: str = ""
) -> None:
"""
Write pytest unit tests to a Python file for each code block.
:param code_blocks: List of code blocks to test
:param output_file: Path to the output Python file
"""
with open(output_file, "w") as file:
file.write("import pytest\n")
file.write(boilerplate)
for index, code_block in enumerate(code_blocks):
indented_code = "\n".join(
[" " + line for line in code_block.split("\n")]
)
test_code = f"""
@pytest.mark.asyncio
async def test_code_block_{index}():
{indented_code}
"""
file.write(test_code)


def main(mdx_file: str, output_file: str, boilerplate: str = "") -> None:
"""
Main function to extract code blocks from MDX and write to pytest file.
:param mdx_file: Path to the MDX file
:param output_file: Path to the output Python file
"""
code_blocks = extract_code_blocks(mdx_file)
write_pytest_tests(code_blocks, output_file, boilerplate=boilerplate)
print(f"Tests written to {output_file}")


if __name__ == "__main__":
root = Path(__file__).parent.parent.absolute()
files = [
(
"tracing/tracing-faq.mdx",
"""from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate\n
chain = ChatPromptTemplate.from_messages([("human", "{query}")]) | ChatOpenAI()
""",
)
]
for file, boilerplate in files:
stem = Path(file).stem
dest_folder = root / "tests/py_unit_tests" / Path(file).parent
dest_folder.mkdir(parents=True, exist_ok=True)
main(
str(root / "docs" / file),
str(dest_folder / f"test_{stem}.py"),
boilerplate=boilerplate,
)
129 changes: 129 additions & 0 deletions _scripts/extract_ts_blocks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import re
from pathlib import Path
from typing import Tuple


def extract_code_blocks(mdx_file: str) -> list[str]:
"""
Extract all TypeScript code blocks from an MDX file.
:param mdx_file: Path to the MDX file
:return: A list of code blocks
"""
with open(mdx_file, "r") as file:
content = file.read()
# Pattern to match code inside TypeScriptBlock(\``) from code tabs
code_blocks = re.findall(
r"TypeScriptBlock\(`(.*?)`(?:,|\))", content, re.DOTALL
)
code_blocks = [
code_block.replace("\\n", "\n").replace("\\\\", "\\")
for code_block in code_blocks
if "<run_id>" not in code_block
and "<your_" not in code_block
]
return code_blocks


def transform_imports(code_block: str) -> Tuple[str, str]:
"""
Transform static import statements to dynamic imports in TypeScript.
:param code_block: The code block to transform
:return: The code block with transformed import statements
"""
lines = code_block.split("\n")
import_lines = []
other_lines = []
import_block = False
for line in lines:
if line.startswith("import "):
if "{" in line:
import_block = True
import_lines.append(line)
elif import_block:
import_lines[-1] += line + "\n"
else:
other_lines.append(line)
if "}" in line:
import_block = False
return "\n".join(import_lines), "\n".join(other_lines)


def add_boilerplate(code_block: str) -> str:
"""
Add boilerplate code to the code block.
:param code_block: The code block to transform
:return: The code block with boilerplate code
"""
if "chain.invoke" in code_block and "const chain = " not in code_block:
code_block = (
"""import { ChatOpenAI } from "langchain/chat_models/openai";
import {
ChatPromptTemplate,
HumanMessagePromptTemplate,
} from "langchain/prompts";
const chatPrompt = ChatPromptTemplate.fromPromptMessages([
HumanMessagePromptTemplate.fromTemplate("{query}"),
]);
const chain = chatPrompt.pipe(new ChatOpenAI());
"""
+ code_block
)
return code_block


def write_jest_tests(code_blocks: list[str], output_dir: str) -> None:
"""
Write Jest unit tests to a file for each TypeScript code block.
:param code_blocks: List of code blocks to test
:param output_file: Path to the output file
"""
for index, code_block in enumerate(code_blocks):
output_file = f"{output_dir}/test_code_block_{index}.test.ts"
with open(output_file, "w") as file:
code_block = add_boilerplate(code_block)
imports, transformed_code = transform_imports(code_block)
if imports:
file.write(imports + "\n")
indented_code = "\n".join(
[" " + line for line in transformed_code.strip().split("\n")]
)
test_code = f"""
test('test_code_block_{index}', async () => {{
{indented_code}
}});
"""
file.write(test_code)
print(f"Tests written to {output_file}")


def main(mdx_file: str, output_dir: str) -> None:
"""
Main function to extract TypeScript code blocks from MDX and write to Jest file.
:param mdx_file: Path to the MDX file
:param output_file: Path to the output file
"""
code_blocks = extract_code_blocks(mdx_file)
write_jest_tests(code_blocks, output_dir)


if __name__ == "__main__":
root = Path(__file__).parent.parent.absolute()
files = ["tracing/tracing-faq.mdx"]
for file in files:
stem = Path(file).stem
dest_folder = root / "tests/js_unit_tests" / Path(file).parent / stem
# Remove recursive all files in the folder
for f in dest_folder.glob("*"):
f.unlink()
dest_folder.mkdir(parents=True, exist_ok=True)
main(
str(root / "docs" / file),
str(dest_folder),
)
Loading

0 comments on commit a8c3776

Please sign in to comment.