langchain-multi-env-setup
Build reliable dev / staging / prod isolation for LangChain 1.0 services — Pydantic `Settings` + `SecretStr`, cloud Secret Manager in prod, per-env prompt and model version pinning, env-specific checkpointer and observability. Use when graduating from `.env`-in-dev to real prod infra, or debugging a config that loaded the wrong values in the wrong env. Trigger with "langchain multi-env", "langchain pydantic settings", "langchain secret manager", "langchain env config", "langchain prod setup".
What this skill does
# LangChain Multi-Env Setup (Python)
## Overview
A team ships a LangChain 1.0 service to staging with `python-dotenv` loading
`.env.staging` into `os.environ`. Security audits —
`docker exec STAGING-POD env` prints `ANTHROPIC_API_KEY=sk-ant-api03-...` in
plain text. Anyone with `kubectl exec`, any sidecar, any core dump, any
error tracker that auto-captures process env sees the key. This is pain
**P37**: secrets loaded from `.env` in production containers leak via `env`.
A second failure chains. A developer runs the staging deploy from a shell
where `LANGCHAIN_ENV=production` was set hours earlier. The loader picks
the prod `.env`, staging answers with a prompt commit tuned only for the
prod model tier, latency doubles. Two root causes: no type-safe env gate,
no startup validation that would have caught the mismatched model id.
Both are one refactor:
```python
# BAD — dotenv populates os.environ; any process with container access sees it
from dotenv import load_dotenv
load_dotenv(".env.production")
api_key = os.environ["ANTHROPIC_API_KEY"] # P37: leaks via `docker exec env`
# GOOD — SecretStr in a validated Settings object, pulled from Secret Manager
from pydantic import SecretStr
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
env: Literal["dev", "staging", "prod"]
anthropic_api_key: SecretStr
settings = build_settings() # pulls from GCP Secret Manager in prod
api_key = settings.anthropic_api_key.get_secret_value()
# repr(settings) prints `SecretStr('**********')` — safe to log
```
This skill owns the per-env **config plumbing** — `Settings` skeleton,
Secret Manager integration, per-env pinning, startup smoke test. It does
**not** own the full secrets lifecycle (rotation, revocation, scope) —
that belongs to `langchain-security-basics`.
Pin: `langchain-core 1.0.x`, `langchain-anthropic 1.0.x`, `pydantic >= 2.5`,
`pydantic-settings >= 2.1`. Pain anchors: **P37** (primary), **P20**
(checkpointer schema — cross-ref `langchain-langgraph-checkpointing`).
Two numbers: **smoke test < 10 seconds**; **env-var count ~15-30** (more
than 30 means `Settings` is absorbing feature flags and should split).
## Prerequisites
- Python 3.10+ (3.11+ recommended for `Literal` and `StrEnum` ergonomics)
- `langchain-core >= 1.0, < 2.0`
- `pydantic >= 2.5`, `pydantic-settings >= 2.1`
- One secret backend: GCP Secret Manager (`google-cloud-secret-manager`),
AWS Secrets Manager (`boto3`), or HashiCorp Vault (`hvac`)
- Completed `langchain-sdk-patterns` — the `Settings` object is injected into
the chain factories from that skill
## Instructions
Run these six steps in order — each adds one invariant the next step depends on:
1. Define a `Settings` class with `SecretStr` keys, `Literal` env, and fail-fast validation.
2. Add a per-env loader — file in dev, env vars in staging, Secret Manager in prod.
3. Use the cloud Secret Manager client to pull keys into memory only.
4. Pin `model_id`, `prompt_commit_hash`, and `vector_index_name` per env.
5. Configure the checkpointer per env — memory in dev, Postgres elsewhere.
6. Run a startup smoke test under 10 seconds before the HTTP server binds.
### Step 1 — Create a Settings class with SecretStr and fail-fast validation
```python
from typing import Literal
from pydantic import SecretStr, HttpUrl, Field, ValidationError
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=None, # see Step 2 — loader picks the file
env_file_encoding="utf-8",
case_sensitive=False,
extra="forbid", # reject unknown env vars — typo detection
)
# --- env switch (drives everything else) ---
env: Literal["dev", "staging", "prod"] = Field(..., alias="LANGCHAIN_ENV")
# --- secrets (always SecretStr — never str) ---
anthropic_api_key: SecretStr = Field(..., alias="ANTHROPIC_API_KEY")
openai_api_key: SecretStr = Field(..., alias="OPENAI_API_KEY")
langsmith_api_key: SecretStr = Field(..., alias="LANGSMITH_API_KEY")
# --- per-env pinning (see Step 4) ---
model_id: str = Field(..., alias="LANGCHAIN_MODEL_ID")
prompt_commit_hash: str = Field(..., alias="LANGCHAIN_PROMPT_COMMIT")
vector_index_name: str = Field(..., alias="LANGCHAIN_VECTOR_INDEX")
# --- endpoints (validated URLs — typo caught at startup) ---
checkpointer_url: HttpUrl | None = Field(None, alias="LANGCHAIN_CHECKPOINTER_URL")
otel_endpoint: HttpUrl = Field(..., alias="OTEL_EXPORTER_OTLP_ENDPOINT")
# --- budget guards (per-env) ---
max_cost_usd_per_day: float = Field(10.0, alias="LANGCHAIN_DAILY_BUDGET_USD")
max_rpm: int = Field(60, alias="LANGCHAIN_MAX_RPM")
```
`SecretStr` masks `repr(settings)` to `SecretStr('**********')` — a routine
`logger.info(settings)` cannot leak the key. The only way to read plaintext
is `.get_secret_value()`, which greps like a sore thumb in review.
`extra="forbid"` catches typos (`LANGCHIN_MODEL_ID`) at import time.
`HttpUrl` rejects `http:/otel:4318` before the exporter wastes 60s on DNS.
See [Settings Skeleton](references/settings-skeleton.md) for the full class.
### Step 2 — Per-env config loading (file OR Secret Manager, never both)
```python
import os
from pathlib import Path
def build_settings() -> Settings:
env = os.environ.get("LANGCHAIN_ENV", "dev")
if env == "dev":
# Local dev: .env.dev file, values checked into 1Password not git
return Settings(_env_file=Path(".env.dev"))
if env == "staging":
# CI / staging: env vars injected by the orchestrator
# (GitHub Actions secrets, k8s envFrom: secretRef, etc.)
return Settings() # reads os.environ directly
if env == "prod":
# Prod: pull from Secret Manager into memory ONLY
values = pull_from_secret_manager()
return Settings(**values)
raise ValueError(f"unknown LANGCHAIN_ENV: {env!r}")
```
Three loaders, one class. Dev touches a file on disk. Staging inherits env
vars from the orchestrator — `envFrom: secretRef` is readable via
`docker exec env`, but the blast radius is bounded and rotation is weekly.
Prod is the P37 fix: `pull_from_secret_manager()` builds a dict and passes
kwargs to `Settings(...)`. Values land in the instance attribute and
**never touch `os.environ`**. A subprocess will not inherit them.
### Step 3 — Secret Manager pull (GCP example; AWS / Vault in reference)
```python
from google.cloud import secretmanager
def pull_from_secret_manager() -> dict[str, str]:
client = secretmanager.SecretManagerServiceClient()
project = os.environ["GCP_PROJECT_ID"]
secret_names = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "LANGSMITH_API_KEY"]
out: dict[str, str] = {}
for name in secret_names:
resource = f"projects/{project}/secrets/{name}/versions/latest"
response = client.access_secret_version(request={"name": resource})
out[name] = response.payload.data.decode("utf-8")
# Non-secret passthrough (model id, prompt hash, endpoints)
for key in ["LANGCHAIN_ENV", "LANGCHAIN_MODEL_ID", "LANGCHAIN_PROMPT_COMMIT",
"LANGCHAIN_VECTOR_INDEX", "LANGCHAIN_CHECKPOINTER_URL",
"OTEL_EXPORTER_OTLP_ENDPOINT"]:
if key in os.environ:
out[key] = os.environ[key]
return out
```
No `os.environ[k] = v` line. The dict goes straight into
`Settings(**values)`. Workload-identity IAM handles auth; no static key on
disk. For AWS / Vault see [Secret Manager Integration](references/secret-manager-integration.md).
### Step 4 — Per-env model and prompt pinning
Dev, staging, and prod run **different** model ids and **different** prompt
commit hashes. Pinning happens at env-var level so app code is env-agnostic
(see the Env Matrix below for values). One function reads
`settings.prompt_commit_hash` and pulls from LangSmith
(cross-ref `langchain-prompt-engineering`):
```python
from langsmitRelated in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.