mirror of
https://github.com/langchain-ai/langchain.git
synced 2026-10-05 09:25:14 +03:00
Adds a first-party `langchain-typesafe` partner package for making
structured decisions using the new and shiny `jev` model. It introduces
a new `TypeSafeClassifier` runnable while using the typesafe wire
protocol, message normalization, response parsing, and provider failures
behind a standard LangChain interface.
- Supports TypeSafe's `Choice`, `Noul`, and `Score` primitives with
typed answers, probabilities, confidence, usage metadata, and request
IDs.
- Resolves credentials and API configuration from explicit arguments or
`TYPESAFE_API_KEY` and `TYPESAFE_BASE_URL`.
- Supports injected `httpx2.Client` and `httpx2.AsyncClient` instances
for custom transports, proxies, test fixtures, and shared connection
pools (matching patterns of other provider packages in this repo)
---
There were a couple of rapid fire design decisions that I'll document
here to aid in review:
### Extending base `Runnable`
This is largely the impetus for this PR; we can use typesafe's native
python client, but we lose tracing if it isn't captured through the
runnable interface. I imagine because this is a different model we want
to attribute its costs and things in tracing (at a later point in time)
### Direct `httpx2` integration
This package implements the System One `POST /v1/systemone` contract
directly rather than wrapping `typesafe-sdk` (the status quo of other
integrations in langchain). This keeps the integration's public types
and behavior native, avoids adding a transitive dependency, and gives us
lower level controls which is harder to do when indirecting through
someone elses client.
Aspirationally this is a direction we've wanted to work towards for
integrations generally:
* The benefit of wrapping an external sdk is that we can stay tied to
the supply-chain of a package thats maintained first party (we don't
need to maintain types, the transitive dependency keeps capabilities up
to date, etc.)
* This was in a time when maintainer attention was limited, and the
focus wasn't on nitty gritty implementation details with a provider
* ^Agents are probably good enough to start shouldering most of the
burden we've encountered in the past
* There's an indirection tax we have to pay when we shove the
responsibility onto a separate package- it makes us harder to design
single abstractions around
Because this is a 0-1 integration, my intention is to trial typesafes'
implementation under a "langchain maintained client" paradigm to see if
this is something that could work more generally
### State typing and message normalization
I had to bifurcate the input state types into `State` and `_StateValue`
representations mostly to respect TypeSafe API invariants: input must be
a string, object, or array. Its private recursive `_StateValue`
representation permits ordinary scalars by itself and through
dicts/sequences.
I'm also adding langchain `BaseMessage` objects and `BaseMessage`
sequences at any nesting level. This is because messages are the most
common unit of context within langchain, and translating between agent
context and classifier context is a very important part of us plugging
jev into the ecosystem. I'm imagining something like this:
```python
class ClassifierMiddleware(AgentMiddleware[AgentState, ContextT, ResponseT]):
def after_model(self, state):
result = TypeSafeClassifier(...).invoke(state.messages)
# do something with results
```
This uses the `convert_to_openai_messages` utility exposed in core since
that is the most context friendly representation we have to pass
messages through to typesafe (which doesn't have any concept of an LLM
message)
### Typed request and response models
Question and answer models use Pydantic because they sit directly on
serialization and validation layers, and I was hoping to meet some of
the invariant criteria that the typesafe API has. Things like:
* request models requiring instructions for every question
* `Score` requiring at least two ordered levels
* rejecting empty model identifiers
Criteria remain JSON-capable rather than being narrowed to strings
because TypeSafe's advanced documentation supports structured
instructions and criteria, even though the compact HTTP reference
presents narrower examples.
(lmk if pydantic is defunct and we should pivot)
### Error handling infrastructure
I wanted to meet the same level of specification that the TypeSafe
Python SDK has with regards to how it represents errors. This package
exports a standard error reference (in `_client_utils.py`) that does
this while also inheriting from alngchains standard `Model*Error`
classes which we recently added. Callers can handle either
TypeSafe-specific metadata or provider-independent model failures.
The package mirrors the TypeSafe Python SDK's status, connection,
timeout, rate-limit, and response-validation exception names.
Status-specific provider errors also inherit from LangChain's standard
`Model*Error` classes, following the OpenAI integration pattern, so
callers can handle either TypeSafe-specific metadata or
provider-independent model failures.
API errors retain structured status, body, headers, request ID,
sanitized endpoint, response-validation field path, and rate-limit delay
metadata where applicable. TypeSafe's `529 Overloaded` response maps to
a retryable `ModelAPIError`, while `429` maps to `ModelRateLimitError`
and preserves `retry_after_ms`.
Automatic retry policy is intentionally outside the initial package
scope. The classified retryable errors and retry metadata provide the
foundation for a focused follow-up without coupling this integration to
a retry policy in its first release.
## Package infrastructure
- Introduces `langchain-typesafe` at version `0.0.1` with typed-package
markers, documentation, unit and integration-compilation coverage, and a
reproducible `uv` lockfile.
- Adds bounded `httpx2>=2.0.0,<3.0.0` and `langchain-core>=1.6.2,<2.0.0`
dependencies. The core minimum provides the standard `Model*Error`
hierarchy used by the provider exceptions.
- Registers the package with repository CI, release automation,
dependency updates, issue routing, and package labeling.
## Release note
Added the new `langchain-typesafe` integration package, including a
composable `TypeSafeClassifier` for typed classification, scoring, and
confidence-aware decisions with TypeSafe's Jev model.
---
This was developed with heavy assistance from Sol, earnestly reviewed by
me, and validated through similar CI patterns established in other
packages
412 lines
19 KiB
YAML
412 lines
19 KiB
YAML
# Routine integration tests against partner libraries with live API credentials.
|
|
#
|
|
# Uses `make integration_tests` within each library being tested.
|
|
#
|
|
# Runs daily with the option to trigger manually.
|
|
|
|
name: "⏰ Integration Tests"
|
|
run-name: "Run Integration Tests - ${{ inputs.working-directory-override || (inputs.working-directory != 'all' && inputs.working-directory) || (inputs.exclude != '' && format('exclude:{0}', inputs.exclude)) || 'all libs' }} (Python ${{ inputs.python-version-override || '3.10, 3.14' }})"
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
working-directory:
|
|
type: choice
|
|
description: "Library to test (select from dropdown)"
|
|
default: "all"
|
|
# Short names only — the `compute-matrix` job re-adds the `libs/` or
|
|
# `libs/partners/` prefix. When adding a new option, also update the
|
|
# `case` statement in `compute-matrix` if it isn't a partner package
|
|
# (partners are the default branch).
|
|
options:
|
|
- "all"
|
|
- "core"
|
|
- "langchain"
|
|
- "langchain_v1"
|
|
- "text-splitters"
|
|
- "standard-tests"
|
|
- "model-profiles"
|
|
- "anthropic"
|
|
- "aws"
|
|
- "chroma"
|
|
- "deepseek"
|
|
- "exa"
|
|
- "fireworks"
|
|
- "google-genai"
|
|
- "google-vertexai"
|
|
- "groq"
|
|
- "huggingface"
|
|
- "mistralai"
|
|
- "nomic"
|
|
- "ollama"
|
|
- "openai"
|
|
- "openrouter"
|
|
- "perplexity"
|
|
- "qdrant"
|
|
- "typesafe"
|
|
- "xai"
|
|
working-directory-override:
|
|
type: string
|
|
description: "Manual override — takes precedence over dropdown (e.g. libs/partners/partner-xyz)"
|
|
exclude:
|
|
type: string
|
|
description: "Comma-separated short names to drop from the 'all' run (e.g. openai,anthropic). Ignored unless dropdown is 'all' and no working-directory-override is set."
|
|
python-version-override:
|
|
type: string
|
|
description: "Python version override — defaults to 3.10 and 3.14 in matrix (e.g. 3.11)"
|
|
schedule:
|
|
- cron: "0 13 * * *" # Runs daily at 1PM UTC (9AM EDT/6AM PDT)
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
UV_FROZEN: "true"
|
|
DEFAULT_LIBS: >-
|
|
["libs/partners/openai",
|
|
"libs/partners/anthropic",
|
|
"libs/partners/fireworks",
|
|
"libs/partners/groq",
|
|
"libs/partners/mistralai",
|
|
"libs/partners/xai",
|
|
"libs/partners/google-vertexai",
|
|
"libs/partners/google-genai",
|
|
"libs/partners/aws"]
|
|
|
|
jobs:
|
|
# Generate dynamic test matrix based on input parameters or defaults
|
|
# Only runs on the main repo (for scheduled runs) or when manually triggered
|
|
compute-matrix:
|
|
# Defend against forks running scheduled jobs, but allow manual runs from forks
|
|
if: github.repository_owner == 'langchain-ai' || github.event_name != 'schedule'
|
|
|
|
runs-on: ubuntu-latest
|
|
name: "📋 Compute Test Matrix"
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
python-version-min-3-11: ${{ steps.set-matrix.outputs.python-version-min-3-11 }}
|
|
steps:
|
|
- name: "🔢 Generate Python & Library Matrix"
|
|
id: set-matrix
|
|
env:
|
|
DEFAULT_LIBS: ${{ env.DEFAULT_LIBS }}
|
|
WORKING_DIRECTORY_OVERRIDE: ${{ github.event.inputs.working-directory-override || '' }}
|
|
WORKING_DIRECTORY_CHOICE: ${{ github.event.inputs.working-directory || 'all' }}
|
|
PYTHON_VERSION_OVERRIDE: ${{ github.event.inputs.python-version-override || '' }}
|
|
EXCLUDE: ${{ github.event.inputs.exclude || '' }}
|
|
run: |
|
|
# echo "matrix=..." where matrix is a json formatted str with keys python-version and working-directory
|
|
# python-version defaults to 3.10 and 3.14, overridden to [PYTHON_VERSION_OVERRIDE] if set
|
|
# working-directory priority: override string > dropdown choice > DEFAULT_LIBS
|
|
python_version='["3.10", "3.14"]'
|
|
python_version_min_3_11='["3.11", "3.14"]'
|
|
working_directory="$DEFAULT_LIBS"
|
|
if [ -n "$PYTHON_VERSION_OVERRIDE" ]; then
|
|
python_version="[\"$PYTHON_VERSION_OVERRIDE\"]"
|
|
# Bound override version to >= 3.11 for packages requiring it
|
|
if [ "$(echo "$PYTHON_VERSION_OVERRIDE >= 3.11" | bc -l)" -eq 1 ]; then
|
|
python_version_min_3_11="[\"$PYTHON_VERSION_OVERRIDE\"]"
|
|
else
|
|
python_version_min_3_11='["3.11"]'
|
|
fi
|
|
fi
|
|
if [ -n "$WORKING_DIRECTORY_OVERRIDE" ]; then
|
|
working_directory="[\"$WORKING_DIRECTORY_OVERRIDE\"]"
|
|
elif [ "$WORKING_DIRECTORY_CHOICE" != "all" ]; then
|
|
# Map short dropdown name back to full path
|
|
case "$WORKING_DIRECTORY_CHOICE" in
|
|
core|langchain|langchain_v1|text-splitters|standard-tests|model-profiles)
|
|
working_directory="[\"libs/$WORKING_DIRECTORY_CHOICE\"]"
|
|
;;
|
|
*)
|
|
working_directory="[\"libs/partners/$WORKING_DIRECTORY_CHOICE\"]"
|
|
;;
|
|
esac
|
|
elif [ -n "$EXCLUDE" ]; then
|
|
# Only honored on the 'all' run (no override, dropdown left at 'all').
|
|
# Map each comma-separated short name to its full path (mirroring the
|
|
# case statement above), then subtract from the DEFAULT_LIBS array.
|
|
exclude_paths='[]'
|
|
IFS=',' read -ra exclude_names <<< "$EXCLUDE"
|
|
for name in "${exclude_names[@]}"; do
|
|
# Trim surrounding whitespace so "openai, anthropic" works.
|
|
name="${name#"${name%%[![:space:]]*}"}" # ltrim
|
|
name="${name%"${name##*[![:space:]]}"}" # rtrim
|
|
[ -z "$name" ] && continue
|
|
case "$name" in
|
|
core|langchain|langchain_v1|text-splitters|standard-tests|model-profiles)
|
|
path="libs/$name"
|
|
;;
|
|
*)
|
|
path="libs/partners/$name"
|
|
;;
|
|
esac
|
|
exclude_paths="$(jq -nc --argjson acc "$exclude_paths" --arg path "$path" '$acc + [$path]')"
|
|
done
|
|
working_directory="$(jq -nc --argjson libs "$working_directory" --argjson excl "$exclude_paths" '$libs - $excl')"
|
|
fi
|
|
matrix="{\"python-version\": $python_version, \"working-directory\": $working_directory}"
|
|
echo "$matrix"
|
|
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
|
echo "python-version-min-3-11=$python_version_min_3_11" >> $GITHUB_OUTPUT
|
|
|
|
# Run integration tests against partner libraries with live API credentials
|
|
integration-tests:
|
|
if: github.repository_owner == 'langchain-ai' || github.event_name != 'schedule'
|
|
name: "🐍 Python ${{ matrix.python-version }}: ${{ matrix.working-directory }}"
|
|
runs-on: ubuntu-latest
|
|
# Scopes LangSmith tracing credentials (and any other env-scoped secrets)
|
|
environment: "Scheduled testing"
|
|
needs: [compute-matrix]
|
|
timeout-minutes: 30
|
|
# Serialize same-package shards across workflow runs so a per-package
|
|
# manual dispatch doesn't race the scheduled "all libs" run against the
|
|
# same live API credentials. Keyed per (working-directory, python-version)
|
|
# so the 3.10/3.14 matrix legs within one run still execute in parallel.
|
|
concurrency:
|
|
group: integration-tests-${{ matrix.working-directory }}-${{ matrix.python-version }}
|
|
cancel-in-progress: false
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
python-version: ${{ fromJSON(needs.compute-matrix.outputs.matrix).python-version }}
|
|
working-directory: ${{ fromJSON(needs.compute-matrix.outputs.matrix).working-directory }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
|
|
with:
|
|
path: langchain
|
|
|
|
# These libraries exist outside of the monorepo and need to be checked out separately
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
|
|
with:
|
|
repository: langchain-ai/langchain-google
|
|
path: langchain-google
|
|
- name: "🔐 Authenticate to Google Cloud"
|
|
id: "auth"
|
|
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
|
|
with:
|
|
credentials_json: "${{ secrets.GOOGLE_CREDENTIALS }}"
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
|
|
with:
|
|
repository: langchain-ai/langchain-aws
|
|
path: langchain-aws
|
|
- name: "🔐 Configure AWS Credentials"
|
|
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6
|
|
with:
|
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
aws-region: ${{ secrets.AWS_REGION }}
|
|
- name: "📦 Organize External Libraries"
|
|
run: |
|
|
rm -rf \
|
|
langchain/libs/partners/google-genai \
|
|
langchain/libs/partners/google-vertexai
|
|
mv langchain-google/libs/genai langchain/libs/partners/google-genai
|
|
mv langchain-google/libs/vertexai langchain/libs/partners/google-vertexai
|
|
mv langchain-aws/libs/aws langchain/libs/partners/aws
|
|
|
|
- name: "🐍 Set up Python ${{ matrix.python-version }} + UV"
|
|
uses: "./langchain/.github/actions/uv_setup"
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: "📦 Install Dependencies"
|
|
# Partner packages use [tool.uv.sources] in their pyproject.toml to resolve
|
|
# langchain-core/langchain to local editable installs, so `uv sync` automatically
|
|
# tests against the versions from the current branch (not published releases).
|
|
#
|
|
# External google/aws packages live in separate repos and don't declare
|
|
# [tool.uv.sources], so `uv sync` pulls langchain-* from PyPI. Overlay
|
|
# local editable installs after sync so integration tests exercise the
|
|
# current branch's langchain code. Matches the pattern used by the
|
|
# `test-dependents` job below for deepagents.
|
|
run: |
|
|
echo "Running scheduled tests, installing dependencies with uv..."
|
|
cd langchain/${{ matrix.working-directory }}
|
|
uv sync --group test --group test_integration
|
|
|
|
case "${{ matrix.working-directory }}" in
|
|
libs/partners/google-genai)
|
|
uv pip install \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/core \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/standard-tests
|
|
;;
|
|
libs/partners/google-vertexai)
|
|
uv pip install \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/core \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/langchain_v1 \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/standard-tests
|
|
;;
|
|
libs/partners/aws)
|
|
uv pip install \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/core \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/langchain_v1 \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/langchain \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/standard-tests \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/partners/anthropic
|
|
;;
|
|
esac
|
|
|
|
- name: "🧾 Build LangSmith Metadata"
|
|
# GHA expression values flow through intermediate env vars (injection
|
|
# hardening) and jq -nc builds the JSON, so quotes/newlines in any
|
|
# field can't corrupt the payload.
|
|
env:
|
|
GH_SHA: ${{ github.sha }}
|
|
GH_RUN_ID: ${{ github.run_id }}
|
|
GH_RUN_ATTEMPT: ${{ github.run_attempt }}
|
|
GH_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
GH_WORKFLOW: ${{ github.workflow }}
|
|
GH_EVENT: ${{ github.event_name }}
|
|
GH_REF: ${{ github.ref }}
|
|
WORKING_DIRECTORY: ${{ matrix.working-directory }}
|
|
PYTHON_VERSION: ${{ matrix.python-version }}
|
|
run: |
|
|
metadata=$(jq -nc \
|
|
--arg github_sha "$GH_SHA" \
|
|
--arg github_run_id "$GH_RUN_ID" \
|
|
--arg github_run_attempt "$GH_RUN_ATTEMPT" \
|
|
--arg github_run_url "$GH_RUN_URL" \
|
|
--arg github_workflow "$GH_WORKFLOW" \
|
|
--arg github_event "$GH_EVENT" \
|
|
--arg github_ref "$GH_REF" \
|
|
--arg working_directory "$WORKING_DIRECTORY" \
|
|
--arg python_version "$PYTHON_VERSION" \
|
|
'{github_sha: $github_sha, github_run_id: $github_run_id, github_run_attempt: $github_run_attempt, github_run_url: $github_run_url, github_workflow: $github_workflow, github_event: $github_event, github_ref: $github_ref, working_directory: $working_directory, python_version: $python_version}')
|
|
echo "LANGSMITH_METADATA=$metadata" >> "$GITHUB_ENV"
|
|
|
|
- name: "🚀 Run Integration Tests"
|
|
# WARNING: All secrets below are available to every matrix job regardless of
|
|
# which package is being tested. This is intentional for simplicity, but means
|
|
# any test file could technically access any key. Only use for trusted code.
|
|
env:
|
|
LANGCHAIN_TESTS_USER_AGENT: ${{ secrets.LANGCHAIN_TESTS_USER_AGENT }}
|
|
|
|
# Route traces to one project with GitHub run metadata so failures link back to the originating Actions run.
|
|
LANGSMITH_TRACING: "true"
|
|
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
|
LANGSMITH_PROJECT: ${{ vars.LANGSMITH_PROJECT || 'scheduled-testing-py' }}
|
|
LANGSMITH_TAGS: "github-actions,${{ matrix.working-directory }},python-${{ matrix.python-version }},sha-${{ github.sha }}"
|
|
|
|
LANGSMITH_GATEWAY: ${{ secrets.LANGSMITH_GATEWAY }}
|
|
LANGSMITH_GATEWAY_API_KEY: ${{ secrets.LANGSMITH_GATEWAY_API_KEY }}
|
|
AI21_API_KEY: ${{ secrets.AI21_API_KEY }}
|
|
ANTHROPIC_FILES_API_IMAGE_ID: ${{ secrets.ANTHROPIC_FILES_API_IMAGE_ID }}
|
|
ANTHROPIC_FILES_API_PDF_ID: ${{ secrets.ANTHROPIC_FILES_API_PDF_ID }}
|
|
ASTRA_DB_API_ENDPOINT: ${{ secrets.ASTRA_DB_API_ENDPOINT }}
|
|
ASTRA_DB_APPLICATION_TOKEN: ${{ secrets.ASTRA_DB_APPLICATION_TOKEN }}
|
|
ASTRA_DB_KEYSPACE: ${{ secrets.ASTRA_DB_KEYSPACE }}
|
|
AZURE_OPENAI_API_VERSION: ${{ secrets.AZURE_OPENAI_API_VERSION }}
|
|
AZURE_OPENAI_API_BASE: ${{ secrets.AZURE_OPENAI_API_BASE }}
|
|
AZURE_OPENAI_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
|
|
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_CHAT_DEPLOYMENT_NAME }}
|
|
AZURE_OPENAI_LEGACY_CHAT_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_LEGACY_CHAT_DEPLOYMENT_NAME }}
|
|
AZURE_OPENAI_LLM_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_LLM_DEPLOYMENT_NAME }}
|
|
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME: ${{ secrets.AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME }}
|
|
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
|
|
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
ES_URL: ${{ secrets.ES_URL }}
|
|
ES_CLOUD_ID: ${{ secrets.ES_CLOUD_ID }}
|
|
ES_API_KEY: ${{ secrets.ES_API_KEY }}
|
|
EXA_API_KEY: ${{ secrets.EXA_API_KEY }}
|
|
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
|
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
|
GOOGLE_SEARCH_API_KEY: ${{ secrets.GOOGLE_SEARCH_API_KEY }}
|
|
GOOGLE_CSE_ID: ${{ secrets.GOOGLE_CSE_ID }}
|
|
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
|
HUGGINGFACEHUB_API_TOKEN: ${{ secrets.HUGGINGFACEHUB_API_TOKEN }}
|
|
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
|
MONGODB_ATLAS_URI: ${{ secrets.MONGODB_ATLAS_URI }}
|
|
NOMIC_API_KEY: ${{ secrets.NOMIC_API_KEY }}
|
|
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
|
|
OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }}
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
PPLX_API_KEY: ${{ secrets.PPLX_API_KEY }}
|
|
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
|
|
TYPESAFE_API_KEY: ${{ secrets.TYPESAFE_API_KEY }}
|
|
UPSTAGE_API_KEY: ${{ secrets.UPSTAGE_API_KEY }}
|
|
WATSONX_APIKEY: ${{ secrets.WATSONX_APIKEY }}
|
|
WATSONX_PROJECT_ID: ${{ secrets.WATSONX_PROJECT_ID }}
|
|
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
run: |
|
|
cd langchain/${{ matrix.working-directory }}
|
|
make integration_tests
|
|
|
|
- name: "🧹 Clean up External Libraries"
|
|
# Clean up external libraries to avoid affecting the following git status check
|
|
run: |
|
|
rm -rf \
|
|
langchain/libs/partners/google-genai \
|
|
langchain/libs/partners/google-vertexai \
|
|
langchain/libs/partners/aws
|
|
|
|
- name: "🧹 Verify Clean Working Directory"
|
|
working-directory: langchain
|
|
run: |
|
|
set -eu
|
|
|
|
STATUS="$(git status)"
|
|
echo "$STATUS"
|
|
|
|
# grep will exit non-zero if the target message isn't found,
|
|
# and `set -e` above will cause the step to fail.
|
|
echo "$STATUS" | grep 'nothing to commit, working tree clean'
|
|
|
|
# Test dependent packages against local packages to catch breaking changes
|
|
test-dependents:
|
|
# Defend against forks running scheduled jobs, but allow manual runs from forks
|
|
if: github.repository_owner == 'langchain-ai' || github.event_name != 'schedule'
|
|
|
|
name: "🐍 Python ${{ matrix.python-version }}: ${{ matrix.package.path }}"
|
|
runs-on: ubuntu-latest
|
|
needs: [compute-matrix]
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# deepagents requires Python >= 3.11, use bounded version from compute-matrix
|
|
python-version: ${{ fromJSON(needs.compute-matrix.outputs.python-version-min-3-11) }}
|
|
package:
|
|
- name: deepagents
|
|
repo: langchain-ai/deepagents
|
|
path: libs/deepagents
|
|
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
|
|
with:
|
|
path: langchain
|
|
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
|
|
with:
|
|
repository: ${{ matrix.package.repo }}
|
|
path: ${{ matrix.package.name }}
|
|
|
|
- name: "🐍 Set up Python ${{ matrix.python-version }} + UV"
|
|
uses: "./langchain/.github/actions/uv_setup"
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: "📦 Install ${{ matrix.package.name }} with Local"
|
|
# Unlike partner packages (which use [tool.uv.sources] for local resolution),
|
|
# external dependents live in separate repos and need explicit overrides to
|
|
# test against the langchain versions from the current branch, as their
|
|
# pyproject.toml files point to released versions.
|
|
run: |
|
|
cd ${{ matrix.package.name }}/${{ matrix.package.path }}
|
|
|
|
# Install the package with test dependencies
|
|
uv sync --group test
|
|
|
|
# Override langchain packages with local versions
|
|
uv pip install \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/core \
|
|
-e $GITHUB_WORKSPACE/langchain/libs/langchain_v1
|
|
|
|
# No API keys needed for now - deepagents `make test` only runs unit tests
|
|
- name: "🚀 Run ${{ matrix.package.name }} Tests"
|
|
run: |
|
|
cd ${{ matrix.package.name }}/${{ matrix.package.path }}
|
|
make test
|