mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
A script to add typescript translations. We can improve by automatically adding markdown tabs appropriately and parallelizing the llm calls
158 lines
5.6 KiB
Python
158 lines
5.6 KiB
Python
"""Add typescript translation to a given markdown file."""
|
|
|
|
import argparse
|
|
import re
|
|
|
|
import requests
|
|
from langchain_anthropic import ChatAnthropic
|
|
|
|
URL = "https://gist.githubusercontent.com/eyurtsev/e7486731415463a9bc5b4682358859c8/raw/b5a5fda9c7e3387cfcb781f25082814d43675d50/gistfile1.txt"
|
|
response = requests.get(URL)
|
|
response.raise_for_status()
|
|
reference_snippets = response.text
|
|
|
|
model = ChatAnthropic(model="claude-3-5-sonnet-latest")
|
|
|
|
|
|
def _get_tqdm():
|
|
try:
|
|
from tqdm import tqdm
|
|
except ImportError:
|
|
# If not available return a simple identity function
|
|
def tqdm(iterable, *args, **kwargs):
|
|
return iterable
|
|
|
|
return tqdm
|
|
|
|
|
|
_tqdm = _get_tqdm()
|
|
|
|
opening_pattern = re.compile(r"^\s*```python(?:\s+.*)?\s*$")
|
|
closing_pattern = re.compile(r"^\s*```\s*$")
|
|
|
|
|
|
def extract_python_snippets(markdown: str) -> list[str]:
|
|
"""
|
|
Extract all python code blocks (including their fence lines) from the markdown content.
|
|
A python block is defined as any block that starts with a line containing an opening fence
|
|
with '```python' (optionally with extra parameters) and ends with a closing fence '```'.
|
|
"""
|
|
snippets = []
|
|
inside_block = False
|
|
current_snippet = []
|
|
|
|
for line in markdown.splitlines(keepends=True):
|
|
if not inside_block:
|
|
if opening_pattern.match(line):
|
|
inside_block = True
|
|
current_snippet = [line]
|
|
else:
|
|
current_snippet.append(line)
|
|
if closing_pattern.match(line):
|
|
inside_block = False
|
|
snippets.append("".join(current_snippet))
|
|
current_snippet = []
|
|
return snippets
|
|
|
|
|
|
def translate_snippet(python_snippet: str) -> str:
|
|
"""Translate a python code block into a TypeScript code block using Langchain.
|
|
The response is expected to be a properly fenced TypeScript code block (i.e.
|
|
starting with ```typescript and ending with ```).
|
|
"""
|
|
ai_message = model.invoke(
|
|
[
|
|
{
|
|
"role": "system",
|
|
"content": (
|
|
f"You have access to the following up-to-date example TypeScript code "
|
|
f"snippets that show examples of building with langgraph "
|
|
f"and langchain:\n\n{reference_snippets}\n\n"
|
|
"Use this context to translate the following Python code to equivalent "
|
|
"TypeScript. Ensure that your output is a valid fenced TypeScript "
|
|
"code block (i.e. starts with ```typescript and ends with ```)."
|
|
),
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": f"Translate this Python snippet to TypeScript:\n\n{python_snippet}",
|
|
},
|
|
]
|
|
)
|
|
|
|
# Use a regular expression to search for a TypeScript code block in the response.
|
|
pattern = r"```typescript\s*(.*?)\s*```"
|
|
match = re.search(pattern, ai_message.content, re.DOTALL)
|
|
if match:
|
|
# Reconstruct the code block with proper fences.
|
|
typescript_code = match.group(1).strip()
|
|
return f"```typescript\n{typescript_code}\n```"
|
|
else:
|
|
raise ValueError("No TypeScript code block found in the model's response.")
|
|
|
|
|
|
def insert_translations_into_markdown(
|
|
markdown: str, typescript_snippets: list[str]
|
|
) -> str:
|
|
"""Walks through the original markdown content and, after each
|
|
Python snippet block, inserts the corresponding translated TypeScript snippet.
|
|
It assumes that the ordering of the Python snippets
|
|
(from extract_python_snippets) matches the order they appear in the markdown.
|
|
"""
|
|
output_lines = []
|
|
lines = markdown.splitlines(keepends=True)
|
|
inside_block = False
|
|
snippet_index = 0
|
|
|
|
for line in lines:
|
|
output_lines.append(line)
|
|
if not inside_block and opening_pattern.match(line):
|
|
# We've encountered the start of a python code block.
|
|
inside_block = True
|
|
elif inside_block:
|
|
if closing_pattern.match(line):
|
|
# End of a python snippet block.
|
|
inside_block = False
|
|
if snippet_index < len(typescript_snippets):
|
|
# Insert an extra newline for clarity, then the translated TypeScript snippet.
|
|
output_lines.append("\n")
|
|
output_lines.append(typescript_snippets[snippet_index])
|
|
output_lines.append("\n")
|
|
snippet_index += 1
|
|
return "".join(output_lines)
|
|
|
|
|
|
def main(file_path: str) -> None:
|
|
# Read the markdown file.
|
|
with open(file_path, "r") as f:
|
|
markdown_content = f.read()
|
|
|
|
# 1. Extract all Python snippets.
|
|
python_snippets = extract_python_snippets(markdown_content)[:1]
|
|
|
|
# 2. Translate each Python snippet to TypeScript.
|
|
typescript_snippets = []
|
|
# Replace with .batch() for faster translation
|
|
for python_snippet in _tqdm(python_snippets):
|
|
ts_snippet = translate_snippet(python_snippet)
|
|
typescript_snippets.append(ts_snippet)
|
|
|
|
# 3. Insert the TypeScript translations after their respective Python snippets.
|
|
updated_markdown = insert_translations_into_markdown(
|
|
markdown_content, typescript_snippets
|
|
)
|
|
|
|
# Overwrite the original markdown file with the updated content.
|
|
with open(file_path, "w") as f:
|
|
f.write(updated_markdown)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="Translate Python snippets in a markdown file to TypeScript and insert them after each Python snippet."
|
|
)
|
|
parser.add_argument("file_path", type=str, help="Path to the markdown file.")
|
|
args = parser.parse_args()
|
|
|
|
main(args.file_path)
|