mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-19 06:05:44 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d503c0bf33 | ||
|
|
25ba4c3bda | ||
|
|
dfc1c59ebf | ||
|
|
6f4c5fefee | ||
|
|
7cf230defa | ||
|
|
5db65e0281 | ||
|
|
b08c2e092f |
@@ -87,3 +87,18 @@ jobs:
|
||||
working-directory: libs/cli/js-examples
|
||||
run: |
|
||||
langgraph build -t langgraph-test-e
|
||||
|
||||
- name: Build JS monorepo service
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/js-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-f -c apps/agent/langgraph.json --build-command "yarn run turbo build" --install-command "yarn install"
|
||||
|
||||
- name: Build Python monorepo service
|
||||
if: steps.changed-files.outputs.all
|
||||
working-directory: libs/cli/python-monorepo-example
|
||||
run: |
|
||||
langgraph build -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
cp apps/agent/.env.example apps/agent/.env
|
||||
if [ -n "${{ secrets.LANGSMITH_API_KEY }}" ]; then echo "LANGSMITH_API_KEY=${{ secrets.LANGSMITH_API_KEY }}" >> apps/agent/.env; fi
|
||||
timeout 60 python ../../../.github/scripts/run_langgraph_cli_test.py -t langgraph-test-g -c apps/agent/langgraph.json
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
"name": "Store",
|
||||
"description": "Store is an API for managing persistent key-value store (long-term memory) that is available from any thread."
|
||||
},
|
||||
{
|
||||
"name": "A2A",
|
||||
"description": "Agent-to-Agent Protocol related endpoints for exposing assistants as A2A-compliant agents."
|
||||
},
|
||||
{
|
||||
"name": "MCP",
|
||||
"description": "Model Context Protocol related endpoints for exposing an agent as an MCP server."
|
||||
@@ -1550,6 +1554,29 @@
|
||||
},
|
||||
"name": "Last-Event-ID",
|
||||
"in": "header"
|
||||
},
|
||||
{
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["lifecycle", "run_modes", "state_update"]
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["lifecycle", "run_modes", "state_update"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": ["run_modes"],
|
||||
"title": "Stream Modes",
|
||||
"description": "Stream modes to control which events are returned. 'lifecycle' returns only run start/end events, 'run_modes' returns all run events (default behavior), 'state_update' returns only state update events."
|
||||
},
|
||||
"name": "stream_modes",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -3159,6 +3186,195 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/a2a/{assistant_id}": {
|
||||
"post": {
|
||||
"operationId": "post_a2a",
|
||||
"summary": "A2A Post",
|
||||
"description": "Communicate with an assistant using the Agent-to-Agent Protocol.\nSends a JSON-RPC 2.0 message to the assistant.\n\n- **Request**: Provide an object with `jsonrpc`, `id`, `method`, and optional `params`.\n- **Response**: Returns a JSON-RPC response with task information or error.\n\n**Supported Methods:**\n- `message/send`: Send a message to the assistant\n- `tasks/get`: Get the status and result of a task\n\n**Notes:**\n- Supports threaded conversations via thread context\n- Messages can contain text and data parts\n- Tasks run asynchronously and return completion status\n",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "assistant_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"description": "The ID of the assistant to communicate with"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["application/json"]
|
||||
},
|
||||
"description": "Must be application/json"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jsonrpc": {
|
||||
"type": "string",
|
||||
"enum": ["2.0"],
|
||||
"description": "JSON-RPC version"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Request identifier"
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"enum": ["message/send", "tasks/get"],
|
||||
"description": "The method to invoke"
|
||||
},
|
||||
"params": {
|
||||
"type": "object",
|
||||
"description": "Method parameters",
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Message Send Parameters",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"enum": ["user", "assistant"],
|
||||
"description": "Message role"
|
||||
},
|
||||
"parts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Text Part",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["text"]
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "text"]
|
||||
},
|
||||
{
|
||||
"title": "Data Part",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["data"]
|
||||
},
|
||||
"data": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "data"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Message parts"
|
||||
},
|
||||
"messageId": {
|
||||
"type": "string",
|
||||
"description": "Unique message identifier"
|
||||
}
|
||||
},
|
||||
"required": ["role", "parts", "messageId"]
|
||||
},
|
||||
"thread": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"threadId": {
|
||||
"type": "string",
|
||||
"description": "Thread identifier for conversation context"
|
||||
}
|
||||
},
|
||||
"description": "Optional thread context"
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
},
|
||||
{
|
||||
"title": "Task Get Parameters",
|
||||
"properties": {
|
||||
"taskId": {
|
||||
"type": "string",
|
||||
"description": "Task identifier to retrieve"
|
||||
}
|
||||
},
|
||||
"required": ["taskId"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["jsonrpc", "id", "method"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "JSON-RPC response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"jsonrpc": {
|
||||
"type": "string",
|
||||
"enum": ["2.0"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"result": {
|
||||
"type": "object",
|
||||
"description": "Success result containing task information or task details"
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Error information if request failed"
|
||||
}
|
||||
},
|
||||
"required": ["jsonrpc", "id"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request - invalid JSON-RPC or missing Accept header"
|
||||
},
|
||||
"404": {
|
||||
"description": "Assistant not found"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"A2A"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/mcp/": {
|
||||
"post": {
|
||||
"operationId": "post_mcp",
|
||||
@@ -4413,6 +4629,17 @@
|
||||
"title": "Checkpoint During",
|
||||
"description": "Whether to checkpoint during the run.",
|
||||
"default": false
|
||||
},
|
||||
"durability": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sync",
|
||||
"async",
|
||||
"exit"
|
||||
],
|
||||
"title": "Durability",
|
||||
"description": "Durability level for the run. Must be one of 'sync', 'async', or 'exit'.",
|
||||
"default": "async"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4649,6 +4876,17 @@
|
||||
"title": "Checkpoint During",
|
||||
"description": "Whether to checkpoint during the run.",
|
||||
"default": false
|
||||
},
|
||||
"durability": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sync",
|
||||
"async",
|
||||
"exit"
|
||||
],
|
||||
"title": "Durability",
|
||||
"description": "Durability level for the run. Must be one of 'sync', 'async', or 'exit'.",
|
||||
"default": "async"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4777,6 +5015,12 @@
|
||||
},
|
||||
"ThreadSearchRequest": {
|
||||
"properties": {
|
||||
"ids": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "format": "uuid"},
|
||||
"title": "Ids",
|
||||
"description": "List of thread IDs to include. Others are excluded."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"title": "Metadata",
|
||||
@@ -5017,11 +5261,30 @@
|
||||
"type": "object",
|
||||
"title": "Metadata",
|
||||
"description": "Metadata to merge with existing thread metadata."
|
||||
},
|
||||
"ttl": {
|
||||
"type": "object",
|
||||
"title": "TTL",
|
||||
"description": "The time-to-live for the thread.",
|
||||
"properties": {
|
||||
"strategy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"delete"
|
||||
],
|
||||
"description": "The TTL strategy. 'delete' removes the entire thread.",
|
||||
"default": "delete"
|
||||
},
|
||||
"ttl": {
|
||||
"type": "number",
|
||||
"description": "The time-to-live in minutes from now until thread should be swept."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "ThreadPatch",
|
||||
"description": "Payload for creating a thread."
|
||||
"description": "Payload for updating a thread."
|
||||
},
|
||||
"ThreadStateCheckpointRequest": {
|
||||
"properties": {
|
||||
|
||||
@@ -483,19 +483,19 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt langchain_community langchain_anthropic langchain_openai wikipedia scikit-learn
|
||||
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \
|
||||
for line in '[project]' \
|
||||
'name = "graphs"' \
|
||||
'version = "0.1"' \
|
||||
'[tool.setuptools.package-data]' \
|
||||
'"*" = ["**/*"]'; do \
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \
|
||||
done
|
||||
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-graphs/src/agent.py:graph", "storm": "/deps/outer-graphs/src/storm.py:graph"}'
|
||||
```
|
||||
|
||||
???+ note "Updating your langgraph.json file"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"prettier",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
parser: "@typescript-eslint/parser",
|
||||
project: "./tsconfig.json",
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["import", "@typescript-eslint", "no-instanceof"],
|
||||
ignorePatterns: [
|
||||
".eslintrc.cjs",
|
||||
"scripts",
|
||||
"src/utils/lodash/*",
|
||||
"node_modules",
|
||||
"dist",
|
||||
"dist-cjs",
|
||||
"*.js",
|
||||
"*.cjs",
|
||||
"*.d.ts",
|
||||
],
|
||||
rules: {
|
||||
"no-process-env": 2,
|
||||
"no-instanceof/no-instanceof": 2,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-shadow": 0,
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/no-use-before-define": ["error", "nofunc"],
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { args: "none" }],
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
camelcase: 0,
|
||||
"class-methods-use-this": 0,
|
||||
"import/extensions": [2, "ignorePackages"],
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{ devDependencies: ["**/*.test.ts"] },
|
||||
],
|
||||
"import/no-unresolved": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"keyword-spacing": "error",
|
||||
"max-classes-per-file": 0,
|
||||
"max-len": 0,
|
||||
"no-await-in-loop": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-console": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"no-shadow": 0,
|
||||
"no-continue": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-useless-constructor": 0,
|
||||
"no-return-await": 0,
|
||||
"consistent-return": 0,
|
||||
"no-else-return": 0,
|
||||
"new-cap": ["error", { properties: false, capIsNew: false }],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"node_version": "20",
|
||||
"graphs": {
|
||||
"agent": "./src/graph.ts:graph"
|
||||
},
|
||||
"env": "../../.env"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/agent",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "src/graph.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@js-monorepo-example/shared": "*",
|
||||
"@langchain/core": "^0.3.2",
|
||||
"@langchain/langgraph": "^0.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Simple LangGraph.js example for monorepo testing
|
||||
*/
|
||||
import { StateGraph } from "@langchain/langgraph";
|
||||
import { RunnableConfig } from "@langchain/core/runnables";
|
||||
import { StateAnnotation } from "./state.js";
|
||||
import { getGreeting } from "@js-monorepo-example/shared";
|
||||
|
||||
/**
|
||||
* Simple node that uses the shared library
|
||||
*/
|
||||
const callModel = async (
|
||||
state: typeof StateAnnotation.State,
|
||||
_config: RunnableConfig,
|
||||
): Promise<typeof StateAnnotation.Update> => {
|
||||
// Use functions from the shared library
|
||||
const greeting = getGreeting();
|
||||
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: `${greeting}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple routing function
|
||||
*/
|
||||
export const route = (
|
||||
state: typeof StateAnnotation.State,
|
||||
): "__end__" | "callModel" => {
|
||||
if (state.messages.length > 0) {
|
||||
return "__end__";
|
||||
}
|
||||
return "callModel";
|
||||
};
|
||||
|
||||
// Create the graph
|
||||
const builder = new StateGraph(StateAnnotation)
|
||||
.addNode("callModel", callModel)
|
||||
.addEdge("__start__", "callModel")
|
||||
.addConditionalEdges("callModel", route);
|
||||
|
||||
export const graph = builder.compile();
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
|
||||
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
|
||||
|
||||
/**
|
||||
* Simple state annotation for the agent
|
||||
*/
|
||||
export const StateAnnotation = Annotation.Root({
|
||||
/**
|
||||
* Messages track the primary execution state of the agent.
|
||||
*/
|
||||
messages: Annotation<BaseMessage[], BaseMessageLike[]>({
|
||||
reducer: messagesStateReducer,
|
||||
default: () => [],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@js-monorepo-example/shared",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Simple utility functions for monorepo testing
|
||||
*/
|
||||
export function getGreeting(): string {
|
||||
return "Hello from shared library!";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "js-monorepo-example",
|
||||
"version": "0.0.1",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"description": "A simple monorepo example for LangGraph integration testing.",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"libs/*",
|
||||
"apps/*"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "turbo build",
|
||||
"clean": "turbo clean",
|
||||
"test": "turbo test",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint 'apps/**/*.ts' 'libs/**/*.ts'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "^2.5.0",
|
||||
"typescript": "^5.3.3",
|
||||
"@tsconfig/recommended": "^1.0.7",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.9.1",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-no-instanceof": "^1.0.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"prettier": "^3.3.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@tsconfig/recommended",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["apps/**/*", "libs/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"clean": {
|
||||
"dependsOn": ["^clean"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["^test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
__version__ = "0.4.0"
|
||||
__version__ = "0.4.1"
|
||||
|
||||
@@ -303,6 +303,8 @@ def _build(
|
||||
pull: bool,
|
||||
tag: str,
|
||||
passthrough: Sequence[str] = (),
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
):
|
||||
# pull latest images
|
||||
if pull:
|
||||
@@ -322,22 +324,38 @@ def _build(
|
||||
"-t",
|
||||
tag,
|
||||
]
|
||||
# determine build context: use current directory for JS projects, config parent for Python
|
||||
is_js_project = config_json.get("node_version") and not config_json.get(
|
||||
"python_version"
|
||||
)
|
||||
# build/install commands only apply to JS projects for now
|
||||
# without install/build command, JS projects will follow the old behavior
|
||||
if is_js_project and (build_command or install_command):
|
||||
build_context = str(pathlib.Path.cwd())
|
||||
else:
|
||||
build_context = str(config.parent)
|
||||
|
||||
# apply config
|
||||
stdin, additional_contexts = langgraph_cli.config.config_to_docker(
|
||||
config, config_json, base_image, api_version
|
||||
config,
|
||||
config_json,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
# add additional_contexts
|
||||
if additional_contexts:
|
||||
for k, v in additional_contexts.items():
|
||||
args.extend(["--build-context", f"{k}={v}"])
|
||||
# run docker build
|
||||
runner.run(
|
||||
subp_exec(
|
||||
"docker",
|
||||
"build",
|
||||
*args,
|
||||
*passthrough,
|
||||
str(config.parent),
|
||||
build_context,
|
||||
input=stdin,
|
||||
verbose=True,
|
||||
)
|
||||
@@ -366,6 +384,14 @@ def _build(
|
||||
"\n --base-image langchain/langgraph-server:0.2 # Pin to a minor version (Python)",
|
||||
)
|
||||
@OPT_API_VERSION
|
||||
@click.option(
|
||||
"--install-command",
|
||||
help="Custom install command to run from the build context root. If not provided, auto-detects based on package manager files.",
|
||||
)
|
||||
@click.option(
|
||||
"--build-command",
|
||||
help="Custom build command to run from the langgraph.json directory. If not provided, uses default build process.",
|
||||
)
|
||||
@click.argument("docker_build_args", nargs=-1, type=click.UNPROCESSED)
|
||||
@cli.command(
|
||||
help="📦 Build LangGraph API server Docker image.",
|
||||
@@ -381,6 +407,8 @@ def build(
|
||||
api_version: Optional[str],
|
||||
pull: bool,
|
||||
tag: str,
|
||||
install_command: Optional[str],
|
||||
build_command: Optional[str],
|
||||
):
|
||||
with Runner() as runner, Progress(message="Pulling...") as set:
|
||||
if shutil.which("docker") is None:
|
||||
@@ -397,6 +425,8 @@ def build(
|
||||
pull,
|
||||
tag,
|
||||
docker_build_args,
|
||||
install_command,
|
||||
build_command,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -913,10 +913,10 @@ def _assemble_local_deps(config_path: pathlib.Path, config: Config) -> LocalDeps
|
||||
"Rename the directory to use it as flat-layout package."
|
||||
)
|
||||
check_reserved(resolved.name, local_dep)
|
||||
container_path = f"/deps/__outer_{resolved.name}/{resolved.name}"
|
||||
container_path = f"/deps/outer-{resolved.name}/{resolved.name}"
|
||||
else:
|
||||
# src layout
|
||||
container_path = f"/deps/__outer_{resolved.name}/src"
|
||||
container_path = f"/deps/outer-{resolved.name}/src"
|
||||
for file in files:
|
||||
rfile = resolved / file
|
||||
if (
|
||||
@@ -1286,7 +1286,7 @@ def python_config_to_docker(
|
||||
if local_deps.pip_reqs:
|
||||
pip_reqs_str = os.linesep.join(
|
||||
(
|
||||
f"COPY --from=__outer_{reqpath.name} requirements.txt {destpath}"
|
||||
f"COPY --from=outer-{reqpath.name} requirements.txt {destpath}"
|
||||
if reqpath.parent in local_deps.additional_contexts
|
||||
else f"ADD {reqpath.relative_to(config_path.parent)} {destpath}"
|
||||
)
|
||||
@@ -1305,7 +1305,7 @@ def python_config_to_docker(
|
||||
faux_pkgs_str = f"{os.linesep}{os.linesep}".join(
|
||||
(
|
||||
f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
COPY --from=__outer_{fullpath.name} . {destpath}"""
|
||||
COPY --from=outer-{fullpath.name} . {destpath}"""
|
||||
if fullpath in local_deps.additional_contexts
|
||||
else f"""# -- Adding non-package dependency {fullpath.name} --
|
||||
ADD {relpath} {destpath}"""
|
||||
@@ -1320,7 +1320,7 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_{fullpath.name}/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-{fullpath.name}/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency {fullpath.name} --"""
|
||||
for fullpath, (relpath, destpath) in local_deps.faux_pkgs.items()
|
||||
@@ -1423,7 +1423,7 @@ ADD {relpath} /deps/{name}
|
||||
if p in local_deps.real_pkgs:
|
||||
name = local_deps.real_pkgs[p][1]
|
||||
elif p in local_deps.faux_pkgs:
|
||||
name = f"__outer_{p.name}"
|
||||
name = f"outer-{p.name}"
|
||||
else:
|
||||
raise RuntimeError(f"Unknown additional context: {p}")
|
||||
additional_contexts[name] = str(p)
|
||||
@@ -1436,9 +1436,28 @@ def node_config_to_docker(
|
||||
config: Config,
|
||||
base_image: str,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
# Calculate paths for monorepo support
|
||||
if build_context:
|
||||
relative_workdir = _calculate_relative_workdir(config_path, build_context)
|
||||
container_name = pathlib.Path(build_context).name
|
||||
if relative_workdir:
|
||||
faux_path = f"/deps/{container_name}/{relative_workdir}"
|
||||
else:
|
||||
faux_path = f"/deps/{container_name}"
|
||||
else:
|
||||
# Backward compatibility: use the original behavior
|
||||
faux_path = f"/deps/{config_path.parent.name}"
|
||||
|
||||
# Use custom install command or auto-detect
|
||||
if install_command:
|
||||
install_cmd = install_command
|
||||
else:
|
||||
install_cmd = _get_node_pm_install_cmd(config_path, config)
|
||||
|
||||
image_str = docker_tag(config, base_image, api_version)
|
||||
|
||||
env_vars: list[str] = []
|
||||
@@ -1465,20 +1484,35 @@ def node_config_to_docker(
|
||||
|
||||
env_vars.append(f"ENV LANGSERVE_GRAPHS='{json.dumps(config['graphs'])}'")
|
||||
|
||||
# For monorepo support, we need to handle install and build commands differently
|
||||
if build_context:
|
||||
# Monorepo case: install from root, build from config directory
|
||||
container_root = f"/deps/{pathlib.Path(build_context).name}"
|
||||
install_step = f"RUN cd {container_root} && {install_cmd}"
|
||||
|
||||
if build_command:
|
||||
build_step = f"RUN cd {faux_path} && {build_command}"
|
||||
else:
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
else:
|
||||
# Original behavior: everything happens in the same directory
|
||||
install_step = f"RUN cd {faux_path} && {install_cmd}"
|
||||
build_step = 'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts'
|
||||
|
||||
docker_file_contents = [
|
||||
f"FROM {image_str}",
|
||||
"",
|
||||
os.linesep.join(config["dockerfile_lines"]),
|
||||
"",
|
||||
f"ADD . {faux_path}",
|
||||
f"ADD . {faux_path if not build_context else container_root}",
|
||||
"",
|
||||
f"RUN cd {faux_path} && {install_cmd}",
|
||||
install_step,
|
||||
"",
|
||||
os.linesep.join(env_vars),
|
||||
"",
|
||||
f"WORKDIR {faux_path}",
|
||||
"",
|
||||
'RUN (test ! -f /api/langgraph_api/js/build.mts && echo "Prebuild script not found, skipping") || tsx /api/langgraph_api/js/build.mts',
|
||||
build_step,
|
||||
]
|
||||
|
||||
return os.linesep.join(docker_file_contents), {}
|
||||
@@ -1526,16 +1560,42 @@ def docker_tag(
|
||||
return f"{base_image}:{full_tag}"
|
||||
|
||||
|
||||
def _calculate_relative_workdir(config_path: pathlib.Path, build_context: str) -> str:
|
||||
"""Calculate the relative path from build context to langgraph.json directory."""
|
||||
config_dir = config_path.parent.resolve()
|
||||
build_context_path = pathlib.Path(build_context).resolve()
|
||||
|
||||
try:
|
||||
relative_path = config_dir.relative_to(build_context_path)
|
||||
return str(relative_path) if str(relative_path) != "." else ""
|
||||
except ValueError as _:
|
||||
raise ValueError(
|
||||
f"Configuration file {config_path} is not under the build context {build_context}. "
|
||||
f"Please run the command from a directory that contains your langgraph.json file, "
|
||||
) from None
|
||||
|
||||
|
||||
def config_to_docker(
|
||||
config_path: pathlib.Path,
|
||||
config: Config,
|
||||
base_image: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
install_command: Optional[str] = None,
|
||||
build_command: Optional[str] = None,
|
||||
build_context: Optional[str] = None,
|
||||
) -> tuple[str, dict[str, str]]:
|
||||
base_image = base_image or default_base_image(config)
|
||||
|
||||
if config.get("node_version") and not config.get("python_version"):
|
||||
return node_config_to_docker(config_path, config, base_image, api_version)
|
||||
return node_config_to_docker(
|
||||
config_path,
|
||||
config,
|
||||
base_image,
|
||||
api_version,
|
||||
install_command,
|
||||
build_command,
|
||||
build_context,
|
||||
)
|
||||
|
||||
return python_config_to_docker(config_path, config, base_image, api_version)
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": [".", "../../libs/shared", "../../libs/common"],
|
||||
"graphs": {
|
||||
"agent": "./src/agent/graph.py:graph"
|
||||
},
|
||||
"env": ".env"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[project]
|
||||
name = "agent"
|
||||
version = "0.0.1"
|
||||
description = "Agent for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["agent"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"agent" = "src/agent"
|
||||
@@ -0,0 +1 @@
|
||||
"""Agent package."""
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Simple LangGraph agent for monorepo testing."""
|
||||
|
||||
from common import get_common_prefix
|
||||
from langchain_core.messages import AIMessage
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from shared import get_dummy_message
|
||||
|
||||
from agent.state import State
|
||||
|
||||
|
||||
def call_model(state: State) -> dict:
|
||||
"""Simple node that uses the shared libraries."""
|
||||
# Use functions from both shared packages
|
||||
dummy_message = get_dummy_message()
|
||||
prefix = get_common_prefix()
|
||||
|
||||
message = AIMessage(content=f"{prefix} Agent says: {dummy_message}")
|
||||
|
||||
return {"messages": [message]}
|
||||
|
||||
|
||||
def should_continue(state: State):
|
||||
"""Conditional edge - end after first message."""
|
||||
messages = state["messages"]
|
||||
if len(messages) > 0:
|
||||
return END
|
||||
return "call_model"
|
||||
|
||||
|
||||
# Build the graph
|
||||
workflow = StateGraph(State)
|
||||
|
||||
# Add the node
|
||||
workflow.add_node("call_model", call_model)
|
||||
|
||||
# Add edges
|
||||
workflow.add_edge(START, "call_model")
|
||||
workflow.add_conditional_edges("call_model", should_continue)
|
||||
|
||||
graph = workflow.compile()
|
||||
@@ -0,0 +1,13 @@
|
||||
"""State definition for the agent."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated, TypedDict
|
||||
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langgraph.graph.message import add_messages
|
||||
|
||||
|
||||
class State(TypedDict):
|
||||
"""The state of the agent."""
|
||||
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Common helper functions package."""
|
||||
|
||||
from .helpers import get_common_prefix
|
||||
|
||||
__all__ = ["get_common_prefix"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Common helper functions."""
|
||||
|
||||
|
||||
def get_common_prefix() -> str:
|
||||
"""Get a common prefix for messages."""
|
||||
return "[COMMON]"
|
||||
@@ -0,0 +1,20 @@
|
||||
[project]
|
||||
name = "shared"
|
||||
version = "0.0.1"
|
||||
description = "Shared utilities for the Python monorepo"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["shared"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"shared" = "src/shared"
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Shared utilities package."""
|
||||
|
||||
from .utils import get_dummy_message
|
||||
|
||||
__all__ = ["get_dummy_message"]
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Shared utility functions."""
|
||||
|
||||
|
||||
def get_dummy_message() -> str:
|
||||
"""Get a dummy message for testing."""
|
||||
return "Hello from shared library!"
|
||||
@@ -0,0 +1,46 @@
|
||||
[project]
|
||||
name = "python-monorepo-example"
|
||||
version = "0.0.1"
|
||||
description = "A Python monorepo example with LangGraph agents and shared packages"
|
||||
authors = [
|
||||
{ name = "Developer", email = "dev@example.com" },
|
||||
]
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11,<4.0"
|
||||
dependencies = [
|
||||
"langgraph>=0.6.0,<0.7.0",
|
||||
"langchain-core>=0.2.14",
|
||||
]
|
||||
|
||||
[tool.uv.workspace]
|
||||
members = ["apps/*", "libs/shared"]
|
||||
|
||||
[tool.uv.sources]
|
||||
shared = { workspace = true }
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["mypy>=1.11.1", "ruff>=0.6.1"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"D", # pydocstyle
|
||||
"UP",
|
||||
]
|
||||
lint.ignore = [
|
||||
"D100", # Missing docstring in public module
|
||||
"D101", # Missing docstring in public class
|
||||
"D102", # Missing docstring in public method
|
||||
"D103", # Missing docstring in public function
|
||||
"D104", # Missing docstring in public package
|
||||
"D105", # Missing docstring in magic method
|
||||
]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
@@ -570,7 +570,7 @@ def test_build_generate_proper_build_context():
|
||||
catch_exceptions=True,
|
||||
)
|
||||
|
||||
build_context_pattern = re.compile(r"--build-context\s+(\w+)=([^\s]+)")
|
||||
build_context_pattern = re.compile(r"--build-context\s+([\w-]+)=([^\s]+)")
|
||||
|
||||
build_contexts = re.findall(build_context_pattern, result.output)
|
||||
assert len(build_contexts) == 2, (
|
||||
|
||||
@@ -421,14 +421,14 @@ def test_config_to_docker_simple():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Installing local requirements --
|
||||
COPY --from=__outer_requirements.txt requirements.txt /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -r /deps/__outer_graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
COPY --from=outer-requirements.txt requirements.txt /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -r /deps/outer-graphs_reqs_a/graphs_reqs_a/requirements.txt
|
||||
# -- End of local requirements install --
|
||||
# -- Adding local package ../../examples --
|
||||
COPY --from=examples . /deps/examples
|
||||
# -- End of local package ../../examples --
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -438,11 +438,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency graphs_reqs_a --
|
||||
COPY --from=__outer_graphs_reqs_a . /deps/__outer_graphs_reqs_a/graphs_reqs_a
|
||||
COPY --from=outer-graphs_reqs_a . /deps/outer-graphs_reqs_a/graphs_reqs_a
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs_reqs_a"' \\
|
||||
@@ -452,21 +452,21 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs_reqs_a/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs_reqs_a/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs_reqs_a --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_HTTP='{{"app": "/deps/examples/my_app.py:app"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
|
||||
assert additional_contexts == {
|
||||
"__outer_graphs_reqs_a": str(
|
||||
"outer-graphs_reqs_a": str(
|
||||
(pathlib.Path(__file__).parent / "../../examples/graphs_reqs_a").resolve()
|
||||
),
|
||||
"examples": str((pathlib.Path(__file__).parent / "../../examples").resolve()),
|
||||
@@ -484,7 +484,7 @@ def test_config_to_docker_outside_path():
|
||||
"""\
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -494,11 +494,11 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Adding non-package dependency tests --
|
||||
COPY --from=__outer_tests . /deps/__outer_tests/tests
|
||||
COPY --from=outer-tests . /deps/outer-tests/tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "tests"' \\
|
||||
@@ -508,22 +508,22 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {
|
||||
"__outer_tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
"outer-tests": str(pathlib.Path(__file__).parent.parent.absolute()),
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ def test_config_to_docker_pipconfig():
|
||||
FROM langchain/langgraph-api:3.11
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -555,17 +555,17 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}'
|
||||
ENV LANGSERVE_GRAPHS='{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}'
|
||||
"""
|
||||
+ FORMATTED_CLEANUP_LINES
|
||||
+ """
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests\
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests\
|
||||
"""
|
||||
)
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -607,7 +607,7 @@ def test_config_to_docker_local_deps():
|
||||
expected_docker_stdin = f"""\
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs /deps/__outer_graphs/src
|
||||
ADD ./graphs /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -617,13 +617,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}\
|
||||
"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
@@ -691,7 +691,7 @@ ARG foo
|
||||
ADD pipconfig.txt /pipconfig.txt
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt langchain langchain_openai
|
||||
# -- Adding non-package dependency graphs --
|
||||
ADD ./graphs/ /deps/__outer_graphs/src
|
||||
ADD ./graphs/ /deps/outer-graphs/src
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "graphs"' \\
|
||||
@@ -701,13 +701,13 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_graphs/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-graphs/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency graphs --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_graphs/src/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-graphs/src/agent.py:graph"}}'
|
||||
{FORMATTED_CLEANUP_LINES}"""
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -797,7 +797,7 @@ def test_config_to_docker_gen_ui_python():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -807,7 +807,7 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
@@ -815,13 +815,13 @@ RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/con
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGGRAPH_UI='{{"agent": "./graphs/agent.ui.jsx"}}'
|
||||
ENV LANGGRAPH_UI_CONFIG='{{"shared": ["nuqs"]}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=20
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -843,7 +843,7 @@ def test_config_to_docker_multiplatform():
|
||||
expected_docker_stdin = f"""FROM langchain/langgraph-api:3.11
|
||||
RUN /storage/install-node.sh
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -853,19 +853,19 @@ RUN set -ex && \\
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/__outer_unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/__outer_unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"python": "/deps/outer-unit_tests/unit_tests/multiplatform/python.py:graph", "js": "/deps/outer-unit_tests/unit_tests/multiplatform/js.mts:graph"}}'
|
||||
# -- Installing JS dependencies --
|
||||
ENV NODE_VERSION=22
|
||||
RUN cd /deps/__outer_unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
RUN cd /deps/outer-unit_tests/unit_tests && npm i && tsx /api/langgraph_api/js/build.mts
|
||||
# -- End of JS dependencies install --
|
||||
{FORMATTED_CLEANUP_LINES}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests"""
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests"""
|
||||
|
||||
assert clean_empty_lines(actual_docker_stdin) == expected_docker_stdin
|
||||
assert additional_contexts == {}
|
||||
@@ -984,7 +984,7 @@ def test_config_to_compose_simple_config():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -994,15 +994,15 @@ def test_config_to_compose_simple_config():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1025,7 +1025,7 @@ def test_config_to_compose_env_vars():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api-custom:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1035,15 +1035,15 @@ def test_config_to_compose_env_vars():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
openai_api_key = "key"
|
||||
actual_compose_stdin = config_to_compose(
|
||||
@@ -1070,7 +1070,7 @@ def test_config_to_compose_env_file():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1080,15 +1080,15 @@ def test_config_to_compose_env_file():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
"""
|
||||
actual_compose_stdin = config_to_compose(
|
||||
PATH_TO_CONFIG,
|
||||
@@ -1108,7 +1108,7 @@ def test_config_to_compose_watch():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1118,15 +1118,15 @@ def test_config_to_compose_watch():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
@@ -1155,7 +1155,7 @@ def test_config_to_compose_end_to_end():
|
||||
dockerfile_inline: |
|
||||
FROM langchain/langgraph-api:3.11
|
||||
# -- Adding non-package dependency unit_tests --
|
||||
ADD . /deps/__outer_unit_tests/unit_tests
|
||||
ADD . /deps/outer-unit_tests/unit_tests
|
||||
RUN set -ex && \\
|
||||
for line in '[project]' \\
|
||||
'name = "unit_tests"' \\
|
||||
@@ -1165,15 +1165,15 @@ def test_config_to_compose_end_to_end():
|
||||
'[build-system]' \\
|
||||
'requires = ["setuptools>=61"]' \\
|
||||
'build-backend = "setuptools.build_meta"'; do \\
|
||||
echo "$line" >> /deps/__outer_unit_tests/pyproject.toml; \\
|
||||
echo "$line" >> /deps/outer-unit_tests/pyproject.toml; \\
|
||||
done
|
||||
# -- End of non-package dependency unit_tests --
|
||||
# -- Installing all local dependencies --
|
||||
RUN PYTHONDONTWRITEBYTECODE=1 uv pip install --system --no-cache-dir -c /api/constraints.txt -e /deps/*
|
||||
# -- End of local dependencies install --
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/__outer_unit_tests/unit_tests/agent.py:graph"}}'
|
||||
ENV LANGSERVE_GRAPHS='{{"agent": "/deps/outer-unit_tests/unit_tests/agent.py:graph"}}'
|
||||
{textwrap.indent(textwrap.dedent(FORMATTED_CLEANUP_LINES), " ")}
|
||||
WORKDIR /deps/__outer_unit_tests/unit_tests
|
||||
WORKDIR /deps/outer-unit_tests/unit_tests
|
||||
|
||||
develop:
|
||||
watch:
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"Bash(python:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(sed:*)",
|
||||
"Bash(awk:*)"
|
||||
"Bash(awk:*)",
|
||||
"Bash(uv run mypy:*)",
|
||||
"Bash(uv run:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from langgraph_sdk.auth import Auth
|
||||
from langgraph_sdk.client import get_client, get_sync_client
|
||||
|
||||
__version__ = "0.2.4"
|
||||
__version__ = "0.2.6"
|
||||
|
||||
__all__ = ["Auth", "get_client", "get_sync_client"]
|
||||
|
||||
@@ -400,6 +400,20 @@ class AuthContext(BaseAuthContext):
|
||||
"""
|
||||
|
||||
|
||||
class ThreadTTL(typing.TypedDict, total=False):
|
||||
"""Time-to-live configuration for a thread.
|
||||
|
||||
Matches the OpenAPI schema where TTL is represented as an object with
|
||||
an optional strategy and a time value in minutes.
|
||||
"""
|
||||
|
||||
strategy: typing.Literal["delete"]
|
||||
"""TTL strategy. Currently only 'delete' is supported."""
|
||||
|
||||
ttl: int
|
||||
"""Time-to-live in minutes from now until the thread should be swept."""
|
||||
|
||||
|
||||
class ThreadsCreate(typing.TypedDict, total=False):
|
||||
"""Parameters for creating a new thread.
|
||||
|
||||
@@ -422,6 +436,9 @@ class ThreadsCreate(typing.TypedDict, total=False):
|
||||
if_exists: OnConflictBehavior
|
||||
"""Behavior when a thread with the same ID already exists."""
|
||||
|
||||
ttl: ThreadTTL
|
||||
"""Optional TTL configuration for the thread."""
|
||||
|
||||
|
||||
class ThreadsRead(typing.TypedDict, total=False):
|
||||
"""Parameters for reading thread state or run information.
|
||||
@@ -489,6 +506,9 @@ class ThreadsSearch(typing.TypedDict, total=False):
|
||||
offset: int
|
||||
"""Offset for pagination."""
|
||||
|
||||
ids: Sequence[UUID] | None
|
||||
"""typing.Optional list of thread IDs to filter by."""
|
||||
|
||||
thread_id: UUID | None
|
||||
"""typing.Optional thread ID to filter by."""
|
||||
|
||||
|
||||
@@ -1179,6 +1179,7 @@ class ThreadsClient:
|
||||
if_exists: OnConflictBehavior | None = None,
|
||||
supersteps: Sequence[dict[str, Sequence[dict[str, Any]]]] | None = None,
|
||||
graph_id: str | None = None,
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -1193,6 +1194,9 @@ class ThreadsClient:
|
||||
supersteps: Apply a list of supersteps when creating a thread, each containing a sequence of updates.
|
||||
Each update has `values` or `command` and `as_node`. Used for copying a thread between deployments.
|
||||
graph_id: Optional graph ID to associate with the thread.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
@@ -1234,6 +1238,11 @@ class ThreadsClient:
|
||||
}
|
||||
for s in supersteps
|
||||
]
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
|
||||
return await self.http.post(
|
||||
"/threads", json=payload, headers=headers, params=params
|
||||
@@ -1244,6 +1253,7 @@ class ThreadsClient:
|
||||
thread_id: str,
|
||||
*,
|
||||
metadata: Mapping[str, Any],
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -1252,6 +1262,9 @@ class ThreadsClient:
|
||||
Args:
|
||||
thread_id: ID of thread to update.
|
||||
metadata: Metadata to merge with existing thread metadata.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
@@ -1265,12 +1278,19 @@ class ThreadsClient:
|
||||
thread = await client.threads.update(
|
||||
thread_id="my-thread-id",
|
||||
metadata={"number":1},
|
||||
ttl=43_200,
|
||||
)
|
||||
```
|
||||
""" # noqa: E501
|
||||
payload: dict[str, Any] = {"metadata": metadata}
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
return await self.http.patch(
|
||||
f"/threads/{thread_id}",
|
||||
json={"metadata": metadata},
|
||||
json=payload,
|
||||
headers=headers,
|
||||
params=params,
|
||||
)
|
||||
@@ -1309,6 +1329,7 @@ class ThreadsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
values: Json = None,
|
||||
ids: Sequence[str] | None = None,
|
||||
status: ThreadStatus | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
@@ -1323,6 +1344,7 @@ class ThreadsClient:
|
||||
Args:
|
||||
metadata: Thread metadata to filter on.
|
||||
values: State values to filter on.
|
||||
ids: List of thread IDs to filter by.
|
||||
status: Thread status to filter on.
|
||||
Must be one of 'idle', 'busy', 'interrupted' or 'error'.
|
||||
limit: Limit on number of threads to return.
|
||||
@@ -1356,6 +1378,8 @@ class ThreadsClient:
|
||||
payload["metadata"] = metadata
|
||||
if values:
|
||||
payload["values"] = values
|
||||
if ids:
|
||||
payload["ids"] = ids
|
||||
if status:
|
||||
payload["status"] = status
|
||||
if sort_by:
|
||||
@@ -4328,6 +4352,7 @@ class SyncThreadsClient:
|
||||
if_exists: OnConflictBehavior | None = None,
|
||||
supersteps: Sequence[dict[str, Sequence[dict[str, Any]]]] | None = None,
|
||||
graph_id: str | None = None,
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -4342,6 +4367,9 @@ class SyncThreadsClient:
|
||||
supersteps: Apply a list of supersteps when creating a thread, each containing a sequence of updates.
|
||||
Each update has `values` or `command` and `as_node`. Used for copying a thread between deployments.
|
||||
graph_id: Optional graph ID to associate with the thread.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
|
||||
Returns:
|
||||
@@ -4383,6 +4411,11 @@ class SyncThreadsClient:
|
||||
}
|
||||
for s in supersteps
|
||||
]
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
|
||||
return self.http.post("/threads", json=payload, headers=headers, params=params)
|
||||
|
||||
@@ -4391,6 +4424,7 @@ class SyncThreadsClient:
|
||||
thread_id: str,
|
||||
*,
|
||||
metadata: Mapping[str, Any],
|
||||
ttl: int | Mapping[str, Any] | None = None,
|
||||
headers: Mapping[str, str] | None = None,
|
||||
params: QueryParamTypes | None = None,
|
||||
) -> Thread:
|
||||
@@ -4399,7 +4433,11 @@ class SyncThreadsClient:
|
||||
Args:
|
||||
thread_id: ID of thread to update.
|
||||
metadata: Metadata to merge with existing thread metadata.
|
||||
ttl: Optional time-to-live in minutes for the thread. You can pass an
|
||||
integer (minutes) or a mapping with keys `ttl` and optional
|
||||
`strategy` (defaults to "delete").
|
||||
headers: Optional custom headers to include with the request.
|
||||
params: Optional query parameters to include with the request.
|
||||
|
||||
Returns:
|
||||
Thread: The created thread.
|
||||
@@ -4411,12 +4449,19 @@ class SyncThreadsClient:
|
||||
thread = client.threads.update(
|
||||
thread_id="my-thread-id",
|
||||
metadata={"number":1},
|
||||
ttl=43_200,
|
||||
)
|
||||
```
|
||||
""" # noqa: E501
|
||||
payload: dict[str, Any] = {"metadata": metadata}
|
||||
if ttl is not None:
|
||||
if isinstance(ttl, (int, float)):
|
||||
payload["ttl"] = {"ttl": ttl, "strategy": "delete"}
|
||||
else:
|
||||
payload["ttl"] = ttl
|
||||
return self.http.patch(
|
||||
f"/threads/{thread_id}",
|
||||
json={"metadata": metadata},
|
||||
json=payload,
|
||||
headers=headers,
|
||||
params=params,
|
||||
)
|
||||
@@ -4454,6 +4499,7 @@ class SyncThreadsClient:
|
||||
*,
|
||||
metadata: Json = None,
|
||||
values: Json = None,
|
||||
ids: Sequence[str] | None = None,
|
||||
status: ThreadStatus | None = None,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
@@ -4468,6 +4514,7 @@ class SyncThreadsClient:
|
||||
Args:
|
||||
metadata: Thread metadata to filter on.
|
||||
values: State values to filter on.
|
||||
ids: List of thread IDs to filter by.
|
||||
status: Thread status to filter on.
|
||||
Must be one of 'idle', 'busy', 'interrupted' or 'error'.
|
||||
limit: Limit on number of threads to return.
|
||||
@@ -4497,6 +4544,8 @@ class SyncThreadsClient:
|
||||
payload["metadata"] = metadata
|
||||
if values:
|
||||
payload["values"] = values
|
||||
if ids:
|
||||
payload["ids"] = ids
|
||||
if status:
|
||||
payload["status"] = status
|
||||
if sort_by:
|
||||
|
||||
Reference in New Issue
Block a user