From e23b7f4f098a4563dcc3379713ef46362de63484 Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:34:26 +0200 Subject: [PATCH 1/3] democratize providers --- scripts/generate_code_snippet_mdx.py | 193 ++++++- .../content-builder-create-agent-py.mdx | 132 ++++- .../deep-research-agent-claude-js.mdx | 300 +++++++++-- .../deep-research-agent-claude-py.mdx | 300 +++++++++-- ...g-term-memory-create-agent-inmemory-js.mdx | 112 +++- ...g-term-memory-create-agent-postgres-js.mdx | 132 ++++- ...long-term-memory-read-tool-inmemory-js.mdx | 500 ++++++++++++++++-- ...long-term-memory-read-tool-inmemory-py.mdx | 436 +++++++++++++-- ...long-term-memory-read-tool-postgres-js.mdx | 348 ++++++++++-- ...ong-term-memory-write-tool-inmemory-js.mdx | 444 ++++++++++++++-- ...ong-term-memory-write-tool-inmemory-py.mdx | 420 +++++++++++++-- ...ong-term-memory-write-tool-postgres-js.mdx | 340 ++++++++++-- .../middleware-dynamic-prompt-js.mdx | 156 +++++- .../streaming-reasoning-tokens-js.mdx | 314 +++++++++-- 14 files changed, 3680 insertions(+), 447 deletions(-) diff --git a/scripts/generate_code_snippet_mdx.py b/scripts/generate_code_snippet_mdx.py index 97a52f67bb..75850b9a27 100644 --- a/scripts/generate_code_snippet_mdx.py +++ b/scripts/generate_code_snippet_mdx.py @@ -3,11 +3,187 @@ Reads .snippet.*.py and .snippet.*.ts files from src/code-samples-generated/ and creates corresponding MDX files in src/snippets/code-samples/ for use in docs. +When a snippet uses a LangChain-style model argument (`model="…"` in Python or +`model: "…"` in TypeScript), the generated MDX can be wrapped in with the same +seven provider/model options as /oss/deepagents/quickstart (Google, OpenAI, Anthropic, +OpenRouter, Fireworks, Baseten, Ollama). Both `provider:model-id` and bare model names +(for example `claude-sonnet-4-5-20250929`) are recognized. + +Snippets are left as a single fenced block when the model is already a non-Google +quickstart tab line, the bare name for one of those tabs (for example `claude-sonnet-4-6`), +or other known non-Deep-Agents cases (for example `gemini-2.5-flash-image` for the +Google Genai client API). The first model= / model: in the file that is not skipped +is the one that triggers expansion. + Run as part of `make code-snippets` after Bluehawk extraction. """ +from __future__ import annotations + +import re from pathlib import Path +# Python: keyword argument model="…" (init_chat_model / create_deep_agent / etc.). +DEEPAGENTS_PY_MODEL_KWARG_RE = re.compile(r'\bmodel\s*=\s*"([^"]+)"') + +# TypeScript: object property model: "…" (ChatAnthropic, createDeepAgent, …). +DEEPAGENTS_TS_MODEL_KWARG_RE = re.compile(r'\bmodel\s*:\s*"([^"]+)"') + +# Tab title and full `model=` / `model:` token for each variant (matches +# src/oss/deepagents/quickstart.mdx Python tabs; JS uses google-genai spelling). +DEEPAGENTS_QUICKSTART_PY_MODEL_TABS: list[tuple[str, str]] = [ + ("Google", 'model="google_genai:gemini-3.1-pro-preview"'), + ("OpenAI", 'model="openai:gpt-5.4"'), + ("Anthropic", 'model="anthropic:claude-sonnet-4-6"'), + ("OpenRouter", 'model="openrouter:anthropic/claude-sonnet-4-6"'), + ("Fireworks", 'model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b"'), + ("Baseten", 'model="baseten:zai-org/GLM-5"'), + ("Ollama", 'model="ollama:devstral-2"'), +] + +DEEPAGENTS_QUICKSTART_TS_MODEL_TABS: list[tuple[str, str]] = [ + ("Google", 'model: "google-genai:gemini-3.1-pro-preview"'), + ("OpenAI", 'model: "openai:gpt-5.4"'), + ("Anthropic", 'model: "anthropic:claude-sonnet-4-6"'), + ("OpenRouter", 'model: "openrouter:anthropic/claude-sonnet-4-6"'), + ("Fireworks", 'model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b"'), + ("Baseten", 'model: "baseten:zai-org/GLM-5"'), + ("Ollama", 'model: "ollama:devstral-2"'), +] + + +def _model_id_from_py_tab_token(tab_token: str) -> str: + m = re.match(r'model="([^"]+)"', tab_token) + if not m: + msg = f"expected model= tab token, got {tab_token!r}" + raise ValueError(msg) + return m.group(1) + + +def _model_id_from_ts_tab_token(tab_token: str) -> str: + m = re.match(r'model:\s*"([^"]+)"', tab_token) + if not m: + msg = f"expected model: tab token, got {tab_token!r}" + raise ValueError(msg) + return m.group(1) + + +# If the snippet already uses one of these model IDs (non-Google quickstart tab), +# do not expand: the author chose that tab line verbatim. +DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( + _model_id_from_py_tab_token(token) + for title, token in DEEPAGENTS_QUICKSTART_PY_MODEL_TABS + if title != "Google" +) + +DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( + _model_id_from_ts_tab_token(token) + for title, token in DEEPAGENTS_QUICKSTART_TS_MODEL_TABS + if title != "Google" +) + + +def _id_after_first_colon(tab_id: str) -> str: + """For openai:gpt-5.4 return gpt-5.4; for bare ids return as-is.""" + if ":" not in tab_id: + return tab_id + return tab_id.split(":", 1)[1] + + +# Bare names that are not Deep Agents init lines (google.genai client, legacy tutorials). +DEEPAGENTS_PY_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( + { + "gemini-2.5-flash-image", + "claude-3-haiku-20240307", + } +) +DEEPAGENTS_TS_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( + { + "gemini-2.5-flash-image", + "claude-3-haiku-20240307", + } +) + + +def _should_skip_expand_py(model_id: str) -> bool: + if ":" in model_id: + return model_id in DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS + return model_id in DEEPAGENTS_PY_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS + + +def _should_skip_expand_ts(model_id: str) -> bool: + if ":" in model_id: + return model_id in DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS + return model_id in DEEPAGENTS_TS_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS + + +def _codegroup_fence(tab_title: str, fence_lang: str, code: str) -> str: + """One fenced code block inside a (indent matches docs conventions).""" + body = "\n".join(" " + line for line in code.splitlines()) + return "\n".join( + [ + f" ```{fence_lang} {tab_title}", + body, + " ```", + ] + ) + + +def _expand_to_deepagents_codegroup( + content: str, + *, + canonical: str, + tab_definitions: list[tuple[str, str]], + fence_lang: str, +) -> str: + """Wrap `content` in a CodeGroup, one tab per quickstart model variant.""" + parts = [ + _codegroup_fence(title, fence_lang, content.replace(canonical, model_token)) + for title, model_token in tab_definitions + ] + return "\n" + "\n\n".join(parts) + "\n\n" + + +def maybe_expand_deepagents_quickstart_codegroup( + content: str, + *, + language: str, + fence_lang: str, +) -> str | None: + """If content uses a quickstart-expandable model= line, return CodeGroup MDX.""" + if language == "python": + for m in DEEPAGENTS_PY_MODEL_KWARG_RE.finditer(content): + if not _should_skip_expand_py(m.group(1)): + return _expand_to_deepagents_codegroup( + content, + canonical=m.group(0), + tab_definitions=DEEPAGENTS_QUICKSTART_PY_MODEL_TABS, + fence_lang=fence_lang, + ) + return None + if language == "ts": + for m in DEEPAGENTS_TS_MODEL_KWARG_RE.finditer(content): + if not _should_skip_expand_ts(m.group(1)): + return _expand_to_deepagents_codegroup( + content, + canonical=m.group(0), + tab_definitions=DEEPAGENTS_QUICKSTART_TS_MODEL_TABS, + fence_lang=fence_lang, + ) + return None + return None + + +def format_snippet_mdx(content: str, *, language: str, fence_lang: str) -> str: + """Return final MDX body for a snippet file.""" + content = content.rstrip() + "\n" + expanded = maybe_expand_deepagents_quickstart_codegroup( + content, language=language, fence_lang=fence_lang + ) + if expanded is not None: + return expanded + return f"```{fence_lang}\n{content.rstrip()}\n```\n" + def main() -> None: repo_root = Path(__file__).resolve().parent.parent @@ -19,29 +195,24 @@ def main() -> None: snippets_dir.mkdir(parents=True, exist_ok=True) - # Mapping: (glob_pattern, language) for each snippet type snippet_configs = [ - ("*.snippet.*.py", "python"), - ("*.snippet.*.ts", "ts"), + ("*.snippet.*.py", "python", "python"), + ("*.snippet.*.ts", "ts", "ts"), ] - # Only process snippets that already have language suffix to - # avoid Bluehawk duplicates lang_suffix = {"python": "-py", "ts": "-js"} - for glob_pattern, language in snippet_configs: + for glob_pattern, language, fence_lang in snippet_configs: for snippet_file in generated_dir.glob(glob_pattern): snippet_name = ".".join(snippet_file.stem.split(".")[2:]) expected_suffix = lang_suffix[language] - # Only process language-specific snippets - # (tool-return-object-py, not tool-return-object) to avoid - # duplicates when Bluehawk emits both suffixed and unsuffixed versions if not snippet_name.endswith(expected_suffix): continue content = snippet_file.read_text(encoding="utf-8") - # Create MDX with fenced code block - mdx_content = f"```{language}\n{content.rstrip()}\n```\n" + mdx_content = format_snippet_mdx( + content, language=language, fence_lang=fence_lang + ) mdx_path = snippets_dir / f"{snippet_name}.mdx" mdx_path.write_text(mdx_content, encoding="utf-8") print(f"Generated {mdx_path.relative_to(repo_root)}") diff --git a/src/snippets/code-samples/content-builder-create-agent-py.mdx b/src/snippets/code-samples/content-builder-create-agent-py.mdx index 3dc0b7634c..9113d0cd3b 100644 --- a/src/snippets/code-samples/content-builder-create-agent-py.mdx +++ b/src/snippets/code-samples/content-builder-create-agent-py.mdx @@ -1,16 +1,120 @@ -```python -from deepagents import create_deep_agent -from deepagents.backends import FilesystemBackend + + ```python Google + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="google_genai:gemini-3.1-pro-preview", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + ```python OpenAI + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="openai:gpt-5.4", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` -def create_content_writer(): - """Create a content writer agent configured by filesystem files.""" - return create_deep_agent( - model="google_genai:gemini-3.1-pro-preview", - memory=["./AGENTS.md"], - skills=["./skills/"], - tools=[generate_cover, generate_social_image], - subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), - backend=FilesystemBackend(root_dir=EXAMPLE_DIR), - ) -``` + ```python Anthropic + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="anthropic:claude-sonnet-4-6", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + + ```python OpenRouter + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="openrouter:anthropic/claude-sonnet-4-6", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + + ```python Fireworks + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + + ```python Baseten + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="baseten:zai-org/GLM-5", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + + ```python Ollama + from deepagents import create_deep_agent + from deepagents.backends import FilesystemBackend + + + def create_content_writer(): + """Create a content writer agent configured by filesystem files.""" + return create_deep_agent( + model="ollama:devstral-2", + memory=["./AGENTS.md"], + skills=["./skills/"], + tools=[generate_cover, generate_social_image], + subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"), + backend=FilesystemBackend(root_dir=EXAMPLE_DIR), + ) + ``` + diff --git a/src/snippets/code-samples/deep-research-agent-claude-js.mdx b/src/snippets/code-samples/deep-research-agent-claude-js.mdx index 22f4819d38..2f51820de8 100644 --- a/src/snippets/code-samples/deep-research-agent-claude-js.mdx +++ b/src/snippets/code-samples/deep-research-agent-claude-js.mdx @@ -1,38 +1,274 @@ -```ts -import { createDeepAgent } from "deepagents"; -import { ChatAnthropic } from "@langchain/anthropic"; + + ```ts Google + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "google-genai:gemini-3.1-pro-preview", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const maxConcurrentResearchUnits = 3; -const maxResearcherIterations = 3; + ```ts OpenAI + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "openai:gpt-5.4", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const currentDate = new Date().toISOString().split("T")[0]; + ```ts Anthropic + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "anthropic:claude-sonnet-4-6", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const INSTRUCTIONS = - RESEARCH_WORKFLOW_INSTRUCTIONS + - "\n\n" + - "=".repeat(80) + - "\n\n" + - SUBAGENT_DELEGATION_INSTRUCTIONS.replace( - "{maxConcurrentResearchUnits}", - String(maxConcurrentResearchUnits), - ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + ```ts OpenRouter + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "openrouter:anthropic/claude-sonnet-4-6", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const researchSubAgent = { - name: "research-agent", - description: "Delegate research to the sub-agent. Give one topic at a time.", - systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), - tools: [tavilySearch], -}; + ```ts Fireworks + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const model = new ChatAnthropic({ - model: "claude-sonnet-4-5-20250929", - temperature: 0, -}); + ```ts Baseten + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "baseten:zai-org/GLM-5", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` -const agent = createDeepAgent({ - model, - tools: [tavilySearch], - systemPrompt: INSTRUCTIONS, - subagents: [researchSubAgent], -}); -``` + ```ts Ollama + import { createDeepAgent } from "deepagents"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const maxConcurrentResearchUnits = 3; + const maxResearcherIterations = 3; + + const currentDate = new Date().toISOString().split("T")[0]; + + const INSTRUCTIONS = + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=".repeat(80) + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.replace( + "{maxConcurrentResearchUnits}", + String(maxConcurrentResearchUnits), + ).replace("{maxResearcherIterations}", String(maxResearcherIterations)); + + const researchSubAgent = { + name: "research-agent", + description: "Delegate research to the sub-agent. Give one topic at a time.", + systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate), + tools: [tavilySearch], + }; + + const model = new ChatAnthropic({ + model: "ollama:devstral-2", + temperature: 0, + }); + + const agent = createDeepAgent({ + model, + tools: [tavilySearch], + systemPrompt: INSTRUCTIONS, + subagents: [researchSubAgent], + }); + ``` + diff --git a/src/snippets/code-samples/deep-research-agent-claude-py.mdx b/src/snippets/code-samples/deep-research-agent-claude-py.mdx index 34527920c6..98199ecc9e 100644 --- a/src/snippets/code-samples/deep-research-agent-claude-py.mdx +++ b/src/snippets/code-samples/deep-research-agent-claude-py.mdx @@ -1,38 +1,274 @@ -```python -from datetime import datetime - -from deepagents import create_deep_agent -from langchain.chat_models import init_chat_model + + ```python Google + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="google_genai:gemini-3.1-pro-preview", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -max_concurrent_research_units = 3 -max_researcher_iterations = 3 + ```python OpenAI + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="openai:gpt-5.4", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -current_date = datetime.now().strftime("%Y-%m-%d") + ```python Anthropic + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="anthropic:claude-sonnet-4-6", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -INSTRUCTIONS = ( - RESEARCH_WORKFLOW_INSTRUCTIONS - + "\n\n" - + "=" * 80 - + "\n\n" - + SUBAGENT_DELEGATION_INSTRUCTIONS.format( - max_concurrent_research_units=max_concurrent_research_units, - max_researcher_iterations=max_researcher_iterations, - ) -) + ```python OpenRouter + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="openrouter:anthropic/claude-sonnet-4-6", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -research_sub_agent = { - "name": "research-agent", - "description": "Delegate research to the sub-agent. Give one topic at a time.", - "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), - "tools": [tavily_search], -} + ```python Fireworks + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -model = init_chat_model(model="anthropic:claude-sonnet-4-5-20250929", temperature=0.0) + ```python Baseten + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="baseten:zai-org/GLM-5", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` -agent = create_deep_agent( - model=model, - tools=[tavily_search], - system_prompt=INSTRUCTIONS, - subagents=[research_sub_agent], -) -``` + ```python Ollama + from datetime import datetime + + from deepagents import create_deep_agent + from langchain.chat_models import init_chat_model + + max_concurrent_research_units = 3 + max_researcher_iterations = 3 + + current_date = datetime.now().strftime("%Y-%m-%d") + + INSTRUCTIONS = ( + RESEARCH_WORKFLOW_INSTRUCTIONS + + "\n\n" + + "=" * 80 + + "\n\n" + + SUBAGENT_DELEGATION_INSTRUCTIONS.format( + max_concurrent_research_units=max_concurrent_research_units, + max_researcher_iterations=max_researcher_iterations, + ) + ) + + research_sub_agent = { + "name": "research-agent", + "description": "Delegate research to the sub-agent. Give one topic at a time.", + "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date), + "tools": [tavily_search], + } + + model = init_chat_model(model="ollama:devstral-2", temperature=0.0) + + agent = create_deep_agent( + model=model, + tools=[tavily_search], + system_prompt=INSTRUCTIONS, + subagents=[research_sub_agent], + ) + ``` + diff --git a/src/snippets/code-samples/long-term-memory-create-agent-inmemory-js.mdx b/src/snippets/code-samples/long-term-memory-create-agent-inmemory-js.mdx index 81584ae0c5..afbff85d7c 100644 --- a/src/snippets/code-samples/long-term-memory-create-agent-inmemory-js.mdx +++ b/src/snippets/code-samples/long-term-memory-create-agent-inmemory-js.mdx @@ -1,13 +1,99 @@ -```ts -import { createAgent } from "langchain"; -import { InMemoryStore } from "@langchain/langgraph"; - -// InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. -const store = new InMemoryStore(); - -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [], - store, -}); -``` + + ```ts Google + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [], + store, + }); + ``` + + ```ts OpenAI + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [], + store, + }); + ``` + + ```ts Anthropic + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [], + store, + }); + ``` + + ```ts OpenRouter + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [], + store, + }); + ``` + + ```ts Fireworks + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [], + store, + }); + ``` + + ```ts Baseten + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [], + store, + }); + ``` + + ```ts Ollama + import { createAgent } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use. + const store = new InMemoryStore(); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [], + store, + }); + ``` + diff --git a/src/snippets/code-samples/long-term-memory-create-agent-postgres-js.mdx b/src/snippets/code-samples/long-term-memory-create-agent-postgres-js.mdx index 9d4e5d9076..045b921df6 100644 --- a/src/snippets/code-samples/long-term-memory-create-agent-postgres-js.mdx +++ b/src/snippets/code-samples/long-term-memory-create-agent-postgres-js.mdx @@ -1,16 +1,120 @@ -```ts -import { createAgent } from "langchain"; -import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + ```ts Google + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [], + store, + }); + ``` -const DB_URI = - process.env.POSTGRES_URI ?? - "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; -const store = PostgresStore.fromConnString(DB_URI); -await store.setup(); + ```ts OpenAI + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [], + store, + }); + ``` -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [], - store, -}); -``` + ```ts Anthropic + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [], + store, + }); + ``` + + ```ts OpenRouter + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [], + store, + }); + ``` + + ```ts Fireworks + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [], + store, + }); + ``` + + ```ts Baseten + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [], + store, + }); + ``` + + ```ts Ollama + import { createAgent } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [], + store, + }); + ``` + diff --git a/src/snippets/code-samples/long-term-memory-read-tool-inmemory-js.mdx b/src/snippets/code-samples/long-term-memory-read-tool-inmemory-js.mdx index ebb06d5770..9e4940021e 100644 --- a/src/snippets/code-samples/long-term-memory-read-tool-inmemory-js.mdx +++ b/src/snippets/code-samples/long-term-memory-read-tool-inmemory-js.mdx @@ -1,63 +1,449 @@ -```ts -import * as z from "zod"; -import { createAgent, tool, type ToolRuntime } from "langchain"; -import { InMemoryStore } from "@langchain/langgraph"; + + ```ts Google + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -// InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. -const store = new InMemoryStore(); -const contextSchema = z.object({ - userId: z.string(), -}); + ```ts OpenAI + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -// Write sample data to the store using the put method -await store.put( - ["users"], // Namespace to group related data together (users namespace for user data) - "user_123", // Key within the namespace (user ID as key) - { - name: "John Smith", - language: "English", - }, // Data to store for the given user -); + ```ts Anthropic + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -const getUserInfo = tool( - // Look up user info. - async (_, runtime: ToolRuntime>) => { - // Access the store - same as that provided to `createAgent` - const userId = runtime.context.userId; - if (!userId) { - throw new Error("userId is required"); - } - // Retrieve data from store - returns StoreValue object with value and metadata - const userInfo = await runtime.store.get(["users"], userId); - return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; - }, - { - name: "getUserInfo", - description: "Look up user info by userId from the store.", - schema: z.object({}), - }, -); + ```ts OpenRouter + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [getUserInfo], - contextSchema, - // Pass store to agent - enables agent to access store when running tools - store, -}); + ```ts Fireworks + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -// Run the agent -const result = await agent.invoke( - { messages: [{ role: "user", content: "look up user information" }] }, - { context: { userId: "user_123" } }, -); + ```ts Baseten + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` -console.log(result.messages.at(-1)?.content); - -/** - * Outputs: - * User Information: - * - **Name:** John Smith - * - **Language:** English - */ -``` + ```ts Ollama + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + const contextSchema = z.object({ + userId: z.string(), + }); + + // Write sample data to the store using the put method + await store.put( + ["users"], // Namespace to group related data together (users namespace for user data) + "user_123", // Key within the namespace (user ID as key) + { + name: "John Smith", + language: "English", + }, // Data to store for the given user + ); + + const getUserInfo = tool( + // Look up user info. + async (_, runtime: ToolRuntime>) => { + // Access the store - same as that provided to `createAgent` + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Retrieve data from store - returns StoreValue object with value and metadata + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [getUserInfo], + contextSchema, + // Pass store to agent - enables agent to access store when running tools + store, + }); + + // Run the agent + const result = await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + + console.log(result.messages.at(-1)?.content); + + /** + * Outputs: + * User Information: + * - **Name:** John Smith + * - **Language:** English + */ + ``` + diff --git a/src/snippets/code-samples/long-term-memory-read-tool-inmemory-py.mdx b/src/snippets/code-samples/long-term-memory-read-tool-inmemory-py.mdx index f4b1c19d64..a7f19c28b6 100644 --- a/src/snippets/code-samples/long-term-memory-read-tool-inmemory-py.mdx +++ b/src/snippets/code-samples/long-term-memory-read-tool-inmemory-py.mdx @@ -1,55 +1,393 @@ -```python -from dataclasses import dataclass + + ```python Google + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="google_genai:gemini-3.1-pro-preview", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` -from langchain.agents import create_agent -from langchain.tools import ToolRuntime, tool -from langchain_core.runnables import Runnable -from langgraph.store.memory import InMemoryStore + ```python OpenAI + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="openai:gpt-5.4", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` + ```python Anthropic + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="anthropic:claude-sonnet-4-6", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` -@dataclass -class Context: - user_id: str + ```python OpenRouter + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="openrouter:anthropic/claude-sonnet-4-6", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` + ```python Fireworks + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` -# InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. -store = InMemoryStore() + ```python Baseten + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="baseten:zai-org/GLM-5", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` -# Write sample data to the store using the put method -store.put( - ( - "users", - ), # Namespace to group related data together (users namespace for user data) - "user_123", # Key within the namespace (user ID as key) - { - "name": "John Smith", - "language": "English", - }, # Data to store for the given user -) - - -@tool -def get_user_info(runtime: ToolRuntime[Context]) -> str: - """Look up user info.""" - # Access the store - same as that provided to `create_agent` - assert runtime.store is not None - user_id = runtime.context.user_id - # Retrieve data from store - returns StoreValue object with value and metadata - user_info = runtime.store.get(("users",), user_id) - return str(user_info.value) if user_info else "Unknown user" - - -agent: Runnable = create_agent( - model="claude-sonnet-4-6", - tools=[get_user_info], - # Pass store to agent - enables agent to access store when running tools - store=store, - context_schema=Context, -) - -# Run the agent -agent.invoke( - {"messages": [{"role": "user", "content": "look up user information"}]}, - context=Context(user_id="user_123"), -) -``` + ```python Ollama + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + + + @dataclass + class Context: + user_id: str + + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + # Write sample data to the store using the put method + store.put( + ( + "users", + ), # Namespace to group related data together (users namespace for user data) + "user_123", # Key within the namespace (user ID as key) + { + "name": "John Smith", + "language": "English", + }, # Data to store for the given user + ) + + + @tool + def get_user_info(runtime: ToolRuntime[Context]) -> str: + """Look up user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + user_id = runtime.context.user_id + # Retrieve data from store - returns StoreValue object with value and metadata + user_info = runtime.store.get(("users",), user_id) + return str(user_info.value) if user_info else "Unknown user" + + + agent: Runnable = create_agent( + model="ollama:devstral-2", + tools=[get_user_info], + # Pass store to agent - enables agent to access store when running tools + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "look up user information"}]}, + context=Context(user_id="user_123"), + ) + ``` + diff --git a/src/snippets/code-samples/long-term-memory-read-tool-postgres-js.mdx b/src/snippets/code-samples/long-term-memory-read-tool-postgres-js.mdx index 85cbd8cb43..8d31e0c46b 100644 --- a/src/snippets/code-samples/long-term-memory-read-tool-postgres-js.mdx +++ b/src/snippets/code-samples/long-term-memory-read-tool-postgres-js.mdx @@ -1,44 +1,316 @@ -```ts -import * as z from "zod"; -import { createAgent, tool, type ToolRuntime } from "langchain"; -import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + ```ts Google + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -const DB_URI = - process.env.POSTGRES_URI ?? - "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; -const store = PostgresStore.fromConnString(DB_URI); -await store.setup(); + ```ts OpenAI + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -const contextSchema = z.object({ userId: z.string() }); + ```ts Anthropic + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -await store.put(["users"], "user_123", { - name: "John Smith", - language: "English", -}); + ```ts OpenRouter + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -const getUserInfo = tool( - async (_, runtime: ToolRuntime>) => { - const userId = runtime.context.userId; - if (!userId) throw new Error("userId is required"); - const userInfo = await runtime.store.get(["users"], userId); - return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; - }, - { - name: "getUserInfo", - description: "Look up user info by userId from the store.", - schema: z.object({}), - }, -); + ```ts Fireworks + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [getUserInfo], - contextSchema, - store, -}); + ```ts Baseten + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` -await agent.invoke( - { messages: [{ role: "user", content: "look up user information" }] }, - { context: { userId: "user_123" } }, -); -``` + ```ts Ollama + import * as z from "zod"; + import { createAgent, tool, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + await store.put(["users"], "user_123", { + name: "John Smith", + language: "English", + }); + + const getUserInfo = tool( + async (_, runtime: ToolRuntime>) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + const userInfo = await runtime.store.get(["users"], userId); + return userInfo?.value ? JSON.stringify(userInfo.value) : "Unknown user"; + }, + { + name: "getUserInfo", + description: "Look up user info by userId from the store.", + schema: z.object({}), + }, + ); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [getUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "look up user information" }] }, + { context: { userId: "user_123" } }, + ); + ``` + diff --git a/src/snippets/code-samples/long-term-memory-write-tool-inmemory-js.mdx b/src/snippets/code-samples/long-term-memory-write-tool-inmemory-js.mdx index 5dcbc72924..6d7f00b51f 100644 --- a/src/snippets/code-samples/long-term-memory-write-tool-inmemory-js.mdx +++ b/src/snippets/code-samples/long-term-memory-write-tool-inmemory-js.mdx @@ -1,56 +1,400 @@ -```ts -import * as z from "zod"; -import { tool, createAgent, type ToolRuntime } from "langchain"; -import { InMemoryStore } from "@langchain/langgraph"; + + ```ts Google + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -// InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. -const store = new InMemoryStore(); + ```ts OpenAI + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -const contextSchema = z.object({ - userId: z.string(), -}); + ```ts Anthropic + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -// Schema defines the structure of user information for the LLM -const UserInfo = z.object({ - name: z.string(), -}); + ```ts OpenRouter + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -// Tool that allows agent to update user information (useful for chat applications) -const saveUserInfo = tool( - async ( - userInfo: z.infer, - runtime: ToolRuntime>, - ) => { - const userId = runtime.context.userId; - if (!userId) { - throw new Error("userId is required"); - } - // Store data in the store (namespace, key, data) - await runtime.store.put(["users"], userId, userInfo); - return "Successfully saved user info."; - }, - { - name: "save_user_info", - description: "Save user info", - schema: UserInfo, - }, -); + ```ts Fireworks + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [saveUserInfo], - contextSchema, - store, -}); + ```ts Baseten + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` -// Run the agent -await agent.invoke( - { messages: [{ role: "user", content: "My name is John Smith" }] }, - // userId passed in context to identify whose information is being updated - { context: { userId: "user_123" } }, -); - -// You can access the store directly to get the value -const result = await store.get(["users"], "user_123"); -console.log(result?.value); // Output: { name: "John Smith" } -``` + ```ts Ollama + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { InMemoryStore } from "@langchain/langgraph"; + + // InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + const store = new InMemoryStore(); + + const contextSchema = z.object({ + userId: z.string(), + }); + + // Schema defines the structure of user information for the LLM + const UserInfo = z.object({ + name: z.string(), + }); + + // Tool that allows agent to update user information (useful for chat applications) + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) { + throw new Error("userId is required"); + } + // Store data in the store (namespace, key, data) + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { + name: "save_user_info", + description: "Save user info", + schema: UserInfo, + }, + ); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [saveUserInfo], + contextSchema, + store, + }); + + // Run the agent + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + // userId passed in context to identify whose information is being updated + { context: { userId: "user_123" } }, + ); + + // You can access the store directly to get the value + const result = await store.get(["users"], "user_123"); + console.log(result?.value); // Output: { name: "John Smith" } + ``` + diff --git a/src/snippets/code-samples/long-term-memory-write-tool-inmemory-py.mdx b/src/snippets/code-samples/long-term-memory-write-tool-inmemory-py.mdx index 5c8923f0ef..ba45398595 100644 --- a/src/snippets/code-samples/long-term-memory-write-tool-inmemory-py.mdx +++ b/src/snippets/code-samples/long-term-memory-write-tool-inmemory-py.mdx @@ -1,53 +1,379 @@ -```python -from dataclasses import dataclass + + ```python Google + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="google_genai:gemini-3.1-pro-preview", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` -from langchain.agents import create_agent -from langchain.tools import ToolRuntime, tool -from langchain_core.runnables import Runnable -from langgraph.store.memory import InMemoryStore -from typing_extensions import TypedDict + ```python OpenAI + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="openai:gpt-5.4", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` -# InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. -store = InMemoryStore() + ```python Anthropic + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="anthropic:claude-sonnet-4-6", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` + ```python OpenRouter + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="openrouter:anthropic/claude-sonnet-4-6", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` -@dataclass -class Context: - user_id: str + ```python Fireworks + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` + ```python Baseten + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="baseten:zai-org/GLM-5", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` -# TypedDict defines the structure of user information for the LLM -class UserInfo(TypedDict): - name: str - - -# Tool that allows agent to update user information (useful for chat applications) -@tool -def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: - """Save user info.""" - # Access the store - same as that provided to `create_agent` - assert runtime.store is not None - store = runtime.store - user_id = runtime.context.user_id - # Store data in the store (namespace, key, data) - store.put(("users",), user_id, dict(user_info)) - return "Successfully saved user info." - - -agent: Runnable = create_agent( - model="claude-sonnet-4-6", - tools=[save_user_info], - store=store, - context_schema=Context, -) - -# Run the agent -agent.invoke( - {"messages": [{"role": "user", "content": "My name is John Smith"}]}, - # user_id passed in context to identify whose information is being updated - context=Context(user_id="user_123"), -) - -# You can access the store directly to get the value -item = store.get(("users",), "user_123") -``` + ```python Ollama + from dataclasses import dataclass + + from langchain.agents import create_agent + from langchain.tools import ToolRuntime, tool + from langchain_core.runnables import Runnable + from langgraph.store.memory import InMemoryStore + from typing_extensions import TypedDict + + # InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production. + store = InMemoryStore() + + + @dataclass + class Context: + user_id: str + + + # TypedDict defines the structure of user information for the LLM + class UserInfo(TypedDict): + name: str + + + # Tool that allows agent to update user information (useful for chat applications) + @tool + def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str: + """Save user info.""" + # Access the store - same as that provided to `create_agent` + assert runtime.store is not None + store = runtime.store + user_id = runtime.context.user_id + # Store data in the store (namespace, key, data) + store.put(("users",), user_id, dict(user_info)) + return "Successfully saved user info." + + + agent: Runnable = create_agent( + model="ollama:devstral-2", + tools=[save_user_info], + store=store, + context_schema=Context, + ) + + # Run the agent + agent.invoke( + {"messages": [{"role": "user", "content": "My name is John Smith"}]}, + # user_id passed in context to identify whose information is being updated + context=Context(user_id="user_123"), + ) + + # You can access the store directly to get the value + item = store.get(("users",), "user_123") + ``` + diff --git a/src/snippets/code-samples/long-term-memory-write-tool-postgres-js.mdx b/src/snippets/code-samples/long-term-memory-write-tool-postgres-js.mdx index 1fe37b838a..147edd9c1e 100644 --- a/src/snippets/code-samples/long-term-memory-write-tool-postgres-js.mdx +++ b/src/snippets/code-samples/long-term-memory-write-tool-postgres-js.mdx @@ -1,43 +1,309 @@ -```ts -import * as z from "zod"; -import { tool, createAgent, type ToolRuntime } from "langchain"; -import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + ```ts Google + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -const DB_URI = - process.env.POSTGRES_URI ?? - "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; -const store = PostgresStore.fromConnString(DB_URI); -await store.setup(); + ```ts OpenAI + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "openai:gpt-5.4", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -const contextSchema = z.object({ userId: z.string() }); + ```ts Anthropic + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -const UserInfo = z.object({ name: z.string() }); + ```ts OpenRouter + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -const saveUserInfo = tool( - async ( - userInfo: z.infer, - runtime: ToolRuntime>, - ) => { - const userId = runtime.context.userId; - if (!userId) throw new Error("userId is required"); - await runtime.store.put(["users"], userId, userInfo); - return "Successfully saved user info."; - }, - { name: "save_user_info", description: "Save user info", schema: UserInfo }, -); + ```ts Fireworks + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -const agent = createAgent({ - model: "claude-sonnet-4-6", - tools: [saveUserInfo], - contextSchema, - store, -}); + ```ts Baseten + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` -await agent.invoke( - { messages: [{ role: "user", content: "My name is John Smith" }] }, - { context: { userId: "user_123" } }, -); - -const result = await store.get(["users"], "user_123"); -console.log(result?.value); -``` + ```ts Ollama + import * as z from "zod"; + import { tool, createAgent, type ToolRuntime } from "langchain"; + import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store"; + + const DB_URI = + process.env.POSTGRES_URI ?? + "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"; + const store = PostgresStore.fromConnString(DB_URI); + await store.setup(); + + const contextSchema = z.object({ userId: z.string() }); + + const UserInfo = z.object({ name: z.string() }); + + const saveUserInfo = tool( + async ( + userInfo: z.infer, + runtime: ToolRuntime>, + ) => { + const userId = runtime.context.userId; + if (!userId) throw new Error("userId is required"); + await runtime.store.put(["users"], userId, userInfo); + return "Successfully saved user info."; + }, + { name: "save_user_info", description: "Save user info", schema: UserInfo }, + ); + + const agent = createAgent({ + model: "ollama:devstral-2", + tools: [saveUserInfo], + contextSchema, + store, + }); + + await agent.invoke( + { messages: [{ role: "user", content: "My name is John Smith" }] }, + { context: { userId: "user_123" } }, + ); + + const result = await store.get(["users"], "user_123"); + console.log(result?.value); + ``` + diff --git a/src/snippets/code-samples/middleware-dynamic-prompt-js.mdx b/src/snippets/code-samples/middleware-dynamic-prompt-js.mdx index 03e7a805bd..a10d761400 100644 --- a/src/snippets/code-samples/middleware-dynamic-prompt-js.mdx +++ b/src/snippets/code-samples/middleware-dynamic-prompt-js.mdx @@ -1,19 +1,141 @@ -```ts -import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + ```ts Google + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "google-genai:gemini-3.1-pro-preview", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` + + ```ts OpenAI + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "openai:gpt-5.4", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` + + ```ts Anthropic + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "anthropic:claude-sonnet-4-6", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` + + ```ts OpenRouter + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` -const addContextMiddleware = createMiddleware({ - name: "AddContextMiddleware", - wrapModelCall: async (request, handler) => { - return handler({ - ...request, - systemMessage: request.systemMessage.concat(`Additional context.`), - }); - }, -}); + ```ts Fireworks + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` -const agent = createAgent({ - model: "gpt-5.4", - systemPrompt: "You are a helpful assistant.", - middleware: [addContextMiddleware], -}); -``` + ```ts Baseten + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "baseten:zai-org/GLM-5", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` + + ```ts Ollama + import { createMiddleware, SystemMessage, createAgent } from "langchain"; + + const addContextMiddleware = createMiddleware({ + name: "AddContextMiddleware", + wrapModelCall: async (request, handler) => { + return handler({ + ...request, + systemMessage: request.systemMessage.concat(`Additional context.`), + }); + }, + }); + + const agent = createAgent({ + model: "ollama:devstral-2", + systemPrompt: "You are a helpful assistant.", + middleware: [addContextMiddleware], + }); + ``` + diff --git a/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx b/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx index 934fb5d1f5..9b30600cd1 100644 --- a/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx +++ b/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx @@ -1,39 +1,281 @@ -```ts -import z from "zod"; -import { createAgent, tool } from "langchain"; -import { ChatAnthropic } from "@langchain/anthropic"; + + ```ts Google + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "google-genai:gemini-3.1-pro-preview", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` -const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, -); + ```ts OpenAI + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "openai:gpt-5.4", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` -const agent = createAgent({ - model: new ChatAnthropic({ - model: "claude-sonnet-4-6", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], -}); + ```ts Anthropic + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "anthropic:claude-sonnet-4-6", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` -for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] -)) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } -} -``` + ```ts OpenRouter + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "openrouter:anthropic/claude-sonnet-4-6", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` + + ```ts Fireworks + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` + + ```ts Baseten + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "baseten:zai-org/GLM-5", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` + + ```ts Ollama + import z from "zod"; + import { createAgent, tool } from "langchain"; + import { ChatAnthropic } from "@langchain/anthropic"; + + const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, + ); + + const agent = createAgent({ + model: new ChatAnthropic({ + model: "ollama:devstral-2", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], + }); + + for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] + )) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } + } + ``` + From 5f0af1a3420f8845e53b4eabf6be21e35e9a2add Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Wed, 22 Apr 2026 11:45:19 +0200 Subject: [PATCH 2/3] update --- scripts/generate_code_snippet_mdx.py | 138 +-- .../deepagents/content-builder.py | 2 + .../deepagents/content-builder.ts | 7 +- src/code-samples/langchain/nostream-tag.py | 2 + src/code-samples/langchain/nostream-tag.ts | 2 + .../langchain/streaming-reasoning-tokens.py | 1 + .../langchain/streaming-reasoning-tokens.ts | 1 + src/oss/deepagents/content-builder.mdx | 2 +- .../content-builder-create-agent-js.mdx | 187 ++- .../code-samples/content-builder-tools-js.mdx | 4 +- .../code-samples/skills-sandbox-js.mdx | 1078 +++++++++++++++-- .../code-samples/skills-sandbox-py.mdx | 726 +++++++++-- .../streaming-reasoning-tokens-js.mdx | 314 +---- 13 files changed, 1881 insertions(+), 583 deletions(-) diff --git a/scripts/generate_code_snippet_mdx.py b/scripts/generate_code_snippet_mdx.py index 75850b9a27..c5bef37374 100644 --- a/scripts/generate_code_snippet_mdx.py +++ b/scripts/generate_code_snippet_mdx.py @@ -9,11 +9,16 @@ OpenRouter, Fireworks, Baseten, Ollama). Both `provider:model-id` and bare model names (for example `claude-sonnet-4-5-20250929`) are recognized. -Snippets are left as a single fenced block when the model is already a non-Google -quickstart tab line, the bare name for one of those tabs (for example `claude-sonnet-4-6`), -or other known non-Deep-Agents cases (for example `gemini-2.5-flash-image` for the -Google Genai client API). The first model= / model: in the file that is not skipped -is the one that triggers expansion. +Snippets are left as a single fenced block when no model argument is found, or when all +model arguments are marked to keep. + +To keep a specific model line: + +- In Python, put `# KEEP MODEL` on the line immediately before the `model="..."` line. +- In TypeScript, put `// KEEP MODEL` on the line immediately before the `model: "..."` line. + +The marker line is stripped during processing and that model occurrence is not +replaced/expanded. Run as part of `make code-snippets` after Bluehawk extraction. """ @@ -68,19 +73,8 @@ def _model_id_from_ts_tab_token(tab_token: str) -> str: return m.group(1) -# If the snippet already uses one of these model IDs (non-Google quickstart tab), -# do not expand: the author chose that tab line verbatim. -DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( - _model_id_from_py_tab_token(token) - for title, token in DEEPAGENTS_QUICKSTART_PY_MODEL_TABS - if title != "Google" -) - -DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( - _model_id_from_ts_tab_token(token) - for title, token in DEEPAGENTS_QUICKSTART_TS_MODEL_TABS - if title != "Google" -) +DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset() +DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset() def _id_after_first_colon(tab_id: str) -> str: @@ -90,31 +84,8 @@ def _id_after_first_colon(tab_id: str) -> str: return tab_id.split(":", 1)[1] -# Bare names that are not Deep Agents init lines (google.genai client, legacy tutorials). -DEEPAGENTS_PY_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( - { - "gemini-2.5-flash-image", - "claude-3-haiku-20240307", - } -) -DEEPAGENTS_TS_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS: frozenset[str] = frozenset( - { - "gemini-2.5-flash-image", - "claude-3-haiku-20240307", - } -) - - -def _should_skip_expand_py(model_id: str) -> bool: - if ":" in model_id: - return model_id in DEEPAGENTS_PY_SKIP_EXPAND_MODEL_IDS - return model_id in DEEPAGENTS_PY_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS - - -def _should_skip_expand_ts(model_id: str) -> bool: - if ":" in model_id: - return model_id in DEEPAGENTS_TS_SKIP_EXPAND_MODEL_IDS - return model_id in DEEPAGENTS_TS_EXTRA_BARE_SKIP_EXPAND_MODEL_IDS +KEEP_MODEL_MARKER_PY = "# KEEP MODEL" +KEEP_MODEL_MARKER_TS = "// KEEP MODEL" def _codegroup_fence(tab_title: str, fence_lang: str, code: str) -> str: @@ -129,16 +100,21 @@ def _codegroup_fence(tab_title: str, fence_lang: str, code: str) -> str: ) +def _replace_span(text: str, start: int, end: int, replacement: str) -> str: + return text[:start] + replacement + text[end:] + + def _expand_to_deepagents_codegroup( content: str, *, - canonical: str, + canonical_span: tuple[int, int], tab_definitions: list[tuple[str, str]], fence_lang: str, ) -> str: """Wrap `content` in a CodeGroup, one tab per quickstart model variant.""" + start, end = canonical_span parts = [ - _codegroup_fence(title, fence_lang, content.replace(canonical, model_token)) + _codegroup_fence(title, fence_lang, _replace_span(content, start, end, model_token)) for title, model_token in tab_definitions ] return "\n" + "\n\n".join(parts) + "\n\n" @@ -149,35 +125,61 @@ def maybe_expand_deepagents_quickstart_codegroup( *, language: str, fence_lang: str, -) -> str | None: - """If content uses a quickstart-expandable model= line, return CodeGroup MDX.""" +) -> tuple[str | None, str]: + """Return (expanded_mdx_or_none, content_with_keep_markers_stripped).""" + model_re: re.Pattern[str] + tab_definitions: list[tuple[str, str]] + keep_marker: str if language == "python": - for m in DEEPAGENTS_PY_MODEL_KWARG_RE.finditer(content): - if not _should_skip_expand_py(m.group(1)): - return _expand_to_deepagents_codegroup( - content, - canonical=m.group(0), - tab_definitions=DEEPAGENTS_QUICKSTART_PY_MODEL_TABS, - fence_lang=fence_lang, - ) - return None - if language == "ts": - for m in DEEPAGENTS_TS_MODEL_KWARG_RE.finditer(content): - if not _should_skip_expand_ts(m.group(1)): - return _expand_to_deepagents_codegroup( - content, - canonical=m.group(0), - tab_definitions=DEEPAGENTS_QUICKSTART_TS_MODEL_TABS, - fence_lang=fence_lang, - ) - return None - return None + model_re = DEEPAGENTS_PY_MODEL_KWARG_RE + tab_definitions = DEEPAGENTS_QUICKSTART_PY_MODEL_TABS + keep_marker = KEEP_MODEL_MARKER_PY + elif language == "ts": + model_re = DEEPAGENTS_TS_MODEL_KWARG_RE + tab_definitions = DEEPAGENTS_QUICKSTART_TS_MODEL_TABS + keep_marker = KEEP_MODEL_MARKER_TS + else: + return None, content + + # Strip marker lines while recording which model occurrence to expand. + out_lines: list[str] = [] + keep_next_model = False + canonical_span: tuple[int, int] | None = None + + for line in content.splitlines(keepends=True): + if line.strip() == keep_marker: + keep_next_model = True + continue + + out_offset = sum(len(l) for l in out_lines) + m = model_re.search(line) + if m is not None: + if keep_next_model: + keep_next_model = False + elif canonical_span is None: + canonical_span = (out_offset + m.start(), out_offset + m.end()) + + out_lines.append(line) + + stripped = "".join(out_lines) + if canonical_span is None: + return None, stripped + + return ( + _expand_to_deepagents_codegroup( + stripped, + canonical_span=canonical_span, + tab_definitions=tab_definitions, + fence_lang=fence_lang, + ), + stripped, + ) def format_snippet_mdx(content: str, *, language: str, fence_lang: str) -> str: """Return final MDX body for a snippet file.""" content = content.rstrip() + "\n" - expanded = maybe_expand_deepagents_quickstart_codegroup( + expanded, content = maybe_expand_deepagents_quickstart_codegroup( content, language=language, fence_lang=fence_lang ) if expanded is not None: diff --git a/src/code-samples/deepagents/content-builder.py b/src/code-samples/deepagents/content-builder.py index 6bfe221ab1..1433f5f92d 100644 --- a/src/code-samples/deepagents/content-builder.py +++ b/src/code-samples/deepagents/content-builder.py @@ -53,6 +53,7 @@ def generate_cover(prompt: str, slug: str) -> str: client = genai.Client() response = client.models.generate_content( + # KEEP MODEL model="gemini-2.5-flash-image", contents=[prompt], ) @@ -84,6 +85,7 @@ def generate_social_image(prompt: str, platform: str, slug: str) -> str: client = genai.Client() response = client.models.generate_content( + # KEEP MODEL model="gemini-2.5-flash-image", contents=[prompt], ) diff --git a/src/code-samples/deepagents/content-builder.ts b/src/code-samples/deepagents/content-builder.ts index 5dd404a0c9..d1aef2eabb 100644 --- a/src/code-samples/deepagents/content-builder.ts +++ b/src/code-samples/deepagents/content-builder.ts @@ -43,6 +43,7 @@ const generateCover = tool( const { GoogleGenerativeAI } = await import("@google/generative-ai"); const genai = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY ?? ""); const model = genai.getGenerativeModel({ + // KEEP MODEL model: "gemini-2.5-flash-image", }); const result = await model.generateContent(prompt); @@ -78,6 +79,7 @@ const generateSocialImage = tool( const { GoogleGenerativeAI } = await import("@google/generative-ai"); const genai = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY ?? ""); const model = genai.getGenerativeModel({ + // KEEP MODEL model: "gemini-2.5-flash-image", }); const result = await model.generateContent(prompt); @@ -100,9 +102,7 @@ const generateSocialImage = tool( prompt: z .string() .describe("Detailed description of the image to generate."), - platform: z - .string() - .describe('Either "linkedin" or "tweets"'), + platform: z.string().describe('Either "linkedin" or "tweets"'), slug: z .string() .describe("Post slug. Image saves to //image.png"), @@ -125,6 +125,7 @@ function createContentWriter() { }; return createDeepAgent({ + model: "google_genai:gemini-3.1-pro-preview", memory: ["./AGENTS.md"], skills: ["./skills/"], tools: [generateCover, generateSocialImage], diff --git a/src/code-samples/langchain/nostream-tag.py b/src/code-samples/langchain/nostream-tag.py index c2faf7de6a..5766364ce4 100644 --- a/src/code-samples/langchain/nostream-tag.py +++ b/src/code-samples/langchain/nostream-tag.py @@ -6,7 +6,9 @@ from langchain_anthropic import ChatAnthropic from langgraph.graph import START, StateGraph +# KEEP MODEL stream_model = ChatAnthropic(model_name="claude-3-haiku-20240307") +# KEEP MODEL internal_model = ChatAnthropic(model_name="claude-3-haiku-20240307").with_config( {"tags": ["nostream"]} ) diff --git a/src/code-samples/langchain/nostream-tag.ts b/src/code-samples/langchain/nostream-tag.ts index ad53cf7037..df0db60dfe 100644 --- a/src/code-samples/langchain/nostream-tag.ts +++ b/src/code-samples/langchain/nostream-tag.ts @@ -7,8 +7,10 @@ import { ChatAnthropic } from "@langchain/anthropic"; import { StateGraph, StateSchema, START } from "@langchain/langgraph"; import * as z from "zod"; +// KEEP MODEL const streamModel = new ChatAnthropic({ model: "claude-3-haiku-20240307" }); const internalModel = new ChatAnthropic({ + // KEEP MODEL model: "claude-3-haiku-20240307", }).withConfig({ tags: ["nostream"], diff --git a/src/code-samples/langchain/streaming-reasoning-tokens.py b/src/code-samples/langchain/streaming-reasoning-tokens.py index c116b8f4ca..15d9ca15c4 100644 --- a/src/code-samples/langchain/streaming-reasoning-tokens.py +++ b/src/code-samples/langchain/streaming-reasoning-tokens.py @@ -11,6 +11,7 @@ def get_weather(city: str) -> str: model = ChatAnthropic( + # KEEP MODEL model_name="claude-sonnet-4-6", timeout=None, stop=None, diff --git a/src/code-samples/langchain/streaming-reasoning-tokens.ts b/src/code-samples/langchain/streaming-reasoning-tokens.ts index 07e7f3ec63..e89dcafbde 100644 --- a/src/code-samples/langchain/streaming-reasoning-tokens.ts +++ b/src/code-samples/langchain/streaming-reasoning-tokens.ts @@ -16,6 +16,7 @@ const getWeather = tool( const agent = createAgent({ model: new ChatAnthropic({ + // KEEP MODEL model: "claude-sonnet-4-6", thinking: { type: "enabled", budget_tokens: 5000 }, }), diff --git a/src/oss/deepagents/content-builder.mdx b/src/oss/deepagents/content-builder.mdx index 3e99ab5fb9..e3da7085ff 100644 --- a/src/oss/deepagents/content-builder.mdx +++ b/src/oss/deepagents/content-builder.mdx @@ -38,7 +38,7 @@ This tutorial covers: API keys: -- Anthropic (Claude) +- Anthropic (Claude) or other provider API key - Google (Gemini) for image generation with `gemini-2.5-flash-image` - [Tavily](https://www.tavily.com/) for web search (free tier) - [LangSmith](https://smith.langchain.com/) for tracing (optional) diff --git a/src/snippets/code-samples/content-builder-create-agent-js.mdx b/src/snippets/code-samples/content-builder-create-agent-js.mdx index 27d8ecddcd..4fc8639e80 100644 --- a/src/snippets/code-samples/content-builder-create-agent-js.mdx +++ b/src/snippets/code-samples/content-builder-create-agent-js.mdx @@ -1,22 +1,169 @@ -```ts -import { createDeepAgent, FilesystemBackend } from "deepagents"; + + ```ts Google + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "google-genai:gemini-3.1-pro-preview", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` -function createContentWriter() { - const researcherSubagent = { - name: "researcher", - description: - "Research subagent with web search capability. Delegate research tasks here.", - systemPrompt: - "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", - tools: [webSearch], - }; + ```ts OpenAI + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "openai:gpt-5.4", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` - return createDeepAgent({ - memory: ["./AGENTS.md"], - skills: ["./skills/"], - tools: [generateCover, generateSocialImage], - subagents: [researcherSubagent], - backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), - }); -} -``` + ```ts Anthropic + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "anthropic:claude-sonnet-4-6", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` + + ```ts OpenRouter + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` + + ```ts Fireworks + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` + + ```ts Baseten + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "baseten:zai-org/GLM-5", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` + + ```ts Ollama + import { createDeepAgent, FilesystemBackend } from "deepagents"; + + function createContentWriter() { + const researcherSubagent = { + name: "researcher", + description: + "Research subagent with web search capability. Delegate research tasks here.", + systemPrompt: + "You are a research assistant. Use the web_search tool to find current, accurate information and return well-organized findings.", + tools: [webSearch], + }; + + return createDeepAgent({ + model: "ollama:devstral-2", + memory: ["./AGENTS.md"], + skills: ["./skills/"], + tools: [generateCover, generateSocialImage], + subagents: [researcherSubagent], + backend: new FilesystemBackend({ rootDir: EXAMPLE_DIR }), + }); + } + ``` + diff --git a/src/snippets/code-samples/content-builder-tools-js.mdx b/src/snippets/code-samples/content-builder-tools-js.mdx index 4f566b6894..f2c96a26d8 100644 --- a/src/snippets/code-samples/content-builder-tools-js.mdx +++ b/src/snippets/code-samples/content-builder-tools-js.mdx @@ -98,9 +98,7 @@ const generateSocialImage = tool( prompt: z .string() .describe("Detailed description of the image to generate."), - platform: z - .string() - .describe('Either "linkedin" or "tweets"'), + platform: z.string().describe('Either "linkedin" or "tweets"'), slug: z .string() .describe("Post slug. Image saves to //image.png"), diff --git a/src/snippets/code-samples/skills-sandbox-js.mdx b/src/snippets/code-samples/skills-sandbox-js.mdx index 6080aa1816..75f22b0a20 100644 --- a/src/snippets/code-samples/skills-sandbox-js.mdx +++ b/src/snippets/code-samples/skills-sandbox-js.mdx @@ -1,136 +1,960 @@ -```ts -import { readFile, readdir } from "node:fs/promises"; -import { join, posix, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -import { createMiddleware } from "langchain"; -import { - CompositeBackend, - createDeepAgent, - type FileData, - StoreBackend, -} from "deepagents"; -import { InMemoryStore } from "@langchain/langgraph"; - -import { DaytonaSandbox } from "@langchain/daytona"; - -/** Identical skill bundles for every user: one shared store namespace. */ -const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; - -function createFileData(content: string): FileData { - const now = new Date().toISOString(); - return { - content: content.split("\n"), - created_at: now, - modified_at: now, - }; -} - -function normalizeSkillsStoreKey(key: string): string { - const k = String(key); - if (k.includes("..") || /[*?]/.test(k)) { - throw new Error(`Invalid key: ${key}`); - } - return k.startsWith("/") ? k : `/${k}`; -} - -async function walkFiles(dir: string): Promise { - const entries = await readdir(dir, { withFileTypes: true }); - const files: string[] = []; - for (const entry of entries) { - const fullPath = join(dir, entry.name); - if (entry.isDirectory()) { - files.push(...(await walkFiles(fullPath))); - } else if (entry.isFile()) { - files.push(fullPath); - } - } - return files.sort((a, b) => a.localeCompare(b)); -} - -/** Load canonical skill files from disk into the shared store namespace (run once at deploy). - * You can retrieve skills from any source (local filesystem, remote URL, etc.). - */ -async function seedSkillStore(store: InMemoryStore) { - const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); - const skillsDir = resolve(moduleDir, "skills"); - const filePaths = await walkFiles(skillsDir); - for (const filePath of filePaths) { - const rel = relative(skillsDir, filePath); - // StoreBackend keys are paths *relative to the routed backend root*. - // CompositeBackend strips the route prefix (`/skills/`) before delegating, - // so store keys should look like "//SKILL.md". - const key = `/${posix.normalize(rel.split("\\").join("/"))}`; - const content = await readFile(filePath, "utf8"); - await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); - } -} - -/** Copy shared skill files from the store into the sandbox before each agent run. */ -function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { - return createMiddleware({ - name: "SkillSandboxSyncMiddleware", - beforeAgent: async (state, runtime) => { - const store = (runtime as any).store; - if (!store) { - throw new Error( - "Store is required for syncing skills into the sandbox. " + - "Pass `store` to createDeepAgent and ensure your runtime provides it.", - ); + + ```ts Google + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); } - - const encoder = new TextEncoder(); - const files: Array<[string, Uint8Array]> = []; - - for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { - const normalized = normalizeSkillsStoreKey(String(item.key)); - const data = item.value as FileData; - // CompositeBackend routes paths and batches uploads to the right backend. - files.push([ - `/skills${normalized}`, - encoder.encode(data.content.join("\n")), - ]); + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "google-genai:gemini-3.1-pro-preview", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` - if (files.length > 0) await backend.uploadFiles(files); - - return state; - }, - }); -} - -async function main() { - const store = new InMemoryStore(); - await seedSkillStore(store); + ```ts OpenAI + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "openai:gpt-5.4", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` - const sandbox = await DaytonaSandbox.create({ - language: "python", - timeout: 300, - }); + ```ts Anthropic + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "anthropic:claude-sonnet-4-6", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` - const backend = new CompositeBackend(sandbox, { - "/skills/": new StoreBackend({ - store, - namespace: () => [...SKILLS_SHARED_NAMESPACE], - } as any), - }); + ```ts OpenRouter + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "openrouter:anthropic/claude-sonnet-4-6", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` - try { - const agent = await createDeepAgent({ - model: "anthropic:claude-sonnet-4-6", - backend, - skills: ["/skills/"], - store, - middleware: [createSkillSandboxSyncMiddleware(backend)], + ```ts Fireworks + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; }); + ``` - } finally { - await sandbox.close(); - } -} + ```ts Baseten + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "baseten:zai-org/GLM-5", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` -main().catch((err) => { - console.error(err); - process.exitCode = 1; -}); -``` + ```ts Ollama + import { readFile, readdir } from "node:fs/promises"; + import { join, posix, relative, resolve } from "node:path"; + import { fileURLToPath } from "node:url"; + + import { createMiddleware } from "langchain"; + import { + CompositeBackend, + createDeepAgent, + type FileData, + StoreBackend, + } from "deepagents"; + import { InMemoryStore } from "@langchain/langgraph"; + + import { DaytonaSandbox } from "@langchain/daytona"; + + /** Identical skill bundles for every user: one shared store namespace. */ + const SKILLS_SHARED_NAMESPACE = ["skills", "builtin"] as const; + + function createFileData(content: string): FileData { + const now = new Date().toISOString(); + return { + content: content.split("\n"), + created_at: now, + modified_at: now, + }; + } + + function normalizeSkillsStoreKey(key: string): string { + const k = String(key); + if (k.includes("..") || /[*?]/.test(k)) { + throw new Error(`Invalid key: ${key}`); + } + return k.startsWith("/") ? k : `/${k}`; + } + + async function walkFiles(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...(await walkFiles(fullPath))); + } else if (entry.isFile()) { + files.push(fullPath); + } + } + return files.sort((a, b) => a.localeCompare(b)); + } + + /** Load canonical skill files from disk into the shared store namespace (run once at deploy). + * You can retrieve skills from any source (local filesystem, remote URL, etc.). + */ + async function seedSkillStore(store: InMemoryStore) { + const moduleDir = resolve(fileURLToPath(new URL(".", import.meta.url))); + const skillsDir = resolve(moduleDir, "skills"); + const filePaths = await walkFiles(skillsDir); + for (const filePath of filePaths) { + const rel = relative(skillsDir, filePath); + // StoreBackend keys are paths *relative to the routed backend root*. + // CompositeBackend strips the route prefix (`/skills/`) before delegating, + // so store keys should look like "//SKILL.md". + const key = `/${posix.normalize(rel.split("\\").join("/"))}`; + const content = await readFile(filePath, "utf8"); + await store.put([...SKILLS_SHARED_NAMESPACE], key, createFileData(content)); + } + } + + /** Copy shared skill files from the store into the sandbox before each agent run. */ + function createSkillSandboxSyncMiddleware(backend: CompositeBackend) { + return createMiddleware({ + name: "SkillSandboxSyncMiddleware", + beforeAgent: async (state, runtime) => { + const store = (runtime as any).store; + if (!store) { + throw new Error( + "Store is required for syncing skills into the sandbox. " + + "Pass `store` to createDeepAgent and ensure your runtime provides it.", + ); + } + + const encoder = new TextEncoder(); + const files: Array<[string, Uint8Array]> = []; + + for (const item of await store.search([...SKILLS_SHARED_NAMESPACE])) { + const normalized = normalizeSkillsStoreKey(String(item.key)); + const data = item.value as FileData; + // CompositeBackend routes paths and batches uploads to the right backend. + files.push([ + `/skills${normalized}`, + encoder.encode(data.content.join("\n")), + ]); + } + + if (files.length > 0) await backend.uploadFiles(files); + + return state; + }, + }); + } + + async function main() { + const store = new InMemoryStore(); + await seedSkillStore(store); + + const sandbox = await DaytonaSandbox.create({ + language: "python", + timeout: 300, + }); + + const backend = new CompositeBackend(sandbox, { + "/skills/": new StoreBackend({ + store, + namespace: () => [...SKILLS_SHARED_NAMESPACE], + } as any), + }); + + try { + const agent = await createDeepAgent({ + model: "ollama:devstral-2", + backend, + skills: ["/skills/"], + store, + middleware: [createSkillSandboxSyncMiddleware(backend)], + }); + + } finally { + await sandbox.close(); + } + } + + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); + ``` + diff --git a/src/snippets/code-samples/skills-sandbox-py.mdx b/src/snippets/code-samples/skills-sandbox-py.mdx index e1cf7c03a1..cd623878e9 100644 --- a/src/snippets/code-samples/skills-sandbox-py.mdx +++ b/src/snippets/code-samples/skills-sandbox-py.mdx @@ -1,92 +1,652 @@ -```python -import asyncio -from pathlib import Path -from typing import Any - -from daytona import Daytona -from deepagents import create_deep_agent -from deepagents.backends import CompositeBackend, StoreBackend -from deepagents.backends.utils import create_file_data -from langchain.agents.middleware import AgentMiddleware, AgentState - -from langchain_daytona import DaytonaSandbox -from langgraph.runtime import Runtime -from langgraph.store.memory import InMemoryStore - -# Identical skill bundles for every user: one shared store namespace. -SKILLS_SHARED_NAMESPACE = ("skills", "builtin") - - -class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): - """Copy shared skill files from the store into the sandbox before each agent run.""" - - def __init__(self, backend: CompositeBackend) -> None: - super().__init__() - self.backend = backend - - async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: - store = runtime.store - - files: list[tuple[str, bytes]] = [] - for item in await store.asearch(SKILLS_SHARED_NAMESPACE): - key = str(item.key) - if ".." in key or any(c in key for c in ("*", "?")): - msg = f"Invalid key: {key}" - raise ValueError(msg) - normalized = key if key.startswith("/") else f"/{key}" - # CompositeBackend routes paths and batches uploads to the right backend. - files.append((f"/skills{normalized}", item.value["content"].encode())) - - if files: - await self.backend.aupload_files(files) - - -async def seed_skill_store(store: InMemoryStore) -> None: - """Load canonical skill files from disk into the shared store namespace (run once at deploy). - You can retrieve skills from any source (local filesystem, remote URL, etc.). - """ - skills_dir = Path(__file__).resolve().parent / "skills" - for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): - rel = file_path.relative_to(skills_dir).as_posix() - key = f"/{rel}" - await store.aput( - SKILLS_SHARED_NAMESPACE, - key, - create_file_data(file_path.read_text(encoding="utf-8")), + + ```python Google + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, ) + + try: + agent = create_deep_agent( + model="google_genai:gemini-3.1-pro-preview", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + ```python OpenAI + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, + ) + + try: + agent = create_deep_agent( + model="openai:gpt-5.4", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` -async def main() -> None: - store = InMemoryStore() - await seed_skill_store(store) - - daytona = Daytona() - sandbox = daytona.create() - sandbox_backend = DaytonaSandbox(sandbox=sandbox) - - backend = CompositeBackend( - default=sandbox_backend, - routes={ - "/skills/": StoreBackend( + ```python Anthropic + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, + ) + + try: + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-6", + backend=backend, + skills=["/skills/"], store=store, - namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, - ), - }, - ) + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` - try: - agent = create_deep_agent( - model="anthropic:claude-sonnet-4-6", - backend=backend, - skills=["/skills/"], - store=store, - middleware=[SkillSandboxSyncMiddleware(backend)], + ```python OpenRouter + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, ) + + try: + agent = create_deep_agent( + model="openrouter:anthropic/claude-sonnet-4-6", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` - finally: - sandbox.stop() + ```python Fireworks + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, + ) + + try: + agent = create_deep_agent( + model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + ```python Baseten + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, + ) + + try: + agent = create_deep_agent( + model="baseten:zai-org/GLM-5", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` -if __name__ == "__main__": - asyncio.run(main()) -``` + ```python Ollama + import asyncio + from pathlib import Path + from typing import Any + + from daytona import Daytona + from deepagents import create_deep_agent + from deepagents.backends import CompositeBackend, StoreBackend + from deepagents.backends.utils import create_file_data + from langchain.agents.middleware import AgentMiddleware, AgentState + + from langchain_daytona import DaytonaSandbox + from langgraph.runtime import Runtime + from langgraph.store.memory import InMemoryStore + + # Identical skill bundles for every user: one shared store namespace. + SKILLS_SHARED_NAMESPACE = ("skills", "builtin") + + + class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]): + """Copy shared skill files from the store into the sandbox before each agent run.""" + + def __init__(self, backend: CompositeBackend) -> None: + super().__init__() + self.backend = backend + + async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None: + store = runtime.store + + files: list[tuple[str, bytes]] = [] + for item in await store.asearch(SKILLS_SHARED_NAMESPACE): + key = str(item.key) + if ".." in key or any(c in key for c in ("*", "?")): + msg = f"Invalid key: {key}" + raise ValueError(msg) + normalized = key if key.startswith("/") else f"/{key}" + # CompositeBackend routes paths and batches uploads to the right backend. + files.append((f"/skills{normalized}", item.value["content"].encode())) + + if files: + await self.backend.aupload_files(files) + + + async def seed_skill_store(store: InMemoryStore) -> None: + """Load canonical skill files from disk into the shared store namespace (run once at deploy). + You can retrieve skills from any source (local filesystem, remote URL, etc.). + """ + skills_dir = Path(__file__).resolve().parent / "skills" + for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()): + rel = file_path.relative_to(skills_dir).as_posix() + key = f"/{rel}" + await store.aput( + SKILLS_SHARED_NAMESPACE, + key, + create_file_data(file_path.read_text(encoding="utf-8")), + ) + + + async def main() -> None: + store = InMemoryStore() + await seed_skill_store(store) + + daytona = Daytona() + sandbox = daytona.create() + sandbox_backend = DaytonaSandbox(sandbox=sandbox) + + backend = CompositeBackend( + default=sandbox_backend, + routes={ + "/skills/": StoreBackend( + store=store, + namespace=lambda _rt: SKILLS_SHARED_NAMESPACE, + ), + }, + ) + + try: + agent = create_deep_agent( + model="ollama:devstral-2", + backend=backend, + skills=["/skills/"], + store=store, + middleware=[SkillSandboxSyncMiddleware(backend)], + ) + + finally: + sandbox.stop() + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + diff --git a/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx b/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx index 9b30600cd1..934fb5d1f5 100644 --- a/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx +++ b/src/snippets/code-samples/streaming-reasoning-tokens-js.mdx @@ -1,281 +1,39 @@ - - ```ts Google - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "google-genai:gemini-3.1-pro-preview", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` +```ts +import z from "zod"; +import { createAgent, tool } from "langchain"; +import { ChatAnthropic } from "@langchain/anthropic"; - ```ts OpenAI - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "openai:gpt-5.4", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` +const getWeather = tool( + async ({ city }) => { + return `It's always sunny in ${city}!`; + }, + { + name: "get_weather", + description: "Get weather for a given city.", + schema: z.object({ city: z.string() }), + }, +); - ```ts Anthropic - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "anthropic:claude-sonnet-4-6", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` +const agent = createAgent({ + model: new ChatAnthropic({ + model: "claude-sonnet-4-6", + thinking: { type: "enabled", budget_tokens: 5000 }, + }), + tools: [getWeather], +}); - ```ts OpenRouter - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "openrouter:anthropic/claude-sonnet-4-6", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` - - ```ts Fireworks - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "fireworks:accounts/fireworks/models/qwen3p5-397b-a17b", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` - - ```ts Baseten - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "baseten:zai-org/GLM-5", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` - - ```ts Ollama - import z from "zod"; - import { createAgent, tool } from "langchain"; - import { ChatAnthropic } from "@langchain/anthropic"; - - const getWeather = tool( - async ({ city }) => { - return `It's always sunny in ${city}!`; - }, - { - name: "get_weather", - description: "Get weather for a given city.", - schema: z.object({ city: z.string() }), - }, - ); - - const agent = createAgent({ - model: new ChatAnthropic({ - model: "ollama:devstral-2", - thinking: { type: "enabled", budget_tokens: 5000 }, - }), - tools: [getWeather], - }); - - for await (const [token, metadata] of await agent.stream( - { messages: [{ role: "user", content: "What is the weather in SF?" }] }, - { streamMode: "messages" }, // [!code highlight] - )) { - if (!token.contentBlocks) continue; - const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); - const text = token.contentBlocks.filter((b) => b.type === "text"); - if (reasoning.length) { - process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); - } - if (text.length) { - process.stdout.write(text[0].text); - } - } - ``` - +for await (const [token, metadata] of await agent.stream( + { messages: [{ role: "user", content: "What is the weather in SF?" }] }, + { streamMode: "messages" }, // [!code highlight] +)) { + if (!token.contentBlocks) continue; + const reasoning = token.contentBlocks.filter((b) => b.type === "reasoning"); + const text = token.contentBlocks.filter((b) => b.type === "text"); + if (reasoning.length) { + process.stdout.write(`[thinking] ${reasoning[0].reasoning}`); + } + if (text.length) { + process.stdout.write(text[0].text); + } +} +``` From 1aa90eaad66c310e06caceaf8218e833ed7aa05e Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:15:10 +0200 Subject: [PATCH 3/3] update --- scripts/generate_code_snippet_mdx.py | 4 +++- src/code-samples/deepagents/content-builder.ts | 2 +- src/code-samples/langchain/nostream-tag.py | 4 ++-- src/code-samples/langchain/nostream-tag.ts | 9 ++++++--- src/code-samples/package-lock.json | 9 --------- src/langsmith/trace-bedrock.mdx | 6 +++--- src/snippets/code-samples/nostream-tag-js.mdx | 4 ++-- src/snippets/code-samples/nostream-tag-py.mdx | 4 ++-- 8 files changed, 19 insertions(+), 23 deletions(-) diff --git a/scripts/generate_code_snippet_mdx.py b/scripts/generate_code_snippet_mdx.py index c5bef37374..3caff18456 100644 --- a/scripts/generate_code_snippet_mdx.py +++ b/scripts/generate_code_snippet_mdx.py @@ -114,7 +114,9 @@ def _expand_to_deepagents_codegroup( """Wrap `content` in a CodeGroup, one tab per quickstart model variant.""" start, end = canonical_span parts = [ - _codegroup_fence(title, fence_lang, _replace_span(content, start, end, model_token)) + _codegroup_fence( + title, fence_lang, _replace_span(content, start, end, model_token) + ) for title, model_token in tab_definitions ] return "\n" + "\n\n".join(parts) + "\n\n" diff --git a/src/code-samples/deepagents/content-builder.ts b/src/code-samples/deepagents/content-builder.ts index d1aef2eabb..de06e8abe6 100644 --- a/src/code-samples/deepagents/content-builder.ts +++ b/src/code-samples/deepagents/content-builder.ts @@ -125,7 +125,7 @@ function createContentWriter() { }; return createDeepAgent({ - model: "google_genai:gemini-3.1-pro-preview", + model: "anthropic:claude-sonnet-4-6", memory: ["./AGENTS.md"], skills: ["./skills/"], tools: [generateCover, generateSocialImage], diff --git a/src/code-samples/langchain/nostream-tag.py b/src/code-samples/langchain/nostream-tag.py index 5766364ce4..daf3d76d2a 100644 --- a/src/code-samples/langchain/nostream-tag.py +++ b/src/code-samples/langchain/nostream-tag.py @@ -7,9 +7,9 @@ from langgraph.graph import START, StateGraph # KEEP MODEL -stream_model = ChatAnthropic(model_name="claude-3-haiku-20240307") +stream_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001") # KEEP MODEL -internal_model = ChatAnthropic(model_name="claude-3-haiku-20240307").with_config( +internal_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001").with_config( {"tags": ["nostream"]} ) diff --git a/src/code-samples/langchain/nostream-tag.ts b/src/code-samples/langchain/nostream-tag.ts index df0db60dfe..b64c7f61e4 100644 --- a/src/code-samples/langchain/nostream-tag.ts +++ b/src/code-samples/langchain/nostream-tag.ts @@ -8,10 +8,10 @@ import { StateGraph, StateSchema, START } from "@langchain/langgraph"; import * as z from "zod"; // KEEP MODEL -const streamModel = new ChatAnthropic({ model: "claude-3-haiku-20240307" }); +const streamModel = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" }); const internalModel = new ChatAnthropic({ // KEEP MODEL - model: "claude-3-haiku-20240307", + model: "claude-haiku-4-5-20251001", }).withConfig({ tags: ["nostream"], }); @@ -44,7 +44,10 @@ const graph = new StateGraph(State) .addEdge("writeAnswer", "internal_notes") .compile(); -const stream = await graph.stream({ topic: "AI" }, { streamMode: "messages" }); +const stream = await graph.stream( + { topic: "AI", answer: "", notes: "" }, + { streamMode: "messages" }, +); // :snippet-end: // :remove-start: diff --git a/src/code-samples/package-lock.json b/src/code-samples/package-lock.json index feeba37f5a..74301017dd 100644 --- a/src/code-samples/package-lock.json +++ b/src/code-samples/package-lock.json @@ -249,7 +249,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1032.0.tgz", "integrity": "sha512-A1wjVhV3IgsZ5td2l4AWgK03EjZ+ldwbiorxuO1hPf7RHJtSdr6oq/gKzyUwP7Tm7ma/M2xS/tplg5C8XB8RWg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", @@ -1502,7 +1501,6 @@ "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.36.tgz", "integrity": "sha512-9NWsdzU3uZD13lJwunXK0t6SIwew+UwcbHggW5yUdaiMmzKeNkDpp1lRD6p49N8+D0Vv4qmQBEKB4Ukh2jfnvw==", "license": "MIT", - "peer": true, "dependencies": { "@cfworker/json-schema": "^4.0.2", "@standard-schema/spec": "^1.1.0", @@ -1562,7 +1560,6 @@ "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz", "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==", "license": "MIT", - "peer": true, "dependencies": { "uuid": "^10.0.0" }, @@ -1747,7 +1744,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3230,7 +3226,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3459,7 +3454,6 @@ "resolved": "https://registry.npmjs.org/deepagents/-/deepagents-1.8.5.tgz", "integrity": "sha512-HgKryOCb00RNmwLJ0m6GekWkYb6mvRJrXGx23rkSzDG5cNpc5/LGuFYQ2DiBCBH0NGn/Rkv6xYRJjGPEKnM02A==", "license": "MIT", - "peer": true, "dependencies": { "@langchain/core": "^1.1.33", "@langchain/langgraph": "^1.1.4", @@ -4062,7 +4056,6 @@ "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.20.tgz", "integrity": "sha512-ULhLM8RswvQDXufLtNtvclHrWCBx8Cb5UPI6lAZC+8Dq59iHsVPz/3Ac9khWNm1VIvChRsuykixD/WrmzuuA3Q==", "license": "MIT", - "peer": true, "dependencies": { "p-queue": "6.6.2", "uuid": "10.0.0" @@ -4319,7 +4312,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", @@ -4906,7 +4898,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/langsmith/trace-bedrock.mdx b/src/langsmith/trace-bedrock.mdx index 85d164c259..994dcdc526 100644 --- a/src/langsmith/trace-bedrock.mdx +++ b/src/langsmith/trace-bedrock.mdx @@ -25,7 +25,7 @@ npm install @aws-sdk/client-bedrock-runtime langsmith -This integration uses the native AWS SDKs with LangSmith's tracing capabilities. For Python, you'll use [`boto3`](https://pypi.org/project/boto3/) (the AWS SDK for Python) along with [`langsmith`](https://pypi.org/project/langsmith/) to capture traces. For JavaScript/TypeScript, you'll use [`@aws-sdk/client-bedrock-runtime`](https://www.npmjs.com/package/@aws-sdk/client-bedrock-runtime) with the [`langsmith`](https://www.npmjs.com/package/langsmith) package. Both implementations use the [Bedrock Converse API](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html), which provides a unified interface for interacting with foundation models. +This integration uses the native AWS SDKs with LangSmith's tracing capabilities. For Python, you'll use [`boto3`](https://pypi.org/project/boto3/) (the AWS SDK for Python) along with [`langsmith`](https://pypi.org/project/langsmith/) to capture traces. For JavaScript/TypeScript, you'll use [`@aws-sdk/client-bedrock-runtime`](https://www.npmjs.org/package/@aws-sdk/client-bedrock-runtime) with the [`langsmith`](https://www.npmjs.org/package/langsmith) package. Both implementations use the [Bedrock Converse API](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html), which provides a unified interface for interacting with foundation models. ## Setup @@ -66,7 +66,7 @@ from langsmith import traceable # Initialize Bedrock runtime client (ensure AWS creds and region are set) bedrock = boto3.client("bedrock-runtime", region_name="us-east-1") -model_id = "anthropic.claude-3-haiku-20240307-v1:0" # Example Bedrock model ID +model_id = "anthropic.claude-haiku-4-5-20251001-v1:0" # Example Bedrock model ID # Decorate the model invocation function to auto-capture a trace with tags/metadata @traceable(tags=["aws-bedrock", "langsmith", "integration-test"], @@ -96,7 +96,7 @@ import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-r import { traceable } from "langsmith"; const client = new BedrockRuntimeClient({ region: "us-east-1" }); -const modelId = "anthropic.claude-3-haiku-20240307-v1:0"; +const modelId = "anthropic.claude-haiku-4-5-20251001-v1:0"; // Wrap the Bedrock invocation in a traceable function with tags and metadata const invokeBedrock = traceable( diff --git a/src/snippets/code-samples/nostream-tag-js.mdx b/src/snippets/code-samples/nostream-tag-js.mdx index c376be7bf9..996cacc8ed 100644 --- a/src/snippets/code-samples/nostream-tag-js.mdx +++ b/src/snippets/code-samples/nostream-tag-js.mdx @@ -3,9 +3,9 @@ import { ChatAnthropic } from "@langchain/anthropic"; import { StateGraph, StateSchema, START } from "@langchain/langgraph"; import * as z from "zod"; -const streamModel = new ChatAnthropic({ model: "claude-3-haiku-20240307" }); +const streamModel = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" }); const internalModel = new ChatAnthropic({ - model: "claude-3-haiku-20240307", + model: "claude-haiku-4-5-20251001", }).withConfig({ tags: ["nostream"], }); diff --git a/src/snippets/code-samples/nostream-tag-py.mdx b/src/snippets/code-samples/nostream-tag-py.mdx index 3d83278cad..d7bd538581 100644 --- a/src/snippets/code-samples/nostream-tag-py.mdx +++ b/src/snippets/code-samples/nostream-tag-py.mdx @@ -4,8 +4,8 @@ from typing import Any, TypedDict from langchain_anthropic import ChatAnthropic from langgraph.graph import START, StateGraph -stream_model = ChatAnthropic(model_name="claude-3-haiku-20240307") -internal_model = ChatAnthropic(model_name="claude-3-haiku-20240307").with_config( +stream_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001") +internal_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001").with_config( {"tags": ["nostream"]} )