diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 12d9f5a3b87..26abc96c876 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -81,7 +81,7 @@ "@ag-ui/client": "0.0.56", "@ag-ui/core": "0.0.56", "@ag-ui/encoder": "0.0.56", - "@ag-ui/langgraph": "0.0.39", + "@ag-ui/langgraph": "0.0.41", "@ag-ui/mcp-apps-middleware": "0.0.3", "@ag-ui/mcp-middleware": "0.0.1", "@ai-sdk/anthropic": "^3.0.49", diff --git a/packages/sdk-js/package.json b/packages/sdk-js/package.json index ccaec370578..1d070812377 100644 --- a/packages/sdk-js/package.json +++ b/packages/sdk-js/package.json @@ -59,7 +59,7 @@ "attw": "attw --pack . --profile node16" }, "dependencies": { - "@ag-ui/langgraph": "0.0.39", + "@ag-ui/langgraph": "0.0.41", "@copilotkit/shared": "workspace:*" }, "devDependencies": { diff --git a/packages/sdk-js/src/langgraph/middleware.ts b/packages/sdk-js/src/langgraph/middleware.ts index a7d95c47c27..2d87c4bcc24 100644 --- a/packages/sdk-js/src/langgraph/middleware.ts +++ b/packages/sdk-js/src/langgraph/middleware.ts @@ -5,7 +5,7 @@ import type { StandardSchemaV1, } from "@standard-schema/spec"; import * as z from "zod"; -import { getA2UITools } from "@ag-ui/langgraph"; +import { getA2UITools, type A2UIToolParams } from "@ag-ui/langgraph"; import { getForwardedHeaders } from "../header-propagation"; // --------------------------------------------------------------------------- @@ -394,11 +394,14 @@ const buildMiddlewareInput = (exposeState: ExposeStateOption) => ({ const decision = a2uiInjectDecision(request.state); if (typeof getA2UITools === "function" && decision) { const catalog = resolveA2uiCatalog(request.state); - const opts: { defaultCatalogId?: string; compositionGuide?: string } = {}; - if (catalog?.catalogId) opts.defaultCatalogId = catalog.catalogId; + // Shared A2UIToolParams: a single params object owned by the toolkit. + // `model` lives inside it; `compositionGuide` is folded into the + // `guidelines` bag alongside generation/design overrides. + const params: A2UIToolParams = { model: request.model }; + if (catalog?.catalogId) params.defaultCatalogId = catalog.catalogId; if (catalog?.compositionGuide) - opts.compositionGuide = catalog.compositionGuide; - const candidate = getA2UITools(request.model, opts); + params.guidelines = { compositionGuide: catalog.compositionGuide }; + const candidate = getA2UITools(params); const existingNames = new Set( (request.tools || []).map((t: any) => t?.name), ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e69d6810ad7..53a8f68c31b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2700,8 +2700,8 @@ importers: specifier: 0.0.56 version: 0.0.56 '@ag-ui/langgraph': - specifier: 0.0.39 - version: 0.0.39(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76)) + specifier: 0.0.41 + version: 0.0.41(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76)) '@ag-ui/mcp-apps-middleware': specifier: 0.0.3 version: 0.0.3(@ag-ui/client@0.0.56)(@cfworker/json-schema@4.1.1)(zod@3.25.76) @@ -2975,8 +2975,8 @@ importers: packages/sdk-js: dependencies: '@ag-ui/langgraph': - specifier: 0.0.39 - version: 0.0.39(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@6.35.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76)) + specifier: 0.0.41 + version: 0.0.41(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@6.35.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76)) '@copilotkit/shared': specifier: workspace:* version: link:../shared @@ -3505,6 +3505,9 @@ packages: '@ag-ui/a2ui-toolkit@0.0.2': resolution: {integrity: sha512-HFphlNxBxGSQfvxlI2LCQValSMDUTh3MAsaFMgYlF8sQXgCrXNiLJ70+Dz3uyOv4y/rfqdFafvlo1GKQtEVIVA==} + '@ag-ui/a2ui-toolkit@0.0.3': + resolution: {integrity: sha512-bKjtuYQufGZ+vc2oTz1v5S6ab2gH/whQIIgbGfP+LMisdAkDV7bqeg4e+lZO3xNmdmkCa6nvkovtudMkqxmxEA==} + '@ag-ui/client@0.0.36': resolution: {integrity: sha512-1Ey2KqK9KQpRJcnJvKPfVyLiTK4+CLBQZ085oJvr6T1nznw224j0KyzXNJ7cRjXeEGnuafmXTgpU+xEbN3xuYQ==} @@ -3559,8 +3562,8 @@ packages: '@ag-ui/langgraph@0.0.11': resolution: {integrity: sha512-3xUkaOelnpQ5tbsbuoOTin71tTgWEN0GDZBjGs/7xAwly2Dn4fahbBAoscXullO/pH9kTGGgbuJ0rWDUgo6fKQ==} - '@ag-ui/langgraph@0.0.39': - resolution: {integrity: sha512-+pFw49I9liEt8omTFFiie2YdtRFodjnWQTgN0Vxgo2XdC68xtyUy6I68D0QlZJE2Yy29oEx377vvkrNkL2AplA==} + '@ag-ui/langgraph@0.0.41': + resolution: {integrity: sha512-xo7ja/kuctmdPiH83QOUIpDs/AY3GzxW1fM37x9otK9fqwnKgi2JIcjfcdvAdGYdsCkXBn2WWQ2PVH+rdsLOzg==} peerDependencies: '@ag-ui/client': '>=0.0.42' '@ag-ui/core': '>=0.0.42' @@ -15374,9 +15377,6 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -24250,6 +24250,8 @@ snapshots: '@ag-ui/a2ui-toolkit@0.0.2': {} + '@ag-ui/a2ui-toolkit@0.0.3': {} + '@ag-ui/client@0.0.36': dependencies: '@ag-ui/core': 0.0.36 @@ -24385,9 +24387,9 @@ snapshots: - react-dom - ws - '@ag-ui/langgraph@0.0.39(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76))': + '@ag-ui/langgraph@0.0.41(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76))': dependencies: - '@ag-ui/a2ui-toolkit': 0.0.2 + '@ag-ui/a2ui-toolkit': 0.0.3 '@ag-ui/client': 0.0.56 '@ag-ui/core': 0.0.56 '@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76))(ws@8.19.0) @@ -24407,9 +24409,9 @@ snapshots: - ws - zod-to-json-schema - '@ag-ui/langgraph@0.0.39(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@6.35.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76))': + '@ag-ui/langgraph@0.0.41(@ag-ui/client@0.0.56)(@ag-ui/core@0.0.56)(@opentelemetry/api@1.9.0)(openai@6.35.0(ws@8.19.0)(zod@3.25.76))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vue@3.5.34(typescript@5.9.3))(ws@8.19.0)(zod-to-json-schema@3.25.1(zod@3.25.76))': dependencies: - '@ag-ui/a2ui-toolkit': 0.0.2 + '@ag-ui/a2ui-toolkit': 0.0.3 '@ag-ui/client': 0.0.56 '@ag-ui/core': 0.0.56 '@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@6.35.0(ws@8.19.0)(zod@3.25.76))(ws@8.19.0) @@ -40765,8 +40767,6 @@ snapshots: eventemitter3@4.0.7: {} - eventemitter3@5.0.1: {} - eventemitter3@5.0.4: {} events-universal@1.0.1: @@ -44348,7 +44348,7 @@ snapshots: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 - eventemitter3: 5.0.1 + eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.2 diff --git a/sdk-python/copilotkit/copilotkit_lg_middleware.py b/sdk-python/copilotkit/copilotkit_lg_middleware.py index 3a021cf154d..2e49a42e4d8 100644 --- a/sdk-python/copilotkit/copilotkit_lg_middleware.py +++ b/sdk-python/copilotkit/copilotkit_lg_middleware.py @@ -34,9 +34,10 @@ # Guarded so an older/skewed version without the factory degrades to # "no auto-A2UI" instead of breaking the whole middleware import. try: # pragma: no cover - exercised indirectly via the a2ui injection path - from ag_ui_langgraph import get_a2ui_tools + from ag_ui_langgraph import get_a2ui_tools, A2UIToolParams except Exception: # noqa: BLE001 - any import failure means the feature is off get_a2ui_tools = None + A2UIToolParams = None # Track which httpx clients already have the header-propagation hook installed # (by object id) so we never double-install on repeated model calls. @@ -420,15 +421,18 @@ def _maybe_build_a2ui_tool(self, request: ModelRequest) -> Any | None: resolved = self._resolve_a2ui_catalog(state) component_schema, catalog_id = resolved if resolved else (None, None) - kwargs: dict[str, Any] = {} + # Shared A2UIToolParams: a single params object owned by the toolkit. + # ``model`` lives inside it; ``composition_guide`` is folded into the + # ``guidelines`` bag alongside generation/design overrides. + params: "A2UIToolParams" = {"model": request.model} if catalog_id: - kwargs["default_catalog_id"] = catalog_id + params["default_catalog_id"] = catalog_id # Feed the registered component schema to the subagent so it composes # only catalog components (the toolkit appends this to its prompt). if component_schema: - kwargs["composition_guide"] = component_schema + params["guidelines"] = {"composition_guide": component_schema} - tool = get_a2ui_tools(request.model, **kwargs) + tool = get_a2ui_tools(params) # (2) Don't double-inject if the agent already defines this tool. existing_names = {getattr(t, "name", None) for t in (request.tools or [])} diff --git a/sdk-python/poetry.lock b/sdk-python/poetry.lock index 051526366ef..c5008e690b3 100644 --- a/sdk-python/poetry.lock +++ b/sdk-python/poetry.lock @@ -2,30 +2,30 @@ [[package]] name = "ag-ui-a2ui-toolkit" -version = "0.0.2" +version = "0.0.3" description = "Framework-agnostic helpers for building A2UI subagent tools — op builders, prompt assembly, history walkers, and request/envelope orchestration shared across framework adapters." optional = false python-versions = "<3.15,>=3.10" groups = ["main"] files = [ - {file = "ag_ui_a2ui_toolkit-0.0.2-py3-none-any.whl", hash = "sha256:028e497dfa2c9ca716143248dee14712d5c1055615a1bd91efa95f85e0a467ef"}, - {file = "ag_ui_a2ui_toolkit-0.0.2.tar.gz", hash = "sha256:5908fa7a9cf474fa26d8821ac15b135bdca2c1cfddce0b7c580c6382d1f0bfd9"}, + {file = "ag_ui_a2ui_toolkit-0.0.3-py3-none-any.whl", hash = "sha256:e0354bd361c09f342fbe671cf870cbd19fdcb1b27e7a5bb2d8a392a4f00c2ba9"}, + {file = "ag_ui_a2ui_toolkit-0.0.3.tar.gz", hash = "sha256:468f25473ac00d098878da54c0069b7fa27dc63b4c1ff61315d4349a324c2fb7"}, ] [[package]] name = "ag-ui-langgraph" -version = "0.0.40" +version = "0.0.41" description = "Implementation of the AG-UI protocol for LangGraph." optional = false python-versions = "<3.15,>=3.10" groups = ["main"] files = [ - {file = "ag_ui_langgraph-0.0.40-py3-none-any.whl", hash = "sha256:ee5e06a47ea267c082f746c9cdab4b0ac2dcc4f8e3a26d750d6dd82d0a0a07f1"}, - {file = "ag_ui_langgraph-0.0.40.tar.gz", hash = "sha256:11b009920b79fe97162f53777ecd7860c8a0999479d55f5d85d8da979d5be28f"}, + {file = "ag_ui_langgraph-0.0.41-py3-none-any.whl", hash = "sha256:ff5f4c3d03305ff51329543ff61bd686a3e0b5c3c4ea071b3575f328d13936ae"}, + {file = "ag_ui_langgraph-0.0.41.tar.gz", hash = "sha256:3ea1fcb49b147d9532b0a90f2a5554d6ffd0d9365590fa2557ba16a881aeeb7a"}, ] [package.dependencies] -ag-ui-a2ui-toolkit = ">=0.0.2" +ag-ui-a2ui-toolkit = ">=0.0.3" ag-ui-protocol = ">=0.1.15" fastapi = {version = ">=0.115.12", optional = true, markers = "extra == \"fastapi\""} langchain = ">=1.2.0" @@ -38,14 +38,14 @@ fastapi = ["fastapi (>=0.115.12)"] [[package]] name = "ag-ui-protocol" -version = "0.1.18" +version = "0.1.19" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "ag_ui_protocol-0.1.18-py3-none-any.whl", hash = "sha256:d151c0f0a34160647f1571163f7185746f4326b15a56d1560de5082a7a0e7a12"}, - {file = "ag_ui_protocol-0.1.18.tar.gz", hash = "sha256:b37c672c3fd6bac12b316c39f45ad9db9f137bbb885489c79f268507029a22ff"}, + {file = "ag_ui_protocol-0.1.19-py3-none-any.whl", hash = "sha256:898843b1410d378824da0c6a776486288b9c5828689d0bf563118868e37f390f"}, + {file = "ag_ui_protocol-0.1.19.tar.gz", hash = "sha256:43c27f60d41712dcad0e9e0a203cbdf1c8e248b22417374c5c68321c448af4ea"}, ] [package.dependencies] @@ -5642,4 +5642,4 @@ crewai = ["crewai"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.15" -content-hash = "63e8a9a538d3c4dafa497f509516c818f51ace66477ce4d1fd2cc273a357976f" +content-hash = "5fc9ecbf95de1d25040712c2675d39dfedc8a099be1e425390c03014e8c97d58" diff --git a/sdk-python/pyproject.toml b/sdk-python/pyproject.toml index c525c79da8f..d0b5b7b5868 100644 --- a/sdk-python/pyproject.toml +++ b/sdk-python/pyproject.toml @@ -31,7 +31,7 @@ python = ">=3.10,<3.15" langgraph = { version = ">=0.3.25,<2" } langchain = { version = ">=0.3.0" } crewai = { version = ">=0.118.0", optional = true, python = ">=3.10,<3.14" } -ag-ui-langgraph = { version = ">=0.0.40", extras = ["fastapi"] } +ag-ui-langgraph = { version = ">=0.0.41", extras = ["fastapi"] } ag-ui-protocol = ">=0.1.15" fastapi = ">=0.115.0,<1.0.0" partialjson = "^0.0.8" diff --git a/sdk-python/tests/test_copilotkit_lg_middleware.py b/sdk-python/tests/test_copilotkit_lg_middleware.py index c00a3c54f89..647e735f1fa 100644 --- a/sdk-python/tests/test_copilotkit_lg_middleware.py +++ b/sdk-python/tests/test_copilotkit_lg_middleware.py @@ -1092,7 +1092,10 @@ def test_none_configurable_falls_back_to_context(self, monkeypatch): _a2ui_unavailable = pytest.mark.skipif( get_a2ui_tools is None, - reason="ag-ui-langgraph without get_a2ui_tools (needs >=0.0.37)", + reason=( + "ag-ui-langgraph without get_a2ui_tools; the single-arg " + "A2UIToolParams API needs the OSS-248 release (>=0.0.41)" + ), ) diff --git a/showcase/integrations/langgraph-typescript/src/agent/graph.ts b/showcase/integrations/langgraph-typescript/src/agent/graph.ts index 294e2452dea..6d1e5d44d4a 100644 --- a/showcase/integrations/langgraph-typescript/src/agent/graph.ts +++ b/showcase/integrations/langgraph-typescript/src/agent/graph.ts @@ -164,7 +164,8 @@ const searchFlights = tool( // a2ui_dynamic). A secondary LLM designs the surface; the factory forces the // host catalog and emits the a2ui_operations envelope. Replaces the prior // hand-rolled generate_a2ui tool. -const generateA2ui = getA2UITools(new ChatOpenAI({ model: "gpt-4.1" }), { +const generateA2ui = getA2UITools({ + model: new ChatOpenAI({ model: "gpt-4.1" }), defaultCatalogId: "copilotkit://app-dashboard-catalog", }); diff --git a/showcase/shell-docs/src/content/docs/integrations/langgraph/generative-ui/a2ui/dynamic-schema.mdx b/showcase/shell-docs/src/content/docs/integrations/langgraph/generative-ui/a2ui/dynamic-schema.mdx index 2f7d11b1fa5..301eadc1593 100644 --- a/showcase/shell-docs/src/content/docs/integrations/langgraph/generative-ui/a2ui/dynamic-schema.mdx +++ b/showcase/shell-docs/src/content/docs/integrations/langgraph/generative-ui/a2ui/dynamic-schema.mdx @@ -71,9 +71,12 @@ base_model = ChatOpenAI(model="gpt-4o") TOOLS = [ get_a2ui_tools( - model=base_model, - default_catalog_id="https://a2ui.org/demos/dojo/dynamic_catalog.json", - composition_guide=COMPOSITION_GUIDE, # how the subagent should use your components + { + "model": base_model, + "default_catalog_id": "https://a2ui.org/demos/dojo/dynamic_catalog.json", + # guidelines: optional generation/design/composition prompt knobs + "guidelines": {"composition_guide": COMPOSITION_GUIDE}, + } ) ] @@ -91,11 +94,11 @@ workflow.add_edge("tool_node", "chat_node") graph = workflow.compile() ``` -`get_a2ui_tools(model, default_catalog_id=..., composition_guide=...)` is the same factory the middleware uses under the hood: +`get_a2ui_tools(params)` takes a single `A2UIToolParams` object — the same factory the middleware uses under the hood: - **`model`** — the secondary LLM that designs the surface. - **`default_catalog_id`** — binds generated surfaces to your catalog (BYOC). Catalog ownership stays with the host; the model never picks it. -- **`composition_guide`** — optional rules appended to the subagent's prompt telling it which components exist and how to compose them. +- **`guidelines`** — optional prompt knobs: `composition_guide` (which components exist and how to compose them), plus `generation_guidelines` / `design_guidelines` to override the built-in generation/design defaults. ## Progressive streaming