From 134456c83d16d934813f9aa89196c04c2fa3f115 Mon Sep 17 00:00:00 2001 From: Kinfe123 Date: Thu, 29 Oct 2026 18:57:52 -0700 Subject: [PATCH] fix(anthropic): accept stable and beta SDK events --- API.md | 4 ++ CHANGELOG.md | 2 + examples/tsconfig.json | 2 +- package.json | 1 + packages/core/src/anthropic.d.ts | 1 + pnpm-lock.yaml | 74 ++++++++++++++++++++++++++++++-- tests/anthropic-sdk.ts | 20 +++++++++ 7 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 tests/anthropic-sdk.ts diff --git a/API.md b/API.md index ff66ee4..a784180 100644 --- a/API.md +++ b/API.md @@ -336,6 +336,10 @@ composable integration value: | `streamfold/langchain` | `langchain` | | `streamfold/ag-ui` | `agUI` | +The Anthropic integration accepts both `client.messages.create({ ..., stream: true })` +and `client.beta.messages.create({ ..., stream: true })` directly. Text, thinking, +and other non-tool SDK events can remain in the stream and are ignored. + | Method | Returns | | --- | --- | | `pushAll(event)` | All lifecycle updates caused by this event, in order; `[]` for ignored events | diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e00533..d846f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Accept Anthropic stable and beta SDK message event interfaces directly in + the Anthropic adapter and managed reader. - Reuse the final frozen snapshot in immutable pool completions, avoiding a second JSON value tree while keeping live final values independently parsed. - Release consumed completion payloads in managed SDK readers while preserving diff --git a/examples/tsconfig.json b/examples/tsconfig.json index fe54bd6..b8011c2 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -5,5 +5,5 @@ "checkJs": true, "skipLibCheck": true }, - "files": ["assistant-ui.mjs", "vercel-ai.mjs", "custom.mjs", "../tests/langchain-sdk.ts"] + "files": ["assistant-ui.mjs", "vercel-ai.mjs", "custom.mjs", "../tests/langchain-sdk.ts", "../tests/anthropic-sdk.ts"] } diff --git a/package.json b/package.json index a47a2e8..1c7eb73 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "verify": "pnpm check && pnpm pack:check && pnpm bench && pnpm bench:sdk" }, "devDependencies": { + "@anthropic-ai/sdk": "1.3.0", "@langchain/core": "1.3.0", "ai": "7.0.152", "assistant-stream": "0.3.42", diff --git a/packages/core/src/anthropic.d.ts b/packages/core/src/anthropic.d.ts index f073497..16daa4c 100644 --- a/packages/core/src/anthropic.d.ts +++ b/packages/core/src/anthropic.d.ts @@ -28,6 +28,7 @@ export type AnthropicToolInputEvent = readonly type: "content_block_stop"; readonly index: number; } + | { readonly type: string } | { readonly type: string; readonly index?: number; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7381c13..63294d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,12 @@ importers: .: devDependencies: + '@anthropic-ai/sdk': + specifier: 1.3.0 + version: 1.3.0(zod@4.6.7) '@langchain/core': specifier: 1.3.0 - version: 1.3.0 + version: 1.3.0(@opentelemetry/api@1.10.0) ai: specifier: 7.0.152 version: 7.0.152(zod@4.6.7) @@ -56,6 +59,19 @@ packages: resolution: {integrity: sha512-C6ylMuAXjgqKJllp6ODIJwUltK/tm3ZmIj4V2fqENP3Y8gif2DyeJSsTcDwFrXppgEKcrBisi/BWx2tYeoeCAw==} engines: {node: '>=22'} + '@anthropic-ai/sdk@1.3.0': + resolution: {integrity: sha512-k7z9BlLLPxfZH+ov7dS6EnaN/wiZeV8YNT1LZfrpAHIy7oY7pHr4UzirF9f2OlHtFEnRBOX+AZQ91Y5fK+TPTw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@babel/runtime@7.29.10': + resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} + engines: {node: '>=6.9.0'} + '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} @@ -63,9 +79,16 @@ packages: resolution: {integrity: sha512-ZiS8USsHdO8ZVFh0m8Rs6v2BvqBEvfFicG66zxdF4rGgXSJjk0d0sLnnFZCx3RXPUvP8ybTJgPlNfkJGs1J7BA==} engines: {node: '>=20'} + '@opentelemetry/api@1.10.0': + resolution: {integrity: sha512-lMkyes8iHrG+mk6WH7X70MdCLI2RS0FwNBY1PyQ8UemaijE2m5mv5A0Niy3HtbYTFrgkUEQjFMXmSAk2hQDuPQ==} + engines: {node: '>=8.0.0'} + '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -242,6 +265,9 @@ packages: resolution: {integrity: sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==} engines: {node: '>=12.17.0'} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -258,6 +284,10 @@ packages: js-tiktoken@1.0.21: resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -331,6 +361,9 @@ packages: secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + standardwebhooks@1.1.1: + resolution: {integrity: sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==} + tinyexec@1.3.1: resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} @@ -339,6 +372,9 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} @@ -388,14 +424,24 @@ snapshots: dependencies: json-schema: 0.4.0 + '@anthropic-ai/sdk@1.3.0(zod@4.6.7)': + dependencies: + '@opentelemetry/api': 1.10.0 + json-schema-to-ts: 3.1.1 + standardwebhooks: 1.1.1 + optionalDependencies: + zod: 4.6.7 + + '@babel/runtime@7.29.10': {} + '@cfworker/json-schema@4.1.1': {} - '@langchain/core@1.3.0': + '@langchain/core@1.3.0(@opentelemetry/api@1.10.0)': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.11.4 + langsmith: 0.11.4(@opentelemetry/api@1.10.0) mustache: 4.2.0 p-queue: 6.6.2 zod: 4.6.7 @@ -406,10 +452,14 @@ snapshots: - openai - ws + '@opentelemetry/api@1.10.0': {} + '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 + '@stablelib/base64@1.0.1': {} + '@standard-schema/spec@1.1.0': {} '@typescript/typescript-aix-ppc64@7.0.2': @@ -517,6 +567,8 @@ snapshots: dependencies: pure-rand: 8.4.2 + fast-sha256@1.3.0: {} + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -527,13 +579,20 @@ snapshots: dependencies: base64-js: 1.5.1 + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.10 + ts-algebra: 2.0.0 + json-schema@0.4.0: {} jsonc-parser@3.3.1: {} - langsmith@0.11.4: + langsmith@0.11.4(@opentelemetry/api@1.10.0): dependencies: p-queue: 6.6.2 + optionalDependencies: + '@opentelemetry/api': 1.10.0 mustache@4.2.0: {} @@ -566,6 +625,11 @@ snapshots: secure-json-parse@4.1.0: {} + standardwebhooks@1.1.1: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + tinyexec@1.3.1: {} tinyglobby@0.2.17: @@ -573,6 +637,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 + ts-algebra@2.0.0: {} + typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 diff --git a/tests/anthropic-sdk.ts b/tests/anthropic-sdk.ts new file mode 100644 index 0000000..34c875f --- /dev/null +++ b/tests/anthropic-sdk.ts @@ -0,0 +1,20 @@ +import Anthropic from "@anthropic-ai/sdk"; +import { readStructured } from "streamfold"; +import { anthropic } from "streamfold/anthropic"; + +const client = new Anthropic({ apiKey: "typecheck-only" }); +const request = { + model: "fixture", max_tokens: 32, + messages: [{ role: "user" as const, content: "Weather in Paris?" }], + stream: true as const, +}; +const events = await client.messages.create(request); +readStructured(events, { integration: anthropic }); +const stream = anthropic(); +for await (const event of events) stream.pushAll(event); + +const betaEvents = await client.beta.messages.create(request); +readStructured(betaEvents, { integration: anthropic }); +const betaStream = anthropic(); +for await (const event of betaEvents) betaStream.push(event); +stream.pushAll({ type: "provider_metadata", metadata: { region: "test" } });