diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml
index 23cf4386..2b0de813 100644
--- a/.github/workflows/create-releases.yml
+++ b/.github/workflows/create-releases.yml
@@ -40,6 +40,5 @@ jobs:
${{ github.workspace }}/artifacts/packages/*.nupkg
--source https://api.nuget.org/v3/index.json
--api-key $NUGET_API_KEY
- --skip-duplicate
env:
NUGET_API_KEY: ${{ secrets.ANTHROPIC_NUGET_API_KEY || secrets.NUGET_API_KEY }}
diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml
index 1ea1d60f..3fd89c2d 100644
--- a/.github/workflows/publish-nuget.yml
+++ b/.github/workflows/publish-nuget.yml
@@ -27,6 +27,5 @@ jobs:
${{ github.workspace }}/artifacts/packages/*.nupkg
--source https://api.nuget.org/v3/index.json
--api-key $NUGET_API_KEY
- --skip-duplicate
env:
NUGET_API_KEY: ${{ secrets.ANTHROPIC_NUGET_API_KEY || secrets.NUGET_API_KEY }}
\ No newline at end of file
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 74705098..f03113bb 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,4 +1,4 @@
{
- ".": "11.0.0",
- "src/Anthropic.Foundry": "0.1.0"
-}
\ No newline at end of file
+ "src/Anthropic": "12.0.0",
+ "src/Anthropic.Foundry": "0.2.0"
+}
diff --git a/.stats.yml b/.stats.yml
index e008716e..bd79f46f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 32
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic%2Fanthropic-a49e89deec4e00d1da490808099d66e2001531b12d8666a7f5d0b496f760440d.yml
openapi_spec_hash: c93ef3808c58e233b01966ff154f31ce
-config_hash: 0abc2a1f94e35774a88fceb60e703c5f
+config_hash: a5d8cd02f9a686d4e0baa7ba652e3e55
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0245589..c63f98ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# Changelog
## 11.0.0 (2025-12-01)
@@ -347,3 +348,312 @@ Full Changelog: [v0.0.1...v0.1.0](https://github.com/anthropics/anthropic-sdk-cs
* **client:** refine enum representation ([a3e973b](https://github.com/anthropics/anthropic-sdk-csharp/commit/a3e973b0e6d057e58e6f0bd08c8a5635da896974))
* **client:** use plural for service namespace ([843da53](https://github.com/anthropics/anthropic-sdk-csharp/commit/843da53c91a4e925298aae8907f8990b7e13de9e))
+||||||| cbf1ebb7
+=======
+# Changelog
+
+## 10.4.0 (2025-11-25)
+
+Full Changelog: [v10.3.0...v10.4.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.3.0...v10.4.0)
+
+### Features
+
+* **client:** support .NET Standard 2.0 ([70928cd](https://github.com/anthropics/anthropic-sdk-csharp/commit/70928cdd02452b2b7ad37f419b43d92680e02f9d))
+
+
+### Bug Fixes
+
+* **internal:** don't format csproj files ([76affbf](https://github.com/anthropics/anthropic-sdk-csharp/commit/76affbf85b3f9c04bd500020644660265d361fb6))
+
+
+### Chores
+
+* **internal:** add logo to nuget package ([#181](https://github.com/anthropics/anthropic-sdk-csharp/issues/181)) ([f2ca130](https://github.com/anthropics/anthropic-sdk-csharp/commit/f2ca130ab65ec6db6ce164a33a7a820de5187e1a))
+* **internal:** remove redundant keyword ([f33f185](https://github.com/anthropics/anthropic-sdk-csharp/commit/f33f185da453cc9c8293891cb653964d085e362e))
+* remove .keep ([#37](https://github.com/anthropics/anthropic-sdk-csharp/issues/37)) ([3974964](https://github.com/anthropics/anthropic-sdk-csharp/commit/3974964dbf738d0a265f77482e3c9fecefdc5f67))
+
+
+### Refactors
+
+* **internal:** remove abstract static methods ([3a3dffe](https://github.com/anthropics/anthropic-sdk-csharp/commit/3a3dffedbc11260c1b5e65606671f9898af9531b))
+
+## 10.3.0 (2025-11-24)
+
+Full Changelog: [v10.2.1...v10.3.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.2.1...v10.3.0)
+
+### Features
+
+* **api:** adds support for Claude Opus 4.5, Effort, Advance Tool Use Features, Autocompaction, and Computer Use v5 ([144a820](https://github.com/anthropics/anthropic-sdk-csharp/commit/144a8209e522f5bba2174b1efd3d5607a2d7c145))
+
+
+### Bug Fixes
+
+* **internal:** install csharpier during ci lint phase ([8898df9](https://github.com/anthropics/anthropic-sdk-csharp/commit/8898df9bf709867ddf3851bd5f5c0acbd8d90764))
+* **internal:** minor project fixes ([3c344e2](https://github.com/anthropics/anthropic-sdk-csharp/commit/3c344e2db929ed43cc49854c791ea10e5e42489c))
+* **internal:** remove release notes from foundry readme ([afeaa2f](https://github.com/anthropics/anthropic-sdk-csharp/commit/afeaa2f526c3818c244bb351b4dad56a59883395))
+
+
+### Chores
+
+* **client:** change name of underlying properties for models and params ([75a2cce](https://github.com/anthropics/anthropic-sdk-csharp/commit/75a2ccecefaf3fff5a07138a3c38ff0b9b9df476))
+* formatting ([6850900](https://github.com/anthropics/anthropic-sdk-csharp/commit/6850900ae2b8f5da55381988af5d4cb5b2ee4351))
+* **internal:** update release please config ([980d7fd](https://github.com/anthropics/anthropic-sdk-csharp/commit/980d7fd21375f9125c0bd0f58a378a081bfa11bb))
+
+## 10.2.1 (2025-11-20)
+
+Full Changelog: [v10.2.0...v10.2.1](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.2.0...v10.2.1)
+
+## 10.2.0 (2025-11-20)
+
+Full Changelog: [v10.1.2...v10.2.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.1.2...v10.2.0)
+
+### Features
+
+* **client:** additional methods for positional params ([8bc6323](https://github.com/anthropics/anthropic-sdk-csharp/commit/8bc6323c38ce551f995bec5e4b1584460b7f037b))
+
+
+### Bug Fixes
+
+* **client:** return correct type for foundry#WithOptions ([#18](https://github.com/anthropics/anthropic-sdk-csharp/issues/18)) ([f814a46](https://github.com/anthropics/anthropic-sdk-csharp/commit/f814a460503abf7fdf7a824b5bf446ef74d60f28))
+* use correct versions ([c78c8db](https://github.com/anthropics/anthropic-sdk-csharp/commit/c78c8db4b6effa6b1438bb879bcafdad2d155808))
+
+
+### Refactors
+
+* **client:** make unknown variants implicit ([eb0e5b6](https://github.com/anthropics/anthropic-sdk-csharp/commit/eb0e5b628d7090adc34300775043ecd26ccfffaf))
+
+## 10.1.2 (2025-11-18)
+
+Full Changelog: [v10.1.1...v10.1.2](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.1.1...v10.1.2)
+
+### Bug Fixes
+
+* use correct version ([a808311](https://github.com/anthropics/anthropic-sdk-csharp/commit/a8083119584c82ec26e1d74f980b6c021e1fbb10))
+
+## 10.1.1 (2025-11-18)
+
+Full Changelog: [v10.1.0...v10.1.1](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.1.0...v10.1.1)
+
+## 10.1.0 (2025-11-18)
+
+Full Changelog: [v10.0.1...v10.1.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v10.0.1...v10.1.0)
+
+### Features
+
+* add Foundry client ([8ddea23](https://github.com/anthropics/anthropic-sdk-csharp/commit/8ddea2363a799b366740779703f074fbe8dadf56))
+
+## 10.0.1 (2025-11-18)
+
+Full Changelog: [v0.2.0...v10.0.1](https://github.com/anthropics/anthropic-sdk-csharp/compare/v0.2.0...v10.0.1)
+
+### ⚠ BREAKING CHANGES
+
+* **client:** improve names of some types
+* **client:** use `DateTimeOffset` instead of `DateTime`
+* **client:** flatten service namespaces
+* **client:** interpret null as omitted in some properties
+
+### Features
+
+* **api:** add file download method ([a03d526](https://github.com/anthropics/anthropic-sdk-csharp/commit/a03d5267282ba893e96ca96c70c7b28326076d1a))
+* **api:** add support for structured outputs beta ([17ea9b3](https://github.com/anthropics/anthropic-sdk-csharp/commit/17ea9b388f10cfe621af9aeb9f3ddd799027fc09))
+* **api:** rename C# package to Anthropic ([2ba3485](https://github.com/anthropics/anthropic-sdk-csharp/commit/2ba34850dcd783b672aff1371970db7e5f0abc14))
+* **client:** add `HttpResponse.ReadAsStream` method ([677857b](https://github.com/anthropics/anthropic-sdk-csharp/commit/677857b53e4bcfbc3f6a7b0d3cd7e2c9af86c9cd))
+* **client:** add cancellation token support ([bf4c0e5](https://github.com/anthropics/anthropic-sdk-csharp/commit/bf4c0e57952376844c27f63311e70cb903c5897c))
+* **client:** add per-resource headers ([1d7658a](https://github.com/anthropics/anthropic-sdk-csharp/commit/1d7658ad37ade9ed4d5a73521f72cb3a389535de))
+* **client:** add retries support ([3327c9b](https://github.com/anthropics/anthropic-sdk-csharp/commit/3327c9b2fd704a2807a9d4453d1c99c7f12e97f9))
+* **client:** add some implicit operators ([bf26da8](https://github.com/anthropics/anthropic-sdk-csharp/commit/bf26da89cad05f586a7f24fbcf0ad5adcfefc44f))
+* **client:** send `User-Agent` header ([e8a0844](https://github.com/anthropics/anthropic-sdk-csharp/commit/e8a08449899460d22522336714d86264755e1a57))
+* **client:** send `X-Stainless-Arch` header ([d66d180](https://github.com/anthropics/anthropic-sdk-csharp/commit/d66d180ff7c04aff7ec53cfefaa1dff0236ce53c))
+* **client:** send `X-Stainless-Lang` and `X-Stainless-OS` headers ([bcc30e9](https://github.com/anthropics/anthropic-sdk-csharp/commit/bcc30e9a754798c96d28516d556e40c4e8cbf802))
+* **client:** send `X-Stainless-Package-Version` headers ([84bf583](https://github.com/anthropics/anthropic-sdk-csharp/commit/84bf583218f56682972add2c77784c88700eff53))
+* **client:** send `X-Stainless-Runtime` and `X-Stainless-Runtime-Version` ([94d2581](https://github.com/anthropics/anthropic-sdk-csharp/commit/94d25812e111657e81e9f7c27dfdab97c0af82f4))
+* **client:** send `X-Stainless-Timeout` header ([95ec578](https://github.com/anthropics/anthropic-sdk-csharp/commit/95ec578685f65b8ff008b35b4cf43f289107dc86))
+* **client:** validate constant values ([493a9ef](https://github.com/anthropics/anthropic-sdk-csharp/commit/493a9efb26479cf26e21d7c7c95b70507c0d3dc9))
+* **csharp:** enable nuget publishing ([4a4a1bc](https://github.com/anthropics/anthropic-sdk-csharp/commit/4a4a1bccd369b7f7b38db636c2f5846c43b7d826))
+* **docs:** add package/version notice ([76b74eb](https://github.com/anthropics/anthropic-sdk-csharp/commit/76b74eb7f1aaee9ba6cb1844b061aee8c1288633))
+* **docs:** Semver warning ([55c20ba](https://github.com/anthropics/anthropic-sdk-csharp/commit/55c20bad38b05b7a2ec166ca403214833103b9c1))
+* **docs:** tweak readme notice ([82d5990](https://github.com/anthropics/anthropic-sdk-csharp/commit/82d5990cb33ba6acc55d12954c94aafaa75b7f7d))
+* **docs:** Update README for nuget (instead of just github) ([6bde0b4](https://github.com/anthropics/anthropic-sdk-csharp/commit/6bde0b45452e1ecde305ebace0b8a063ac205e40))
+* **docs:** Update version refs in README ([70d787d](https://github.com/anthropics/anthropic-sdk-csharp/commit/70d787dcc7d47a79e47814209f81a1366a3460c7))
+
+
+### Bug Fixes
+
+* **client:** interpret null as omitted in some properties ([56059db](https://github.com/anthropics/anthropic-sdk-csharp/commit/56059db7047e7263cbd666f19293985577f8339d))
+* **client:** use `DateTimeOffset` instead of `DateTime` ([dbc7f6f](https://github.com/anthropics/anthropic-sdk-csharp/commit/dbc7f6f086dd0a75d869c1c683fa3c245c18f548))
+* use correct header name ([f6d0942](https://github.com/anthropics/anthropic-sdk-csharp/commit/f6d0942657fd87bc7b479602e1e913f404da0bb7))
+
+
+### Performance Improvements
+
+* **client:** optimize header creation ([3d37bb5](https://github.com/anthropics/anthropic-sdk-csharp/commit/3d37bb54241981dfbfdfc7a8f69c2430de808bfb))
+
+
+### Chores
+
+* **client:** deprecate some symbols ([b3446f6](https://github.com/anthropics/anthropic-sdk-csharp/commit/b3446f6d62f8d6e53a6871aee5979903f6b04498))
+* **internal:** add prism log file to gitignore ([8588901](https://github.com/anthropics/anthropic-sdk-csharp/commit/8588901ed4a32880165b344246bc3b8c1dc2464d))
+* **internal:** codegen related update ([cf3f5d5](https://github.com/anthropics/anthropic-sdk-csharp/commit/cf3f5d5f9af0f066c53c2dcb0d27bed5f602edce))
+* **internal:** delete empty test files ([a79abd1](https://github.com/anthropics/anthropic-sdk-csharp/commit/a79abd17f32d1313f77365faf0fed8d004ff48c3))
+* **internal:** improve devcontainer ([ab246ff](https://github.com/anthropics/anthropic-sdk-csharp/commit/ab246ffcde051808c017d73c46d18a769ec7d2c0))
+* **internal:** minor improvements to csproj and gitignore ([bf94b8c](https://github.com/anthropics/anthropic-sdk-csharp/commit/bf94b8c15a7f296780660134ceb251e28ee0ed23))
+* **internal:** reduce import qualification ([137c8b4](https://github.com/anthropics/anthropic-sdk-csharp/commit/137c8b4b2103d5b510698629359e7ef2a28512ad))
+* **internal:** update release please config ([bd94183](https://github.com/anthropics/anthropic-sdk-csharp/commit/bd9418322fe76a3c7db57375ddb2f0ba8ee49543))
+
+
+### Documentation
+
+* **client:** document max retries ([e1f611f](https://github.com/anthropics/anthropic-sdk-csharp/commit/e1f611fdd28e19788f0fe843396707d20bb069fa))
+* **client:** separate comment content into paragraphs ([1f89605](https://github.com/anthropics/anthropic-sdk-csharp/commit/1f89605692d5cfee120c740098f0a35ccded6d93))
+* **internal:** add warning about implementing interface ([5476caf](https://github.com/anthropics/anthropic-sdk-csharp/commit/5476cafac1904b8185fecd56ebbe088136df3ccd))
+
+
+### Refactors
+
+* **client:** flatten service namespaces ([8de3f66](https://github.com/anthropics/anthropic-sdk-csharp/commit/8de3f666532cf1ed31031587c4819e024e3bfb6f))
+* **client:** improve names of some types ([2e52d59](https://github.com/anthropics/anthropic-sdk-csharp/commit/2e52d5996dd0121814b2827eafa3a6fca6f5c3d9))
+* **client:** move some defaults out of `ClientOptions` ([d536293](https://github.com/anthropics/anthropic-sdk-csharp/commit/d536293d0cc42d3341437f390587907cc4a8df5e))
+* **client:** pass around `ClientOptions` instead of client ([608310d](https://github.com/anthropics/anthropic-sdk-csharp/commit/608310d02a14ccfdaefad3c0f8d921ed98c2375e))
+
+## 0.2.0 (2025-11-05)
+
+Full Changelog: [v0.1.0...v0.2.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v0.1.0...v0.2.0)
+
+### ⚠ BREAKING CHANGES
+
+* **client:** make models immutable
+
+### Features
+
+* **api:** add ability to clear thinking in context management ([05d2ce6](https://github.com/anthropics/anthropic-sdk-csharp/commit/05d2ce6bc64fe547fe7bc695d383af89caf7a45d))
+* **client:** add response validation option ([6130f1b](https://github.com/anthropics/anthropic-sdk-csharp/commit/6130f1bc759bcc6c54cac411f69dd237c7fb40ce))
+* **client:** add support for option modification ([e105fba](https://github.com/anthropics/anthropic-sdk-csharp/commit/e105fbad5f26c737c57ce23ad2cbcd81b89bd07e))
+* **client:** make models immutable ([f55629c](https://github.com/anthropics/anthropic-sdk-csharp/commit/f55629c40cf51fc43cf3a64ec87e53051f88fee6))
+* **client:** support request timeout ([7411046](https://github.com/anthropics/anthropic-sdk-csharp/commit/7411046b4bc02671bd805d96a6c2745df0af4fcc))
+
+
+### Chores
+
+* **api:** mark older sonnet models as deprecated ([fc00d2b](https://github.com/anthropics/anthropic-sdk-csharp/commit/fc00d2b1dd5f100e523acf6f440e7a32c2452576))
+* **client:** simplify field validations ([6130f1b](https://github.com/anthropics/anthropic-sdk-csharp/commit/6130f1bc759bcc6c54cac411f69dd237c7fb40ce))
+* **internal:** codegen related update ([2798e0a](https://github.com/anthropics/anthropic-sdk-csharp/commit/2798e0a5fdc81a6076d449a73e8e880eb451b500))
+* **internal:** extract `ClientOptions` struct ([7e906c8](https://github.com/anthropics/anthropic-sdk-csharp/commit/7e906c854b0b68e981565df411407039dc6486e9))
+* **internal:** full qualify some references ([8a52868](https://github.com/anthropics/anthropic-sdk-csharp/commit/8a528685fbb605a06427773868638ebdcecb97b6))
+
+
+### Documentation
+
+* **client:** document `WithOptions` ([38352b0](https://github.com/anthropics/anthropic-sdk-csharp/commit/38352b0ec8b3b1d1f98ef08e83437875440cb9ba))
+* **client:** document response validation ([0e9f728](https://github.com/anthropics/anthropic-sdk-csharp/commit/0e9f72869c1c85f3e116c17eae5422847e2615fb))
+* **client:** document timeout option ([80d8d7f](https://github.com/anthropics/anthropic-sdk-csharp/commit/80d8d7fa0f2251892ee6c17e99c9a8db04334321))
+* **client:** improve snippet formatting ([94dc213](https://github.com/anthropics/anthropic-sdk-csharp/commit/94dc21334c5caeb106f5d07971c92c8b4a45aa1a))
+
+## 0.1.0 (2025-10-27)
+
+Full Changelog: [v0.0.1...v0.1.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v0.0.1...v0.1.0)
+
+### Features
+
+* **api:** add claude-opus-4-1-20250805 ([c38689c](https://github.com/anthropics/anthropic-sdk-csharp/commit/c38689ce56b61bd5259785cd0478c8cecdf01630))
+* **api:** add support for Search Result Content Blocks ([3300718](https://github.com/anthropics/anthropic-sdk-csharp/commit/33007185312999c941e9ece33dde30b397e1b2ec))
+* **api:** adding support for agent skills ([4acc546](https://github.com/anthropics/anthropic-sdk-csharp/commit/4acc546f3d2117c098bf5eada070a83e619dbe5f))
+* **api:** adds support for Claude Sonnet 4.5 and context management features ([bab904c](https://github.com/anthropics/anthropic-sdk-csharp/commit/bab904c771612cde421696bda8616819024e863e))
+* **api:** adds support for Documents in tool results ([a7b5086](https://github.com/anthropics/anthropic-sdk-csharp/commit/a7b5086b8dd0211e723b4d6f9b903091df387d37))
+* **api:** adds support for text_editor_20250728 tool ([159d728](https://github.com/anthropics/anthropic-sdk-csharp/commit/159d7280cc3347b2241833ec32e64ddd8d467fbf))
+* **api:** adds support for web_fetch_20250910 tool ([74a7a92](https://github.com/anthropics/anthropic-sdk-csharp/commit/74a7a923abf5eef3ba34d6b2dda23a0e038d1064))
+* **api:** makes 1 hour TTL Cache Control generally available ([84b1ad3](https://github.com/anthropics/anthropic-sdk-csharp/commit/84b1ad3530ecf8f6fdb3c6dcd12e9a6331add9b4))
+* **api:** manual updates ([1528d71](https://github.com/anthropics/anthropic-sdk-csharp/commit/1528d714aee94bec3e0218e3f12d207fb5178878))
+* **api:** removed older deprecated models ([f5aafba](https://github.com/anthropics/anthropic-sdk-csharp/commit/f5aafbabd37dce4c3d14e3a8925bd9fde926bbd3))
+* **api:** search result content blocks ([e4368ee](https://github.com/anthropics/anthropic-sdk-csharp/commit/e4368ee1df5de9963ecd5295db7adaa2f882b776))
+* **api:** update PHP and C# ([d63878a](https://github.com/anthropics/anthropic-sdk-csharp/commit/d63878a830159b05ad5262de680cbd3c1cd1dd99))
+* **api:** update to desired NuGet name ([c4b6820](https://github.com/anthropics/anthropic-sdk-csharp/commit/c4b682000227c3daf1b6c854f7b4b3fe316aec45))
+* **betas:** add context-1m-2025-08-07 ([f65802a](https://github.com/anthropics/anthropic-sdk-csharp/commit/f65802a33c9474d32774a4aabae84ff53403acf8))
+* **ci:** add publishing flow for nuget ([487ac2e](https://github.com/anthropics/anthropic-sdk-csharp/commit/487ac2e31527626cf2105bb3209faa49ddb1654a))
+* **ci:** implement test/lint ci ([b34d54a](https://github.com/anthropics/anthropic-sdk-csharp/commit/b34d54ab994e80cb9a57721bfef817f857b4a0b9))
+* **client:** add and set all client ops ([3dee455](https://github.com/anthropics/anthropic-sdk-csharp/commit/3dee45538cd1f65cfa6da729ab9c3e6b47dafab7))
+* **client:** add implicit conversions to enums ([324f263](https://github.com/anthropics/anthropic-sdk-csharp/commit/324f263ccdee745b3f815abb17c09310146e56c0))
+* **client:** add some convenience constructors ([e2541e1](https://github.com/anthropics/anthropic-sdk-csharp/commit/e2541e10315a9304f4925fdafffc2494ab62a20f))
+* **client:** add streaming methods ([b394064](https://github.com/anthropics/anthropic-sdk-csharp/commit/b394064caef025f0a8cacfc299dc1dbe9636b1c8))
+* **client:** add switch and match helpers for unions ([d44a80c](https://github.com/anthropics/anthropic-sdk-csharp/commit/d44a80c8872f1fca137fbbfb4ed41c178ebe3c35))
+* **client:** adds support for code-execution-2025-08-26 tool ([5be3c78](https://github.com/anthropics/anthropic-sdk-csharp/commit/5be3c787f331d2dcaae55f1ed900b6cc04052818))
+* **client:** allow omitting all params object when all optional ([68a792f](https://github.com/anthropics/anthropic-sdk-csharp/commit/68a792f6591d02d8fce140949831a84b21eed686))
+* **client:** automatically set constants for user ([bb1343e](https://github.com/anthropics/anthropic-sdk-csharp/commit/bb1343ef5311c535a0836e83c65e156483eb4a45))
+* **client:** basic paginated endpoint support ([4766f1e](https://github.com/anthropics/anthropic-sdk-csharp/commit/4766f1ec369b01863ce96a22264f40d9f953f412))
+* **client:** implement implicit union casts ([e36b8fa](https://github.com/anthropics/anthropic-sdk-csharp/commit/e36b8fa372c81c387298bd2e700a74a0dac2c8d1))
+* **client:** improve model names ([18a0af9](https://github.com/anthropics/anthropic-sdk-csharp/commit/18a0af9f5d5eca5e0b1267c213e35d748ca3a0a0))
+* **client:** improve signature of `trypickx` methods ([620b39b](https://github.com/anthropics/anthropic-sdk-csharp/commit/620b39bd653c5c5fbdf3ddd0d8bfe3921ec9c81f))
+* **client:** make union deserialization more robust ([26d42da](https://github.com/anthropics/anthropic-sdk-csharp/commit/26d42dae0039f709e4ca33449c9567bbc0ff689b))
+* **client:** make union deserialization more robust ([f85bc36](https://github.com/anthropics/anthropic-sdk-csharp/commit/f85bc367ad3f076d36b233cc956768fea226d1ae))
+* **client:** refactor exceptions ([e5cfd36](https://github.com/anthropics/anthropic-sdk-csharp/commit/e5cfd364afd96ce37f01a639a6587e7c27801715))
+* **client:** refactor unions ([f6b60e3](https://github.com/anthropics/anthropic-sdk-csharp/commit/f6b60e3e4ce82b5442d27989f751c86de0354fc2))
+* **client:** shorten union variant names ([c397c9b](https://github.com/anthropics/anthropic-sdk-csharp/commit/c397c9bda8cfde000e9b092fb0f384695a9993cd))
+* **internal:** add dedicated build job in ci ([9d46238](https://github.com/anthropics/anthropic-sdk-csharp/commit/9d46238a5bfc3c25276ed63bcc55b26aa42674d7))
+* **internal:** add dev container ([e7682c0](https://github.com/anthropics/anthropic-sdk-csharp/commit/e7682c0790e1adbe4b24c9c6cadfb2c6c7c43112))
+* **internal:** allow overriding mock url via `TEST_API_BASE_URL` env ([f14a23c](https://github.com/anthropics/anthropic-sdk-csharp/commit/f14a23c5b6065a377bf273189c5cf4d5b1826250))
+* **internal:** generate release flow files ([7a759d7](https://github.com/anthropics/anthropic-sdk-csharp/commit/7a759d76d63bd673defaa8a00aeb9c1111ce20a4))
+
+
+### Bug Fixes
+
+* **client:** better type names ([057bf2d](https://github.com/anthropics/anthropic-sdk-csharp/commit/057bf2ddf817d443f86fe5913cf5399705c65914))
+* **client:** compilation error ([56d1c41](https://github.com/anthropics/anthropic-sdk-csharp/commit/56d1c41dbcca95ddbd40cb296ebe516a3598b30d))
+* **client:** handle multiple auth options gracefully ([beabac5](https://github.com/anthropics/anthropic-sdk-csharp/commit/beabac5836af2a6bb946605d978cdc1325912aba))
+* **client:** improve model validation ([b77753e](https://github.com/anthropics/anthropic-sdk-csharp/commit/b77753e46cad3eda6ef37f4ad2df2066199b1a14))
+* **client:** instantiate union variant from list properly ([0db37e5](https://github.com/anthropics/anthropic-sdk-csharp/commit/0db37e5874d4a361048feac13014f25740e5142a))
+* **client:** support non-optional client options ([fadaa63](https://github.com/anthropics/anthropic-sdk-csharp/commit/fadaa63599a9411094aede97aa59084916a3de6d))
+* **docs:** re-order using statements ([b77bdb2](https://github.com/anthropics/anthropic-sdk-csharp/commit/b77bdb2aa4bcde1a0e21938c1d4be5ea755dfaed))
+* **internal:** add message to sse exception ([8481832](https://github.com/anthropics/anthropic-sdk-csharp/commit/8481832fd8861b4f1ec9ed46389716ce0be4589c))
+* **internal:** minor bug fixes on model instantiation and union validation ([6d0f0d9](https://github.com/anthropics/anthropic-sdk-csharp/commit/6d0f0d9b399fb1e270f215d130e9e59b37bec627))
+* **internal:** prefer to use implicit instantiation when possible ([b869753](https://github.com/anthropics/anthropic-sdk-csharp/commit/b86975337839d95e151e27421c84566ad0c6ecd7))
+* **internal:** remove example csproj ([e6e2c93](https://github.com/anthropics/anthropic-sdk-csharp/commit/e6e2c932f4ac99d7dacef0fad4177f0d0d76c9f2))
+* **internal:** remove unused null class ([c46f844](https://github.com/anthropics/anthropic-sdk-csharp/commit/c46f844118f54ca85615794d420c8b4202761f27))
+* **internal:** rename package directory ([a2557ac](https://github.com/anthropics/anthropic-sdk-csharp/commit/a2557ac8a9567267147d2d4f296c674f74460b82))
+* **internal:** various minor code fixes ([136162a](https://github.com/anthropics/anthropic-sdk-csharp/commit/136162addc0812087d051e8e5844226f31eda895))
+
+
+### Chores
+
+* **api:** remove unsupported endpoints ([d318ba7](https://github.com/anthropics/anthropic-sdk-csharp/commit/d318ba7c3c652b813fe81316ac5d5110fd8ebcb2))
+* **api:** update BetaCitationSearchResultLocation ([801a222](https://github.com/anthropics/anthropic-sdk-csharp/commit/801a222c8eeaa43625bdc078ef9da8ffec9351e4))
+* **client:** add context-management-2025-06-27 beta header ([c716a85](https://github.com/anthropics/anthropic-sdk-csharp/commit/c716a85034072a14e1c189ca2422f6ec5fce680b))
+* **client:** add model-context-window-exceeded-2025-08-26 beta header ([6ea4ac3](https://github.com/anthropics/anthropic-sdk-csharp/commit/6ea4ac36590316c30a7622f1cf67ce5dd473ed7e))
+* **client:** add TextEditor_20250429 tool ([adee5b4](https://github.com/anthropics/anthropic-sdk-csharp/commit/adee5b42af4ac04e3569570aca45a931aa16dd6f))
+* **client:** make some interfaces internal ([476e69e](https://github.com/anthropics/anthropic-sdk-csharp/commit/476e69e077869ce56271dfe69837a02ea1d66811))
+* **client:** swap `[@params](https://github.com/params)` to better name ([3d8e0d9](https://github.com/anthropics/anthropic-sdk-csharp/commit/3d8e0d96ba2e7e6d1c2aaf4da3848647bd6d5e1f))
+* **docs:** clarify beta library limitations in readme ([0aafa74](https://github.com/anthropics/anthropic-sdk-csharp/commit/0aafa74d0d8d2e4664033eacb248688aab52247b))
+* improve example values ([7b3bc97](https://github.com/anthropics/anthropic-sdk-csharp/commit/7b3bc9703a5d189f5a7b41a96e91efb5463e0e8e))
+* **internal:** codegen related update ([b98acb4](https://github.com/anthropics/anthropic-sdk-csharp/commit/b98acb42e3fe9bae70c6a799e48f914019e003b1))
+* **internal:** codegen related update ([c765e20](https://github.com/anthropics/anthropic-sdk-csharp/commit/c765e20eada019988d7d13597258f5eff28431e8))
+* **internal:** codegen related update ([fb6b738](https://github.com/anthropics/anthropic-sdk-csharp/commit/fb6b7383219e9fef56cdf0786170f1943249b9c7))
+* **internal:** codegen related update ([135523a](https://github.com/anthropics/anthropic-sdk-csharp/commit/135523aad5f9df5ee22a25f4ba7670335f2b8647))
+* **internal:** fix tests ([c7205c2](https://github.com/anthropics/anthropic-sdk-csharp/commit/c7205c25c86ce6f61d49a97d24827c21853f4d19))
+* **internal:** refactor tests to de-duplicate client instantiation logic ([f14a23c](https://github.com/anthropics/anthropic-sdk-csharp/commit/f14a23c5b6065a377bf273189c5cf4d5b1826250))
+* **internal:** remove unnecessary internal aliasing ([d210122](https://github.com/anthropics/anthropic-sdk-csharp/commit/d2101221fc498b57c60593896491751a6c77f9d8))
+* **internal:** rename parameters ([0013847](https://github.com/anthropics/anthropic-sdk-csharp/commit/0013847d2d7db6f4611b6c863f74b11a442310a1))
+* **internal:** restructure some imports ([974e4a3](https://github.com/anthropics/anthropic-sdk-csharp/commit/974e4a31bde9f9e64e8115fd0198baa4342603c7))
+* **internal:** stop running whitespace lint ([f14a23c](https://github.com/anthropics/anthropic-sdk-csharp/commit/f14a23c5b6065a377bf273189c5cf4d5b1826250))
+* **internal:** update comment in script ([d9ff761](https://github.com/anthropics/anthropic-sdk-csharp/commit/d9ff7619e8a211f948913945e3f3d2b94a122611))
+* **internal:** update test skipping reason ([124aab3](https://github.com/anthropics/anthropic-sdk-csharp/commit/124aab31ade145f7e326483f6ffc4aeda8005fe1))
+* **internal:** use nicer generic names ([00c3c7e](https://github.com/anthropics/anthropic-sdk-csharp/commit/00c3c7e215233ff0882930db8dc8177c22b85165))
+* rename some things ([654eb75](https://github.com/anthropics/anthropic-sdk-csharp/commit/654eb75cd6097c1554d07e2ec81da2c212e395be))
+* update @stainless-api/prism-cli to v5.15.0 ([3a1d8f7](https://github.com/anthropics/anthropic-sdk-csharp/commit/3a1d8f7920630ca2111f401d0c4792ba324135ff))
+* update formatting ([8b06f4f](https://github.com/anthropics/anthropic-sdk-csharp/commit/8b06f4f14153b608acbe1f00461a055e3c74d553))
+* update SDK settings ([f5e0568](https://github.com/anthropics/anthropic-sdk-csharp/commit/f5e05681a49e4de0d8cc3f73e08d9590997c27a6))
+* use non-aliased `using` ([ba9d1ac](https://github.com/anthropics/anthropic-sdk-csharp/commit/ba9d1ac2f5b3e86dc4fcf9f5857e550a40ec8995))
+
+
+### Documentation
+
+* add more info to the readme ([9f20bf2](https://github.com/anthropics/anthropic-sdk-csharp/commit/9f20bf26184307069b94c81d219d732ac46ace50))
+* **client:** add more property comments ([a3e973b](https://github.com/anthropics/anthropic-sdk-csharp/commit/a3e973b0e6d057e58e6f0bd08c8a5635da896974))
+* fix installation instructions ([4c76768](https://github.com/anthropics/anthropic-sdk-csharp/commit/4c767688eca1a4a873c8f80e266c1600bfd4bafa))
+* note alpha status ([cc023e3](https://github.com/anthropics/anthropic-sdk-csharp/commit/cc023e3d5096fc5bdb08f86f85c3afd71090159a))
+* streaming in readme ([6063490](https://github.com/anthropics/anthropic-sdk-csharp/commit/6063490d142965cf0be2d937e4d39f5d624a5b84))
+
+
+### Refactors
+
+* **client:** refine enum representation ([a3e973b](https://github.com/anthropics/anthropic-sdk-csharp/commit/a3e973b0e6d057e58e6f0bd08c8a5635da896974))
+* **client:** use plural for service namespace ([843da53](https://github.com/anthropics/anthropic-sdk-csharp/commit/843da53c91a4e925298aae8907f8990b7e13de9e))
+>>>>>>> origin/generated--merge-conflict
diff --git a/README.md b/README.md
index fe689450..5e5d8462 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,8 @@ The REST API documentation can be found on [docs.anthropic.com](https://docs.ant
## Installation
+Install the package from [NuGet](https://www.nuget.org/packages/Anthropic):
+
```bash
dotnet add package Anthropic
```
diff --git a/examples/Anthropic.Beta.ClientExample/Anthropic.Beta.ClientExample.csproj b/examples/Anthropic.Beta.ClientExample/Anthropic.Beta.ClientExample.csproj
deleted file mode 100644
index 76a262de..00000000
--- a/examples/Anthropic.Beta.ClientExample/Anthropic.Beta.ClientExample.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Exe
- net8.0
- Anthropic.Beta.ClientExample
- enable
- disable
-
-
diff --git a/examples/Anthropic.ClientExample/Anthropic.ClientExample.csproj b/examples/Anthropic.ClientExample/Anthropic.ClientExample.csproj
deleted file mode 100644
index b973a978..00000000
--- a/examples/Anthropic.ClientExample/Anthropic.ClientExample.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Exe
- net8.0
- Anthropic.ClientExample
- enable
- disable
-
-
diff --git a/examples/Anthropic.Examples.sln b/examples/Anthropic.Examples.sln
index 2362337d..c5f116ad 100644
--- a/examples/Anthropic.Examples.sln
+++ b/examples/Anthropic.Examples.sln
@@ -5,6 +5,14 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagesExample", "MessagesExample\MessagesExample.csproj", "{FA9BAC30-CC15-4D21-99CF-011D5D082FAF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThinkingStreamingExample", "ThinkingStreamingExample\ThinkingStreamingExample.csproj", "{97BE49BD-B144-4967-8718-C79E4521BA3F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThinkingExample", "ThinkingExample\ThinkingExample.csproj", "{79DCA526-628F-413B-83DA-F1B92029C065}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagesStreamingExample", "MessagesStreamingExample\MessagesStreamingExample.csproj", "{8CD54CE9-894F-4636-8CAF-D20914CF4014}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatClientExample", "ChatClientExample\ChatClientExample.csproj", "{AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +35,54 @@ Global
{FA9BAC30-CC15-4D21-99CF-011D5D082FAF}.Release|x64.Build.0 = Release|Any CPU
{FA9BAC30-CC15-4D21-99CF-011D5D082FAF}.Release|x86.ActiveCfg = Release|Any CPU
{FA9BAC30-CC15-4D21-99CF-011D5D082FAF}.Release|x86.Build.0 = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|x64.Build.0 = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Debug|x86.Build.0 = Debug|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|x64.ActiveCfg = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|x64.Build.0 = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|x86.ActiveCfg = Release|Any CPU
+ {97BE49BD-B144-4967-8718-C79E4521BA3F}.Release|x86.Build.0 = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|x64.Build.0 = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Debug|x86.Build.0 = Debug|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|Any CPU.Build.0 = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|x64.ActiveCfg = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|x64.Build.0 = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|x86.ActiveCfg = Release|Any CPU
+ {79DCA526-628F-413B-83DA-F1B92029C065}.Release|x86.Build.0 = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|x64.Build.0 = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Debug|x86.Build.0 = Debug|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|x64.ActiveCfg = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|x64.Build.0 = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|x86.ActiveCfg = Release|Any CPU
+ {8CD54CE9-894F-4636-8CAF-D20914CF4014}.Release|x86.Build.0 = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|x64.Build.0 = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Debug|x86.Build.0 = Debug|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|x64.ActiveCfg = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|x64.Build.0 = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|x86.ActiveCfg = Release|Any CPU
+ {AA6ED2E6-A693-4FD8-AF20-1D339FBFBF96}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/examples/MessagesExample/Program.cs b/examples/MessagesExample/Program.cs
index 6c8b0fc2..fa724119 100644
--- a/examples/MessagesExample/Program.cs
+++ b/examples/MessagesExample/Program.cs
@@ -1,10 +1,8 @@
-using System;
-using Anthropic;
-using Anthropic.Foundry;
+using Anthropic;
using Anthropic.Models.Messages;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
-IAnthropicClient client = new AnthropicClient();
+var client = new AnthropicClient();
// For using the Foundry client, use this instead
// AnthropicFoundryClient client = new(new AnthropicFoundryApiKeyCredentials("API-TOKEN", "RESOURCE-NAME"));
@@ -21,7 +19,7 @@
var response = await client.Messages.Create(parameters);
-var message = String.Join(
+var message = string.Join(
"",
response
.Content.Where(message => message.Value is TextBlock)
diff --git a/examples/MessagesStreamingExample/Program.cs b/examples/MessagesStreamingExample/Program.cs
index 9c803716..7135a31f 100644
--- a/examples/MessagesStreamingExample/Program.cs
+++ b/examples/MessagesStreamingExample/Program.cs
@@ -1,8 +1,5 @@
-using System;
-using Anthropic;
+using Anthropic;
using Anthropic.Models.Messages;
-using Anthropic.Models.Messages.ContentBlockVariants;
-using Anthropic.Models.Messages.MessageParamProperties;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
AnthropicClient client = new();
diff --git a/examples/ThinkingExample/Program.cs b/examples/ThinkingExample/Program.cs
index 18ecef7c..888588e1 100644
--- a/examples/ThinkingExample/Program.cs
+++ b/examples/ThinkingExample/Program.cs
@@ -1,8 +1,5 @@
-using System;
-using Anthropic;
+using Anthropic;
using Anthropic.Models.Messages;
-using Anthropic.Models.Messages.MessageParamProperties;
-using ContentBlockVariants = Anthropic.Models.Messages.ContentBlockVariants;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
AnthropicClient client = new();
@@ -22,21 +19,19 @@
foreach (ContentBlock block in response.Content)
{
- if (block.TryPickThinking(out ThinkingBlock thinking))
+ if (block.TryPickThinking(out ThinkingBlock? thinking))
{
Console.WriteLine($"Thinking: {thinking.Thinking}");
}
- else if (block.TryPickText(out TextBlock text))
+ else if (block.TryPickText(out TextBlock? text))
{
Console.WriteLine($"Text: {text.Text}");
}
}
-var message = String.Join(
+var message = string.Join(
"",
- response
- .Content.OfType()
- .Select((textBlock) => textBlock.Value.Text)
+ response.Content.Select(e => e.Value).OfType().Select((textBlock) => textBlock.Text)
);
Console.WriteLine(message);
diff --git a/examples/ThinkingStreamingExample/Program.cs b/examples/ThinkingStreamingExample/Program.cs
index cb15d3fa..f965b96e 100644
--- a/examples/ThinkingStreamingExample/Program.cs
+++ b/examples/ThinkingStreamingExample/Program.cs
@@ -1,7 +1,5 @@
-using System;
-using Anthropic;
+using Anthropic;
using Anthropic.Models.Messages;
-using Anthropic.Models.Messages.MessageParamProperties;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
AnthropicClient client = new();
diff --git a/release-please-config.json b/release-please-config.json
index 2a1838bc..779d38a8 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -1,6 +1,6 @@
{
"packages": {
- ".": {},
+ "src/Anthropic": {},
"src/Anthropic.Foundry": {}
},
"$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
@@ -60,7 +60,7 @@
"hidden": true
}
],
- "release-type": "simple",
+ "release-type": "csharp",
"extra-files": [
{
"type": "xml",
diff --git a/scripts/build b/scripts/build
index 3ce1e776..ab5f1179 100755
--- a/scripts/build
+++ b/scripts/build
@@ -5,4 +5,5 @@ set -e
cd "$(dirname "$0")/.."
echo "==> Running dotnet build"
-dotnet build
+dotnet build Anthropic.sln
+dotnet build examples/Anthropic.Examples.sln
diff --git a/src/Anthropic.Foundry/Anthropic.Foundry.csproj b/src/Anthropic.Foundry/Anthropic.Foundry.csproj
index a9f2825b..2f453e21 100644
--- a/src/Anthropic.Foundry/Anthropic.Foundry.csproj
+++ b/src/Anthropic.Foundry/Anthropic.Foundry.csproj
@@ -20,7 +20,7 @@
true
true
Anthropic.Foundry
- 0.1.0
+ 0.2.0
Stainless Software, Inc.
Stainless Software, Inc.
https://github.com/anthropics/anthropic-sdk-csharp
diff --git a/src/Anthropic.Foundry/CHANGELOG.md b/src/Anthropic.Foundry/CHANGELOG.md
index 1a34dc64..e11a8574 100644
--- a/src/Anthropic.Foundry/CHANGELOG.md
+++ b/src/Anthropic.Foundry/CHANGELOG.md
@@ -1,5 +1,34 @@
# Changelog
+## 0.2.0 (2025-12-10)
+
+Full Changelog: [Foundry-v0.1.0...Foundry-v0.2.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/Foundry-v0.1.0...Foundry-v0.2.0)
+
+### Features
+
+* add Foundry client ([5f87e12](https://github.com/anthropics/anthropic-sdk-csharp/commit/5f87e129a262d8a373e5e10bcca4196cf5db0394))
+* **client:** additional methods for positional params ([08c27c6](https://github.com/anthropics/anthropic-sdk-csharp/commit/08c27c6a4cb45b886be44babbb51bf4934add374))
+* **client:** additional methods for positional params ([8bc6323](https://github.com/anthropics/anthropic-sdk-csharp/commit/8bc6323c38ce551f995bec5e4b1584460b7f037b))
+
+
+### Bug Fixes
+
+* **client:** return correct type for foundry#WithOptions ([#18](https://github.com/anthropics/anthropic-sdk-csharp/issues/18)) ([9ff2124](https://github.com/anthropics/anthropic-sdk-csharp/commit/9ff2124a9190269ff4a469b6e8c9f6b895f8d2d2))
+* **client:** return correct type for foundry#WithOptions ([#18](https://github.com/anthropics/anthropic-sdk-csharp/issues/18)) ([f814a46](https://github.com/anthropics/anthropic-sdk-csharp/commit/f814a460503abf7fdf7a824b5bf446ef74d60f28))
+* **internal:** remove release notes from foundry readme ([afeaa2f](https://github.com/anthropics/anthropic-sdk-csharp/commit/afeaa2f526c3818c244bb351b4dad56a59883395))
+* use correct versions ([7c97d7f](https://github.com/anthropics/anthropic-sdk-csharp/commit/7c97d7f19c6937a2dacb666b05b9b9d040d677c7))
+* use correct versions ([c78c8db](https://github.com/anthropics/anthropic-sdk-csharp/commit/c78c8db4b6effa6b1438bb879bcafdad2d155808))
+
+
+### Chores
+
+* fix ci ([#196](https://github.com/anthropics/anthropic-sdk-csharp/issues/196)) ([8dede61](https://github.com/anthropics/anthropic-sdk-csharp/commit/8dede6176cb86e1ae85db9c8d0fae50c595ef964))
+* **internal:** add logo to nuget package ([#181](https://github.com/anthropics/anthropic-sdk-csharp/issues/181)) ([e01f08d](https://github.com/anthropics/anthropic-sdk-csharp/commit/e01f08dbd35f05c3ecc964eb040312b4f7ca6713))
+* **internal:** suppress diagnostic for .netstandard2.0 ([9ede62d](https://github.com/anthropics/anthropic-sdk-csharp/commit/9ede62de370abcad1fc1a5211700a6c967d360ca))
+* **internal:** suppress diagnostic for .netstandard2.0 ([1b0714d](https://github.com/anthropics/anthropic-sdk-csharp/commit/1b0714dc78ba2e69ab149d7cf768963379ec73e5))
+* sync with release-please ([2f5aa29](https://github.com/anthropics/anthropic-sdk-csharp/commit/2f5aa2992a2d462bb1f547efa09355e4f0b60656))
+* sync with release-please ([191236b](https://github.com/anthropics/anthropic-sdk-csharp/commit/191236bf5fb39092a2e2afc062e9cd222b859d2d))
+
## 0.1.0 (2025-12-01)
Full Changelog: [v0.0.3...v0.1.0](https://github.com/anthropics/anthropic-sdk-csharp/compare/v0.0.3...v0.1.0)
diff --git a/src/Anthropic.Tests/AnthropicClientBetaExtensionsTests.cs b/src/Anthropic.Tests/AnthropicClientBetaExtensionsTests.cs
index a1b39eba..c4db4d0d 100644
--- a/src/Anthropic.Tests/AnthropicClientBetaExtensionsTests.cs
+++ b/src/Anthropic.Tests/AnthropicClientBetaExtensionsTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Anthropic;
@@ -1128,7 +1129,9 @@ public async Task GetResponseAsync_WithAIFunctionTool_AdditionalProperties_Flows
{
new() { ["query"] = JsonSerializer.SerializeToElement("example query") },
},
- [nameof(BetaTool.AllowedCallers)] = new List>
+ [nameof(BetaTool.AllowedCallers)] = new List<
+ ApiEnum
+ >
{
new(JsonSerializer.SerializeToElement("direct")),
},
@@ -1210,4 +1213,902 @@ public async Task GetResponseAsync_WithAIFunctionTool_PartialAdditionalPropertie
ChatResponse response = await chatClient.GetResponseAsync("Use strict tool", options);
Assert.NotNull(response);
}
+
+ [Fact]
+ public void AsAITool_GetService_ReturnsToolUnion()
+ {
+ BetaToolUnion toolUnion = new BetaWebSearchTool20250305()
+ {
+ AllowedDomains = ["example.com"],
+ };
+ AITool aiTool = toolUnion.AsAITool();
+ Assert.Same(toolUnion, aiTool.GetService());
+
+ Assert.Null(aiTool.GetService("key"));
+ Assert.Null(aiTool.GetService());
+
+ Assert.Contains(nameof(BetaWebSearchTool20250305), aiTool.Name);
+ }
+
+ [Fact]
+ public void AsAITool_GetService_ThrowsOnNullServiceType()
+ {
+ AITool aiTool = (
+ (BetaToolUnion)new BetaWebSearchTool20250305() { AllowedDomains = ["example.com"] }
+ ).AsAITool();
+ Assert.Throws(() => aiTool.GetService(null!, null));
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithHostedFileContent()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "document",
+ "source": {
+ "type": "file",
+ "file_id": "file_abc123"
+ }
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_hosted_file_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I read the hosted file."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 20,
+ "output_tokens": 6
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ var hostedFile = new HostedFileContent("file_abc123");
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.User, [hostedFile])]
+ );
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithHostedCodeInterpreterTool()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Execute code"
+ }]
+ }],
+ "tools": [{
+ "type": "code_execution_20250825",
+ "name": "code_execution"
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_code_exec_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I can execute code."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 6
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new() { Tools = [new HostedCodeInterpreterTool()] };
+
+ ChatResponse response = await chatClient.GetResponseAsync("Execute code", options);
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 2048,
+ "model": "claude-haiku-4-5",
+ "messages": [
+ {
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Preconfigured message"
+ }]
+ },
+ {
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "New message"
+ }]
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_factory_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 20,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 2048,
+ Model = "claude-haiku-4-5",
+ Messages =
+ [
+ new BetaMessageParam()
+ {
+ Role = Role.User,
+ Content = new BetaMessageParamContent(
+ [new BetaTextBlockParam() { Text = "Preconfigured message" }]
+ ),
+ },
+ ],
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync("New message", options);
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory_SystemMessagesMerged()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test"
+ }]
+ }],
+ "system": [
+ {
+ "type": "text",
+ "text": "Existing system message"
+ },
+ {
+ "type": "text",
+ "text": "New system message"
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_sys_merge_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 1024,
+ Model = "claude-haiku-4-5",
+ Messages = [],
+ System = "Existing system message",
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [
+ new ChatMessage(ChatRole.System, "New system message"),
+ new ChatMessage(ChatRole.User, "Test"),
+ ],
+ options
+ );
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory_SystemMessagesListMerged()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test"
+ }]
+ }],
+ "system": [
+ {
+ "type": "text",
+ "text": "First"
+ },
+ {
+ "type": "text",
+ "text": "Second"
+ },
+ {
+ "type": "text",
+ "text": "Third"
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_sys_list_merge_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 1024,
+ Model = "claude-haiku-4-5",
+ Messages = [],
+ System = new System.Collections.Generic.List
+ {
+ new() { Text = "First" },
+ new() { Text = "Second" },
+ },
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.System, "Third"), new ChatMessage(ChatRole.User, "Test")],
+ options
+ );
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_McpToolResultWithTextList()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test MCP text list"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_mcp_text_list_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "mcp_tool_result",
+ "tool_use_id": "mcp_call_789",
+ "is_error": false,
+ "content": [{
+ "type": "text",
+ "text": "First result"
+ }, {
+ "type": "text",
+ "text": "Second result"
+ }]
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 15
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Test MCP text list");
+
+ McpServerToolResultContent mcpResult = Assert.IsType(
+ response.Messages[0].Contents[0]
+ );
+ Assert.NotNull(mcpResult);
+ Assert.Equal("mcp_call_789", mcpResult.CallId);
+ Assert.NotNull(mcpResult.Output);
+ Assert.Equal(2, mcpResult.Output.Count);
+ Assert.Equal("First result", ((TextContent)mcpResult.Output[0]).Text);
+ Assert.Equal("Second result", ((TextContent)mcpResult.Output[1]).Text);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_CodeExecutionResult_WithStdout()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Run code"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_code_stdout_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "code_execution_tool_result",
+ "tool_use_id": "code_exec_1",
+ "content": {
+ "type": "code_execution_result",
+ "stdout": "Hello World\n42\n",
+ "stderr": "",
+ "return_code": 0,
+ "content": []
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Run code");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult);
+ Assert.Equal("code_exec_1", codeResult.CallId);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Single(codeResult.Outputs);
+
+ TextContent textOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("Hello World\n42\n", textOutput.Text);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_CodeExecutionResult_WithStderrAndNonZeroReturnCode()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Run failing code"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_code_stderr_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "code_execution_tool_result",
+ "tool_use_id": "code_exec_2",
+ "content": {
+ "type": "code_execution_result",
+ "stdout": "",
+ "stderr": "Division by zero error",
+ "return_code": 1,
+ "content": []
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Run failing code");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Single(codeResult.Outputs);
+
+ ErrorContent errorOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("Division by zero error", errorOutput.Message);
+ Assert.Equal("1", errorOutput.ErrorCode);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_CodeExecutionResult_WithFileOutputs()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Create file"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_code_files_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "code_execution_tool_result",
+ "tool_use_id": "code_exec_3",
+ "content": {
+ "type": "code_execution_result",
+ "stdout": "File created",
+ "stderr": "",
+ "return_code": 0,
+ "content": [{
+ "type": "code_execution_output",
+ "file_id": "file_output_123"
+ }, {
+ "type": "code_execution_output",
+ "file_id": "file_output_456"
+ }]
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Create file");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Equal(3, codeResult.Outputs.Count);
+
+ TextContent textOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("File created", textOutput.Text);
+
+ HostedFileContent fileOutput1 = Assert.IsType(codeResult.Outputs[1]);
+ Assert.Equal("file_output_123", fileOutput1.FileId);
+
+ HostedFileContent fileOutput2 = Assert.IsType(codeResult.Outputs[2]);
+ Assert.Equal("file_output_456", fileOutput2.FileId);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_BashCodeExecutionResult_WithStdout()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Run bash"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_bash_stdout_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "bash_code_execution_tool_result",
+ "tool_use_id": "bash_exec_1",
+ "content": {
+ "type": "bash_code_execution_result",
+ "stdout": "Hello from bash\n5\n",
+ "stderr": "",
+ "return_code": 0,
+ "content": []
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Run bash");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult);
+ Assert.Equal("bash_exec_1", codeResult.CallId);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Single(codeResult.Outputs);
+
+ TextContent textOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("Hello from bash\n5\n", textOutput.Text);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_BashCodeExecutionResult_WithStderrAndNonZeroReturnCode()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Run failing bash"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_bash_stderr_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "bash_code_execution_tool_result",
+ "tool_use_id": "bash_exec_2",
+ "content": {
+ "type": "bash_code_execution_result",
+ "stdout": "",
+ "stderr": "bash: command not found: nonexistent",
+ "return_code": 127,
+ "content": []
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Run failing bash");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Single(codeResult.Outputs);
+
+ ErrorContent errorOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("bash: command not found: nonexistent", errorOutput.Message);
+ Assert.Equal("127", errorOutput.ErrorCode);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_BashCodeExecutionResult_WithFileOutputs()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Create files with bash"
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_bash_files_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "bash_code_execution_tool_result",
+ "tool_use_id": "bash_exec_3",
+ "content": {
+ "type": "bash_code_execution_result",
+ "stdout": "Files created successfully",
+ "stderr": "",
+ "return_code": 0,
+ "content": [{
+ "type": "bash_code_execution_output",
+ "file_id": "file_bash_123"
+ }, {
+ "type": "bash_code_execution_output",
+ "file_id": "file_bash_456"
+ }]
+ }
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+ ChatResponse response = await chatClient.GetResponseAsync("Create files with bash");
+
+ CodeInterpreterToolResultContent codeResult =
+ Assert.IsType(response.Messages[0].Contents[0]);
+ Assert.NotNull(codeResult.Outputs);
+ Assert.Equal(3, codeResult.Outputs.Count);
+
+ TextContent textOutput = Assert.IsType(codeResult.Outputs[0]);
+ Assert.Equal("Files created successfully", textOutput.Text);
+
+ HostedFileContent fileOutput1 = Assert.IsType(codeResult.Outputs[1]);
+ Assert.Equal("file_bash_123", fileOutput1.FileId);
+
+ HostedFileContent fileOutput2 = Assert.IsType(codeResult.Outputs[2]);
+ Assert.Equal("file_bash_456", fileOutput2.FileId);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithHostedTools_AddsBetaHeaders()
+ {
+ IEnumerable? capturedBetaHeaders = null;
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Use both tools"
+ }]
+ }],
+ "tools": [{
+ "type": "code_execution_20250825",
+ "name": "code_execution"
+ }],
+ "mcp_servers": [{
+ "name": "mcp",
+ "type": "url",
+ "url": "https://mcp.example.com/server"
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_both_beta_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I have access to both tools."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 25,
+ "output_tokens": 10
+ }
+ }
+ """
+ )
+ {
+ OnRequestHeaders = headers =>
+ headers.TryGetValues("anthropic-beta", out capturedBetaHeaders),
+ };
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ Tools =
+ [
+ new HostedCodeInterpreterTool(),
+ new HostedMcpServerTool("my-mcp-server", new Uri("https://mcp.example.com/server")),
+ ],
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync("Use both tools", options);
+ Assert.NotNull(response);
+ Assert.NotNull(capturedBetaHeaders);
+ Assert.Contains("code-execution-2025-08-25", capturedBetaHeaders);
+ Assert.Contains("mcp-client-2025-11-20", capturedBetaHeaders);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithHostedToolsAndExistingBetas_PreservesAndDeduplicatesBetas()
+ {
+ IEnumerable? capturedBetaHeaders = null;
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test"
+ }]
+ }],
+ "tools": [{
+ "type": "code_execution_20250825",
+ "name": "code_execution"
+ }],
+ "mcp_servers": [{
+ "name": "mcp",
+ "type": "url",
+ "url": "https://mcp.example.com/server"
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_preserve_beta_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 10,
+ "output_tokens": 5
+ }
+ }
+ """
+ )
+ {
+ OnRequestHeaders = headers =>
+ headers.TryGetValues("anthropic-beta", out capturedBetaHeaders),
+ };
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 1024,
+ Model = "claude-haiku-4-5",
+ Messages = [],
+ Betas = ["custom-beta-feature", "code-execution-2025-08-25"],
+ },
+ Tools =
+ [
+ new HostedCodeInterpreterTool(),
+ new HostedMcpServerTool("my-mcp-server", new Uri("https://mcp.example.com/server")),
+ ],
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync("Test", options);
+ Assert.NotNull(response);
+ Assert.NotNull(capturedBetaHeaders);
+ Assert.Equal(3, capturedBetaHeaders.Count());
+ Assert.Contains("custom-beta-feature", capturedBetaHeaders);
+ Assert.Contains("code-execution-2025-08-25", capturedBetaHeaders);
+ Assert.Contains("mcp-client-2025-11-20", capturedBetaHeaders);
+ }
}
diff --git a/src/Anthropic.Tests/AnthropicClientExtensionsTests.cs b/src/Anthropic.Tests/AnthropicClientExtensionsTests.cs
index 02ff03ef..3443c7d8 100644
--- a/src/Anthropic.Tests/AnthropicClientExtensionsTests.cs
+++ b/src/Anthropic.Tests/AnthropicClientExtensionsTests.cs
@@ -1,4 +1,7 @@
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using Anthropic;
using Anthropic.Models.Messages;
@@ -222,4 +225,241 @@ public async Task GetResponseAsync_WithToolUnionAsAITool_FlowsThroughToRequest()
ChatResponse response = await chatClient.GetResponseAsync("Search the web", options);
Assert.NotNull(response);
}
+
+ [Fact]
+ public void AsAITool_GetService_ReturnsToolUnion()
+ {
+ ToolUnion toolUnion = new WebSearchTool20250305() { AllowedDomains = ["example.com"] };
+ AITool aiTool = toolUnion.AsAITool();
+
+ Assert.Same(toolUnion, aiTool.GetService());
+
+ Assert.Null(aiTool.GetService("key"));
+ Assert.Null(aiTool.GetService());
+
+ Assert.NotNull(aiTool.Name);
+ Assert.Contains(nameof(WebSearchTool20250305), aiTool.Name);
+ }
+
+ [Fact]
+ public void AsAITool_GetService_ThrowsOnNullServiceType()
+ {
+ AITool aiTool = (
+ (ToolUnion)new WebSearchTool20250305() { AllowedDomains = ["example.com"] }
+ ).AsAITool();
+ Assert.Throws(() => aiTool.GetService(null!, null));
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 2048,
+ "model": "claude-haiku-4-5",
+ "messages": [
+ {
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Preconfigured message"
+ }]
+ },
+ {
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "New message"
+ }]
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_factory_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 20,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 2048,
+ Model = "claude-haiku-4-5",
+ Messages =
+ [
+ new MessageParam()
+ {
+ Role = Role.User,
+ Content = new MessageParamContent(
+ [new TextBlockParam() { Text = "Preconfigured message" }]
+ ),
+ },
+ ],
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync("New message", options);
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory_SystemMessagesMerged()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test"
+ }]
+ }],
+ "system": [
+ {
+ "type": "text",
+ "text": "Existing system message"
+ },
+ {
+ "type": "text",
+ "text": "New system message"
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_sys_merge_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 1024,
+ Model = "claude-haiku-4-5",
+ Messages = [],
+ System = "Existing system message",
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [
+ new ChatMessage(ChatRole.System, "New system message"),
+ new ChatMessage(ChatRole.User, "Test"),
+ ],
+ options
+ );
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithRawRepresentationFactory_SystemMessagesListMerged()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Test"
+ }]
+ }],
+ "system": [
+ {
+ "type": "text",
+ "text": "First"
+ },
+ {
+ "type": "text",
+ "text": "Second"
+ },
+ {
+ "type": "text",
+ "text": "Third"
+ }
+ ]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_sys_list_merge_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "Response"
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ ChatOptions options = new()
+ {
+ RawRepresentationFactory = _ => new MessageCreateParams()
+ {
+ MaxTokens = 1024,
+ Model = "claude-haiku-4-5",
+ Messages = [],
+ System = new System.Collections.Generic.List
+ {
+ new() { Text = "First" },
+ new() { Text = "Second" },
+ },
+ },
+ };
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.System, "Third"), new ChatMessage(ChatRole.User, "Test")],
+ options
+ );
+ Assert.NotNull(response);
+ }
}
diff --git a/src/Anthropic.Tests/AnthropicClientExtensionsTestsBase.cs b/src/Anthropic.Tests/AnthropicClientExtensionsTestsBase.cs
index d5e90f27..fc903291 100644
--- a/src/Anthropic.Tests/AnthropicClientExtensionsTestsBase.cs
+++ b/src/Anthropic.Tests/AnthropicClientExtensionsTestsBase.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
+using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -54,6 +55,51 @@ public void AsIChatClient_GetService_ReturnsKnownTypes(string? defaultModelId)
Assert.Null(chatClient.GetService());
}
+ [Fact]
+ public void AsIChatClient_GetService_ThrowsOnNullServiceType()
+ {
+ IChatClient chatClient = CreateChatClient(
+ new VerbatimHttpHandler("", ""),
+ "claude-haiku-4-5"
+ );
+ Assert.Throws(() => chatClient.GetService(null!, null));
+ }
+
+ [Fact]
+ public void AsIChatClient_GetService_ReturnsNullWithNonNullServiceKey()
+ {
+ IChatClient chatClient = CreateChatClient(
+ new VerbatimHttpHandler("", ""),
+ "claude-haiku-4-5"
+ );
+ Assert.Null(chatClient.GetService(typeof(string), "someKey"));
+ }
+
+ [Fact]
+ public void AsIChatClient_GetService_ReturnsMetadata()
+ {
+ AnthropicClient client = new() { APIKey = "test-key" };
+ IChatClient chatClient = CreateChatClient(client, "claude-haiku-4-5");
+
+ var metadata = chatClient.GetService();
+
+ Assert.NotNull(metadata);
+ Assert.Equal("anthropic", metadata.ProviderName);
+ Assert.Equal("claude-haiku-4-5", metadata.DefaultModelId);
+ }
+
+ [Fact]
+ public void AsIChatClient_GetService_ReturnsSelf()
+ {
+ AnthropicClient client = new() { APIKey = "test-key" };
+ IChatClient chatClient = CreateChatClient(client, "claude-haiku-4-5");
+
+ var self = chatClient.GetService();
+
+ Assert.NotNull(self);
+ Assert.Same(chatClient, self);
+ }
+
[Fact]
public void IChatClient_Dispose_Nop()
{
@@ -635,28 +681,154 @@ public async Task GetStreamingResponseAsync_BasicTextCompletion()
}
[Fact]
- public void AsIChatClient_GetService_ReturnsMetadata()
+ public async Task GetResponseAsync_WithTextDataContent()
{
- AnthropicClient client = new() { APIKey = "test-key" };
- IChatClient chatClient = CreateChatClient(client, "claude-haiku-4-5");
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "document",
+ "source": {
+ "type": "text",
+ "media_type": "text/plain",
+ "data": "Sample text content"
+ }
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_text_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I read the text."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 15,
+ "output_tokens": 5
+ }
+ }
+ """
+ );
- var metadata = chatClient.GetService();
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
- Assert.NotNull(metadata);
- Assert.Equal("anthropic", metadata.ProviderName);
- Assert.Equal("claude-haiku-4-5", metadata.DefaultModelId);
+ var dataContent = new DataContent(
+ Encoding.UTF8.GetBytes("Sample text content"),
+ "text/plain"
+ );
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.User, [dataContent])]
+ );
+ Assert.NotNull(response);
}
[Fact]
- public void AsIChatClient_GetService_ReturnsSelf()
+ public async Task GetResponseAsync_WithImageUriContent()
{
- AnthropicClient client = new() { APIKey = "test-key" };
- IChatClient chatClient = CreateChatClient(client, "claude-haiku-4-5");
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "image",
+ "source": {
+ "type": "url",
+ "url": "https://example.com/image.jpg"
+ }
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_img_uri_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I see the image."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 20,
+ "output_tokens": 6
+ }
+ }
+ """
+ );
- var self = chatClient.GetService();
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
- Assert.NotNull(self);
- Assert.Same(chatClient, self);
+ var imageUri = new UriContent(new Uri("https://example.com/image.jpg"), "image/jpeg");
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.User, [imageUri])]
+ );
+ Assert.NotNull(response);
+ }
+
+ [Fact]
+ public async Task GetResponseAsync_WithPdfUriContent()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "document",
+ "source": {
+ "type": "url",
+ "url": "https://example.com/document.pdf"
+ }
+ }]
+ }]
+ }
+ """,
+ actualResponse: """
+ {
+ "id": "msg_pdf_01",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-haiku-4-5",
+ "content": [{
+ "type": "text",
+ "text": "I analyzed the PDF."
+ }],
+ "stop_reason": "end_turn",
+ "usage": {
+ "input_tokens": 20,
+ "output_tokens": 6
+ }
+ }
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ var pdfUri = new UriContent(new Uri("https://example.com/document.pdf"), "application/pdf");
+
+ ChatResponse response = await chatClient.GetResponseAsync(
+ [new ChatMessage(ChatRole.User, [pdfUri])]
+ );
+ Assert.NotNull(response);
}
[Fact]
@@ -1894,6 +2066,11 @@ public async Task GetStreamingResponseAsync_WithToolCallInputDelta()
.FirstOrDefault();
Assert.NotNull(functionCall);
Assert.Equal("test_tool", functionCall.Name);
+
+ // Verify arguments were properly accumulated from multiple delta events
+ Assert.NotNull(functionCall.Arguments);
+ Assert.True(functionCall.Arguments.ContainsKey("arg"));
+ Assert.Equal("value", functionCall.Arguments["arg"]?.ToString());
}
[Fact]
@@ -1958,6 +2135,317 @@ var update in chatClient.GetStreamingResponseAsync("Call parameterless tool")
Assert.True(functionCall.Arguments == null || functionCall.Arguments.Count == 0);
}
+ [Fact]
+ public async Task GetStreamingResponseAsync_WithMultipleToolCalls_DoesNotDuplicateFunctionCalls()
+ {
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Call multiple tools"
+ }]
+ }],
+ "stream": true
+ }
+ """,
+ actualResponse: """
+ event: message_start
+ data: {"type":"message_start","message":{"id":"msg_multi_tool_01","type":"message","role":"assistant","model":"claude-haiku-4-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":0}}}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_1","name":"tool_a","input":{}}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"arg\":\"a\"}"}}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":0}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_2","name":"tool_b","input":{}}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"arg\":\"b\"}"}}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":1}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_3","name":"tool_c","input":{}}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"arg\":\"c\"}"}}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":2}
+
+ event: message_delta
+ data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":15}}
+
+ event: message_stop
+ data: {"type":"message_stop"}
+
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ List updates = [];
+ await foreach (var update in chatClient.GetStreamingResponseAsync("Call multiple tools"))
+ {
+ updates.Add(update);
+ }
+
+ var allFunctionCalls = updates
+ .SelectMany(u => u.Contents.OfType())
+ .ToList();
+
+ Assert.Equal(3, allFunctionCalls.Count);
+
+ var fccA = allFunctionCalls.First(fc => fc.Name == "tool_a");
+ Assert.Equal("toolu_1", fccA.CallId);
+ Assert.Equal("a", fccA.Arguments?["arg"]?.ToString());
+
+ var fccB = allFunctionCalls.First(fc => fc.Name == "tool_b");
+ Assert.Equal("toolu_2", fccB.CallId);
+ Assert.Equal("b", fccB.Arguments?["arg"]?.ToString());
+
+ var fccC = allFunctionCalls.First(fc => fc.Name == "tool_c");
+ Assert.Equal("toolu_3", fccC.CallId);
+ Assert.Equal("c", fccC.Arguments?["arg"]?.ToString());
+ }
+
+ [Fact]
+ public async Task GetStreamingResponseAsync_WithManyToolCallsAndFragmentedArguments()
+ {
+ // Build a streaming response with 5 tool calls, each with arguments spread across many small delta events
+ var responseBuilder = new StringBuilder();
+ responseBuilder.AppendLine(
+ """
+ event: message_start
+ data: {"type":"message_start","message":{"id":"msg_many_tools_01","type":"message","role":"assistant","model":"claude-haiku-4-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":0}}}
+
+ """
+ );
+
+ // Create 5 tool calls, each with complex arguments spread over multiple deltas
+ for (int toolIndex = 0; toolIndex < 5; toolIndex++)
+ {
+ char toolLetter = (char)('a' + toolIndex);
+ string toolId = $"toolu_{toolIndex + 1}";
+ string toolName = $"tool_{toolLetter}";
+
+ // Start the tool use block
+ responseBuilder.AppendLine(
+ $"event: content_block_start\n"
+ + $"data: {{\"type\":\"content_block_start\",\"index\":{toolIndex},\"content_block\":{{\"type\":\"tool_use\",\"id\":\"{toolId}\",\"name\":\"{toolName}\",\"input\":{{}}}}}}\n"
+ );
+
+ // Build the JSON argument piece by piece
+ string fullJson =
+ $"{{\"name\":\"tool_{toolLetter}_value\",\"count\":{toolIndex + 1},\"description\":\"This is tool {toolLetter} with a longer description to test accumulation\"}}";
+
+ // Split the JSON into small chunks (3 characters each) to simulate realistic streaming
+ int chunkSize = 3;
+ for (int i = 0; i < fullJson.Length; i += chunkSize)
+ {
+ string chunk = fullJson.Substring(i, Math.Min(chunkSize, fullJson.Length - i));
+ // Escape the chunk for JSON embedding
+ string escapedChunk = chunk.Replace("\\", "\\\\").Replace("\"", "\\\"");
+ responseBuilder.AppendLine(
+ $"event: content_block_delta\n"
+ + $"data: {{\"type\":\"content_block_delta\",\"index\":{toolIndex},\"delta\":{{\"type\":\"input_json_delta\",\"partial_json\":\"{escapedChunk}\"}}}}\n"
+ );
+ }
+
+ // Stop the content block
+ responseBuilder.AppendLine(
+ $"event: content_block_stop\n"
+ + $"data: {{\"type\":\"content_block_stop\",\"index\":{toolIndex}}}\n"
+ );
+ }
+
+ // End the message
+ responseBuilder.AppendLine(
+ """
+ event: message_delta
+ data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":50}}
+
+ event: message_stop
+ data: {"type":"message_stop"}
+
+ """
+ );
+
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Call many tools"
+ }]
+ }],
+ "stream": true
+ }
+ """,
+ actualResponse: responseBuilder.ToString()
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ List updates = [];
+ await foreach (var update in chatClient.GetStreamingResponseAsync("Call many tools"))
+ {
+ updates.Add(update);
+ }
+
+ var allFunctionCalls = updates
+ .SelectMany(u => u.Contents.OfType())
+ .ToList();
+
+ Assert.Equal(5, allFunctionCalls.Count);
+ for (int i = 0; i < 5; i++)
+ {
+ string expectedCallId = $"toolu_{i + 1}";
+ string expectedName = $"tool_{(char)('a' + i)}";
+ string expectedNameValue = $"tool_{(char)('a' + i)}_value";
+ int expectedCount = i + 1;
+
+ var functionCall = allFunctionCalls.SingleOrDefault(fc => fc.CallId == expectedCallId);
+ Assert.NotNull(functionCall);
+ Assert.Equal(expectedName, functionCall.Name);
+
+ Assert.NotNull(functionCall.Arguments);
+ Assert.Equal(expectedNameValue, functionCall.Arguments["name"]?.ToString());
+ Assert.Equal(expectedCount.ToString(), functionCall.Arguments["count"]?.ToString());
+ Assert.Contains(
+ $"This is tool {(char)('a' + i)}",
+ functionCall.Arguments["description"]?.ToString()
+ );
+ }
+ }
+
+ [Fact]
+ public async Task GetStreamingResponseAsync_WithInterleavedToolCallDeltas()
+ {
+ // This test simulates a scenario where multiple tool calls are being streamed
+ // with their argument deltas interleaved (receiving parts of all tools before any completes)
+ VerbatimHttpHandler handler = new(
+ expectedRequest: """
+ {
+ "max_tokens": 1024,
+ "model": "claude-haiku-4-5",
+ "messages": [{
+ "role": "user",
+ "content": [{
+ "type": "text",
+ "text": "Call interleaved tools"
+ }]
+ }],
+ "stream": true
+ }
+ """,
+ actualResponse: """
+ event: message_start
+ data: {"type":"message_start","message":{"id":"msg_interleaved_01","type":"message","role":"assistant","model":"claude-haiku-4-5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":0}}}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_alpha","name":"tool_alpha","input":{}}}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_beta","name":"tool_beta","input":{}}}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_gamma","name":"tool_gamma","input":{}}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"query\":"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"id\":"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\"San Fran"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"weather "}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"123,\"act"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"cisco\"}"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"forecast\"}"}}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"ive\":true}"}}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":0}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":1}
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":2}
+
+ event: message_delta
+ data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":25}}
+
+ event: message_stop
+ data: {"type":"message_stop"}
+
+ """
+ );
+
+ IChatClient chatClient = CreateChatClient(handler, "claude-haiku-4-5");
+
+ List updates = [];
+ await foreach (var update in chatClient.GetStreamingResponseAsync("Call interleaved tools"))
+ {
+ updates.Add(update);
+ }
+
+ var allFunctionCalls = updates
+ .SelectMany(u => u.Contents.OfType())
+ .ToList();
+
+ Assert.Equal(3, allFunctionCalls.Count);
+
+ var alphaCall = allFunctionCalls.SingleOrDefault(fc => fc.CallId == "toolu_alpha");
+ Assert.NotNull(alphaCall);
+ Assert.Equal("tool_alpha", alphaCall.Name);
+ Assert.NotNull(alphaCall.Arguments);
+ Assert.Equal("San Francisco", alphaCall.Arguments["city"]?.ToString());
+
+ var betaCall = allFunctionCalls.SingleOrDefault(fc => fc.CallId == "toolu_beta");
+ Assert.NotNull(betaCall);
+ Assert.Equal("tool_beta", betaCall.Name);
+ Assert.NotNull(betaCall.Arguments);
+ Assert.Equal("weather forecast", betaCall.Arguments["query"]?.ToString());
+
+ var gammaCall = allFunctionCalls.SingleOrDefault(fc => fc.CallId == "toolu_gamma");
+ Assert.NotNull(gammaCall);
+ Assert.Equal("tool_gamma", gammaCall.Name);
+ Assert.NotNull(gammaCall.Arguments);
+ Assert.Equal("123", gammaCall.Arguments["id"]?.ToString());
+ Assert.Equal("True", gammaCall.Arguments["active"]?.ToString());
+ }
+
[Fact]
public async Task GetResponseAsync_WithAdditionalUsageCounts()
{
@@ -2983,38 +3471,6 @@ var update in chatClient.GetStreamingResponseAsync("Test multiple message starts
Assert.Equal(2, usageContent.Details.OutputTokenCount);
}
- protected sealed class VerbatimHttpHandler(string expectedRequest, string actualResponse)
- : HttpMessageHandler
- {
- protected override async Task SendAsync(
- HttpRequestMessage request,
- CancellationToken cancellationToken
- )
- {
- if (!string.IsNullOrEmpty(expectedRequest))
- {
- Assert.NotNull(request.Content);
- string actualRequest = await request.Content.ReadAsStringAsync(
-#if NET
- cancellationToken
-#endif
- );
- Assert.True(
- JsonNode.DeepEquals(
- JsonNode.Parse(expectedRequest),
- JsonNode.Parse(actualRequest)
- ),
- $"Expected:\n{expectedRequest}\nActual:\n{actualRequest}"
- );
- }
-
- return new()
- {
- Content = new StringContent(actualResponse, Encoding.UTF8, "application/json"),
- };
- }
- }
-
[Fact]
public async Task GetResponseAsync_FunctionResult_WithSingleTextContent()
{
@@ -3833,4 +4289,40 @@ public async Task GetResponseAsync_WithFunctionResultContent_UriContent_PDF()
ChatResponse response = await chatClient.GetResponseAsync(messages);
Assert.NotNull(response);
}
+
+ protected sealed class VerbatimHttpHandler(string expectedRequest, string actualResponse)
+ : HttpMessageHandler
+ {
+ public Action? OnRequestHeaders { get; set; }
+
+ protected override async Task SendAsync(
+ HttpRequestMessage request,
+ CancellationToken cancellationToken
+ )
+ {
+ OnRequestHeaders?.Invoke(request.Headers);
+
+ if (!string.IsNullOrEmpty(expectedRequest))
+ {
+ Assert.NotNull(request.Content);
+ string actualRequest = await request.Content.ReadAsStringAsync(
+#if NET
+ cancellationToken
+#endif
+ );
+ Assert.True(
+ JsonNode.DeepEquals(
+ JsonNode.Parse(expectedRequest),
+ JsonNode.Parse(actualRequest)
+ ),
+ $"Expected:\n{expectedRequest}\nActual:\n{actualRequest}"
+ );
+ }
+
+ return new()
+ {
+ Content = new StringContent(actualResponse, Encoding.UTF8, "application/json"),
+ };
+ }
+ }
}
diff --git a/src/Anthropic.Tests/AnthropicTestClients.cs b/src/Anthropic.Tests/AnthropicTestClients.cs
index 7172c476..337cd252 100644
--- a/src/Anthropic.Tests/AnthropicTestClients.cs
+++ b/src/Anthropic.Tests/AnthropicTestClients.cs
@@ -9,6 +9,11 @@ namespace Anthropic.Tests;
public class AnthropicTestClientsAttribute : DataAttribute
{
+ public static string DataServiceUrl { get; } =
+ Environment.GetEnvironmentVariable("TEST_API_BASE_URL") ?? "http://localhost:4010";
+ public static string ApiKey { get; } = "YourApiKeyHere";
+ public static string Resource { get; } = "YourRegionOrResourceHere";
+
public AnthropicTestClientsAttribute(TestSupportTypes testSupportTypes = TestSupportTypes.All)
{
TestSupportTypes = testSupportTypes;
@@ -18,17 +23,12 @@ public AnthropicTestClientsAttribute(TestSupportTypes testSupportTypes = TestSup
public override IEnumerable