Merge commit '36c66dd290d3ce6eb1ccd310d0c658d4a32bb8eb' as 'repos/effect'

This commit is contained in:
-Puter
2026-07-19 03:25:10 +05:30
parent dd1071cc10
commit 2daf979036
2214 changed files with 673090 additions and 0 deletions

View File

@@ -0,0 +1,736 @@
# @effect/ai-anthropic
## 4.0.0-beta.99
### Patch Changes
- [#6371](https://github.com/Effect-TS/effect/pull/6371) [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd) Thanks @polRk! - Fix client-executed provider tools (Memory, Text Editor, Computer Use, Bash) which were unusable on the wire.
- `makeResponse` (and the streaming equivalents) now map a provider `tool_use` wire name (e.g. `"memory"`) back to the tool's custom name (e.g. `"AnthropicMemory"`) that the toolkit is keyed by, instead of raising `ToolNotFoundError`.
- `AnthropicTool.MemoryCreateCommand` now includes the required `file_text` field, so a `create` command no longer drops the file body.
- Optional parameters on client-executed provider tools now use `Schema.optionalKey` instead of `Schema.optional`, which the Anthropic codec rejected with "Unsupported AST Undefined": `Memory`/`TextEditor` `view_range`, `ComputerUse` `coordinate`, and `Bash` `restart`.
Closes [#2615](https://github.com/Effect-TS/effect/issues/2615).
- Updated dependencies [[`8ce4795`](https://github.com/Effect-TS/effect/commit/8ce4795ccbaebca4292757db568c005a992546a4), [`80b539f`](https://github.com/Effect-TS/effect/commit/80b539f8aba68f478c75c35c2b4140c4ffc4fada), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`e6e6dba`](https://github.com/Effect-TS/effect/commit/e6e6dba6e9d86e7c2ad27dcedf289db76a19697f), [`bfb203e`](https://github.com/Effect-TS/effect/commit/bfb203e95aa439f731acad37fc3a9a831a190f1c), [`2e9a34a`](https://github.com/Effect-TS/effect/commit/2e9a34ac2bece4f3a206160480c991e3841dc67a), [`55d4eb3`](https://github.com/Effect-TS/effect/commit/55d4eb34f2c64d54f6a25a305b5c5438ebd7934e), [`bddb010`](https://github.com/Effect-TS/effect/commit/bddb010eac3d4436cb094edbbee7460c5440c162), [`a328835`](https://github.com/Effect-TS/effect/commit/a328835e50d76bc96648a1c1550456e8c9f81210), [`5560d05`](https://github.com/Effect-TS/effect/commit/5560d05aa6abdd29466d9c3412cc5e648b0adbde), [`8f6e3ad`](https://github.com/Effect-TS/effect/commit/8f6e3adb185b16e8820b98c509b308086f7ff1af), [`46997fa`](https://github.com/Effect-TS/effect/commit/46997fa60401f5e3c93daa4b61f7df8e31caaab4), [`9e6e12d`](https://github.com/Effect-TS/effect/commit/9e6e12d75c118cd265496f2880490d1f33a5c8bf), [`3394b93`](https://github.com/Effect-TS/effect/commit/3394b93d97d6f24fc38670641d1490289ffca7f1), [`febeabc`](https://github.com/Effect-TS/effect/commit/febeabc3f7c31094da000a23edeaabfe2ab00a38), [`54161c9`](https://github.com/Effect-TS/effect/commit/54161c98f6f3569e0c31842f54e6a257f9421c4c), [`385f7a4`](https://github.com/Effect-TS/effect/commit/385f7a4ee4a7359928597ea56d151dbaf5eb5802), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd), [`44b9cf3`](https://github.com/Effect-TS/effect/commit/44b9cf3d240d726997b4bbcd0ede48e074d3c456), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`0a8aa6a`](https://github.com/Effect-TS/effect/commit/0a8aa6acb90a72b91c24d17133c950e4cacd8abd), [`c8d9fcf`](https://github.com/Effect-TS/effect/commit/c8d9fcf7b030f7c474effbab2764ce7aee1c7209), [`9ca7f9a`](https://github.com/Effect-TS/effect/commit/9ca7f9a69363e4485645966d5a93b8f9597c5206), [`e7aca89`](https://github.com/Effect-TS/effect/commit/e7aca894bb32fbb785b5830837e6061c415a6015), [`55d7560`](https://github.com/Effect-TS/effect/commit/55d75609b8acf8a1b54c1b1c7fbbb65ec741aa3e), [`f809189`](https://github.com/Effect-TS/effect/commit/f809189ddf6b6011ba43a9901baaa734e315da2a), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`0ebdbe7`](https://github.com/Effect-TS/effect/commit/0ebdbe74463dc84385956d0b1e8c2b79ebab5400), [`7517d09`](https://github.com/Effect-TS/effect/commit/7517d09f12a0b183a81bd425962c4e280a68b05d), [`212493b`](https://github.com/Effect-TS/effect/commit/212493b9a1eb98cd1ef6959c707a2e5784a5ae91), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`80ea8cb`](https://github.com/Effect-TS/effect/commit/80ea8cb9222ca73f564c8267ab2f82966fea027a), [`8df19f4`](https://github.com/Effect-TS/effect/commit/8df19f4fe81d90cc33ace88b9a77e5534f82d604)]:
- effect@4.0.0-beta.99
## 4.0.0-beta.98
### Patch Changes
- Updated dependencies [[`989603b`](https://github.com/Effect-TS/effect-smol/commit/989603b60ab1197b64acf214208e0d370cd1f842), [`214c458`](https://github.com/Effect-TS/effect-smol/commit/214c458084bb6995d543cd37d1055f24be3d454e), [`a037273`](https://github.com/Effect-TS/effect-smol/commit/a0372736ac34796969b051bbba4717d7983f1ebe), [`97fdaa9`](https://github.com/Effect-TS/effect-smol/commit/97fdaa9c1f522c65e579365d314a07878e2b904f), [`b24d248`](https://github.com/Effect-TS/effect-smol/commit/b24d248c8df44222ce642087cde2bd859a2dc709), [`19c222c`](https://github.com/Effect-TS/effect-smol/commit/19c222cac2353a3d7b7733caecb00556fffe9a5c), [`eec85dd`](https://github.com/Effect-TS/effect-smol/commit/eec85ddba09ea326fd268ee33eeffd47e50d4671), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`8849052`](https://github.com/Effect-TS/effect-smol/commit/884905232d1e9a365e046d8dde27bf9c5707f57f), [`c15e16a`](https://github.com/Effect-TS/effect-smol/commit/c15e16ad130d1fbde25d912b7ac55995066cb35b), [`01d00a3`](https://github.com/Effect-TS/effect-smol/commit/01d00a3abfbf1f37996cdbe738ea5137c646cdd7), [`8bd4589`](https://github.com/Effect-TS/effect-smol/commit/8bd458975a1b3a8ed042eccf317b93d28ded91e7), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`6e08428`](https://github.com/Effect-TS/effect-smol/commit/6e08428d980501b856f846ad3f3f0e4ea46e7786), [`388dcf9`](https://github.com/Effect-TS/effect-smol/commit/388dcf953f65d317547f34d40e6443c5f264205f), [`2b7ce2b`](https://github.com/Effect-TS/effect-smol/commit/2b7ce2b513e7ec2a77822f1116dc6ffb6ba93f4e), [`87bea7e`](https://github.com/Effect-TS/effect-smol/commit/87bea7e16259246f3bcdf565446394751abca953), [`ce38dc3`](https://github.com/Effect-TS/effect-smol/commit/ce38dc33bda805a684432cca071f4dc3c6b9a1ba), [`a807cd1`](https://github.com/Effect-TS/effect-smol/commit/a807cd170341deca8a1cfb52c4222585f2431bb9), [`fd8a356`](https://github.com/Effect-TS/effect-smol/commit/fd8a356f06a8c9ce4e7e0a13fc4021c178ed31de), [`c2a5edc`](https://github.com/Effect-TS/effect-smol/commit/c2a5edc3abd31ad5bc123362bc1213e03e4095c3), [`5946da3`](https://github.com/Effect-TS/effect-smol/commit/5946da3804a1be5e752b05b96bd058cdba50a1bf), [`4ae0c5f`](https://github.com/Effect-TS/effect-smol/commit/4ae0c5ffcbe6c56ddfcb05c639112a079483539e), [`5b2a0bc`](https://github.com/Effect-TS/effect-smol/commit/5b2a0bceea3a28a33a58555210c90a415dc74a76), [`72ac585`](https://github.com/Effect-TS/effect-smol/commit/72ac585884befde6af9208da738699a93f1bae79), [`5e8c1b8`](https://github.com/Effect-TS/effect-smol/commit/5e8c1b82bfafa121311f987a49ab75395e3647a7), [`0f9c078`](https://github.com/Effect-TS/effect-smol/commit/0f9c07841b04183f485ee6e6458de73b290b09f5)]:
- effect@4.0.0-beta.98
## 4.0.0-beta.97
### Patch Changes
- [#2568](https://github.com/Effect-TS/effect-smol/pull/2568) [`97a4138`](https://github.com/Effect-TS/effect-smol/commit/97a4138f9e9d31d38026f935b52d423f5ccadc4f) Thanks @K-Mistele! - Widen the Anthropic `Model` schema to accept both known model identifiers as well
as any string to allow for newer models
- Updated dependencies []:
- effect@4.0.0-beta.97
## 4.0.0-beta.96
### Patch Changes
- Updated dependencies [[`1503f45`](https://github.com/Effect-TS/effect-smol/commit/1503f45cb5bb2a74f4705252ec505a1f0ade7e62), [`57fe793`](https://github.com/Effect-TS/effect-smol/commit/57fe79316ffbc380b30626a168981fb26ae97459), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`97f29df`](https://github.com/Effect-TS/effect-smol/commit/97f29df457f7ffd07cfb4b379315c12c086af805)]:
- effect@4.0.0-beta.96
## 4.0.0-beta.95
### Patch Changes
- [#2531](https://github.com/Effect-TS/effect-smol/pull/2531) [`bdca354`](https://github.com/Effect-TS/effect-smol/commit/bdca35449d5dfce5b4433da75ec0a88d0a9b2b27) Thanks @polRk! - Fix `Memory_20250818` provider-defined tool missing `requiresHandler: true`. Like the other client-executed tools (`TextEditor_20250728`, `Bash_2025*`, `ComputerUse_2025*`), the memory tool requires the application to implement its execution (view/create/str_replace/insert/delete/rename over `/memories/*`). Without this flag, `Tool.HandlersFor` excluded it from the required handlers, making it impossible to type-check a handler for `Memory_20250818` in `Toolkit.toLayer`.
- Updated dependencies [[`a482442`](https://github.com/Effect-TS/effect-smol/commit/a482442abdeb490e9652b854ec3495e4aa7273e7), [`fbefa85`](https://github.com/Effect-TS/effect-smol/commit/fbefa850fab2f0a302c20614496aeaaa2a8b5590), [`0b4a32f`](https://github.com/Effect-TS/effect-smol/commit/0b4a32f4260f0d8500942a133001b0d349328102), [`18a49e1`](https://github.com/Effect-TS/effect-smol/commit/18a49e1786679456258002ff9397faf02f678c2d), [`266cb90`](https://github.com/Effect-TS/effect-smol/commit/266cb90bb2c17aabc40563c32db334f09ba3d74b), [`912f095`](https://github.com/Effect-TS/effect-smol/commit/912f095a34572bbd3cedf6edb27878443e3e4a95), [`a6718f9`](https://github.com/Effect-TS/effect-smol/commit/a6718f9e00a15ca903b0732da46116cbf3d6aca7), [`bef5154`](https://github.com/Effect-TS/effect-smol/commit/bef51540a243aa2f872a00c01d0cd58b7a769baa), [`18e0564`](https://github.com/Effect-TS/effect-smol/commit/18e0564bd0f8ebbdfcaf1e2c21529948e9e4a81d), [`fb50f14`](https://github.com/Effect-TS/effect-smol/commit/fb50f14fc3657c1973785aa5b72ecf0b0d28e0b2)]:
- effect@4.0.0-beta.95
## 4.0.0-beta.94
### Patch Changes
- Updated dependencies [[`95a0e9b`](https://github.com/Effect-TS/effect-smol/commit/95a0e9bb62797af0e81c9998773405f248f218c5), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63), [`f11ce73`](https://github.com/Effect-TS/effect-smol/commit/f11ce73af60823754dc24194f4ffc561b9ea1c2d), [`ff30b6e`](https://github.com/Effect-TS/effect-smol/commit/ff30b6e7c2c63ffc56a4c5818d6d86b01b5ad528), [`1caab3c`](https://github.com/Effect-TS/effect-smol/commit/1caab3cc30f626efbf15e59d74f539a487e5c85c), [`aa80c47`](https://github.com/Effect-TS/effect-smol/commit/aa80c4775a04db87553e5568764cab7e32a72814), [`c2ae4fc`](https://github.com/Effect-TS/effect-smol/commit/c2ae4fce2f03a4cd1861c2b1179da7df656e662d), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63)]:
- effect@4.0.0-beta.94
## 4.0.0-beta.93
### Patch Changes
- Updated dependencies [[`00652fe`](https://github.com/Effect-TS/effect-smol/commit/00652fe95c18f87208e91343eb8bf218faa2f677), [`6c58167`](https://github.com/Effect-TS/effect-smol/commit/6c5816746eaf91d2a3c7c899c5720809fa230ae3), [`2bc5415`](https://github.com/Effect-TS/effect-smol/commit/2bc541501a7ef89e542d7cb98e96beb53cd205cc), [`e11cccc`](https://github.com/Effect-TS/effect-smol/commit/e11cccc7d5fe631abccc7d6e3bd296938de0fa2e), [`ba7e77e`](https://github.com/Effect-TS/effect-smol/commit/ba7e77e046b8641a3a4e9750bb88ca4a1d063d3f), [`5713ee7`](https://github.com/Effect-TS/effect-smol/commit/5713ee7edbc3054efde407b2286bbfd45bbc6e1c)]:
- effect@4.0.0-beta.93
## 4.0.0-beta.92
### Patch Changes
- Updated dependencies [[`affdc13`](https://github.com/Effect-TS/effect-smol/commit/affdc139045cc325dce321a84a580fdc1b2da7b9)]:
- effect@4.0.0-beta.92
## 4.0.0-beta.91
### Patch Changes
- Updated dependencies [[`b135b25`](https://github.com/Effect-TS/effect-smol/commit/b135b2517fca9e7839734ace3699a7dfa75b9075), [`aaa21a3`](https://github.com/Effect-TS/effect-smol/commit/aaa21a369a171c600db294f2a4f640583043e150), [`3475ee6`](https://github.com/Effect-TS/effect-smol/commit/3475ee6c2bda6b05c6d7a12ce30c8bb840b5b1a6)]:
- effect@4.0.0-beta.91
## 4.0.0-beta.90
### Patch Changes
- Updated dependencies [[`d237fdf`](https://github.com/Effect-TS/effect-smol/commit/d237fdf726481f76eb52a6196e111b24122bc3d5)]:
- effect@4.0.0-beta.90
## 4.0.0-beta.89
### Patch Changes
- Updated dependencies [[`b7d46ab`](https://github.com/Effect-TS/effect-smol/commit/b7d46ab7e1a29d8711817bab583c9febf48a0dad), [`7777e15`](https://github.com/Effect-TS/effect-smol/commit/7777e1540fd3680dd8346723cffec812b9384669), [`5376197`](https://github.com/Effect-TS/effect-smol/commit/5376197ca8e50358a41b1fd3cec27bd1ec680ec6)]:
- effect@4.0.0-beta.89
## 4.0.0-beta.88
### Patch Changes
- [#2450](https://github.com/Effect-TS/effect-smol/pull/2450) [`7b8a549`](https://github.com/Effect-TS/effect-smol/commit/7b8a5493950aa6f9e20024cd7e588838722218f0) Thanks @DomiR! - Fix non-streaming Anthropic responses throwing when a tool call carries `caller` metadata. The mapper emitted `caller.toolId: undefined`, but `ProviderMetadata` is `Record(String, NullOr(Json))` and `undefined` is not a valid Json value, so decoding the model's own response threw `Expected JSON value`. Emit `null` instead, matching the streaming mappers.
- Updated dependencies [[`911f1b8`](https://github.com/Effect-TS/effect-smol/commit/911f1b84790ce42b3a70c95b33e6f6fd9e74de8b), [`8beeeea`](https://github.com/Effect-TS/effect-smol/commit/8beeeea52879d8613a39468848f01c3092bd54d4), [`c306fcf`](https://github.com/Effect-TS/effect-smol/commit/c306fcfeb1ef38455156932a1faf49292b1318da)]:
- effect@4.0.0-beta.88
## 4.0.0-beta.87
### Patch Changes
- Updated dependencies [[`5a0c1a4`](https://github.com/Effect-TS/effect-smol/commit/5a0c1a4faee5707b5cc35e646ff1ffdad70f1956), [`1eea2ea`](https://github.com/Effect-TS/effect-smol/commit/1eea2ea3795ba47316b82b1ac8d4612c0ba389ed)]:
- effect@4.0.0-beta.87
## 4.0.0-beta.86
### Patch Changes
- Updated dependencies [[`0b5795a`](https://github.com/Effect-TS/effect-smol/commit/0b5795a0ab4395e8f15955d8d96f2303084bfc64), [`3e3a859`](https://github.com/Effect-TS/effect-smol/commit/3e3a859ec6351a9e0d31674aabbd48fcefabb12e), [`7dbec24`](https://github.com/Effect-TS/effect-smol/commit/7dbec240dbf3bca599a20c486632abce694ef5ab), [`d8c00a1`](https://github.com/Effect-TS/effect-smol/commit/d8c00a171ac7141e8adc08c332d1162d9a9d56fc), [`85b6317`](https://github.com/Effect-TS/effect-smol/commit/85b631701e935866f2762bd595237aa718370cd9), [`6d0fda0`](https://github.com/Effect-TS/effect-smol/commit/6d0fda0d0cbdfffc523c89c57dfdb1608f84fb12), [`108a933`](https://github.com/Effect-TS/effect-smol/commit/108a9335ff8571928197e5847a09c28ac83d6f46), [`7e1f455`](https://github.com/Effect-TS/effect-smol/commit/7e1f455fab5005d769b939c91e519d450f802cf9), [`46b3e79`](https://github.com/Effect-TS/effect-smol/commit/46b3e79944cfdae7901eb148135c85b7eb39834e)]:
- effect@4.0.0-beta.86
## 4.0.0-beta.85
### Patch Changes
- Updated dependencies [[`328d97c`](https://github.com/Effect-TS/effect-smol/commit/328d97cc53c0dcb89077a5623e35b095eaa59a8c), [`8441836`](https://github.com/Effect-TS/effect-smol/commit/8441836e6dde70e8ae2126be9cefe9b45798b134), [`074e436`](https://github.com/Effect-TS/effect-smol/commit/074e4361091289104cb0ab6959dc3b0ea7794a6a), [`c1dfd60`](https://github.com/Effect-TS/effect-smol/commit/c1dfd60663eb13a58916f3712d877499943b628a), [`2ba316b`](https://github.com/Effect-TS/effect-smol/commit/2ba316bd15fcbf1c50626500d44a2c9b3bec19f5), [`7ce7344`](https://github.com/Effect-TS/effect-smol/commit/7ce7344c41056c79e2ee19ee6a9346c0f1d227c1)]:
- effect@4.0.0-beta.85
## 4.0.0-beta.84
### Patch Changes
- Updated dependencies [[`87f52ba`](https://github.com/Effect-TS/effect-smol/commit/87f52ba16c4370ffa3f84bf8e53038e1419c284e), [`b8ee07f`](https://github.com/Effect-TS/effect-smol/commit/b8ee07ffda8903b5ec2e45a786ddcba59f128fda), [`867c0d7`](https://github.com/Effect-TS/effect-smol/commit/867c0d70a09079b040260d45a1e92ff04dbfbf2f), [`b93bc6c`](https://github.com/Effect-TS/effect-smol/commit/b93bc6c9cb27b909a41d094c97c4f9d25bbc6d6b), [`57d387f`](https://github.com/Effect-TS/effect-smol/commit/57d387f92c30ab63e15e3e641f0a903b65886610), [`bacca41`](https://github.com/Effect-TS/effect-smol/commit/bacca4141c2400effae1eabfdb36c89a459cf246), [`0f8ac79`](https://github.com/Effect-TS/effect-smol/commit/0f8ac7959d29ed68c68ce25aabd6bf0cb7e63ecc), [`25b4482`](https://github.com/Effect-TS/effect-smol/commit/25b448270c01317703f25107e1480d4cd0246d9a), [`9cf3a25`](https://github.com/Effect-TS/effect-smol/commit/9cf3a25c66b0c44a52be9829870c44517ea52db2), [`8def767`](https://github.com/Effect-TS/effect-smol/commit/8def7674b1787f91035298cda4d122937e87ef72)]:
- effect@4.0.0-beta.84
## 4.0.0-beta.83
### Patch Changes
- Updated dependencies [[`1f2e8ce`](https://github.com/Effect-TS/effect-smol/commit/1f2e8ceef09e0a791c850ed2ade01f97089596f9)]:
- effect@4.0.0-beta.83
## 4.0.0-beta.82
### Patch Changes
- Updated dependencies [[`193690b`](https://github.com/Effect-TS/effect-smol/commit/193690b642ea802bbed40d663bd677251bbe9dc3)]:
- effect@4.0.0-beta.82
## 4.0.0-beta.81
### Patch Changes
- Updated dependencies [[`93cb4f8`](https://github.com/Effect-TS/effect-smol/commit/93cb4f8fbfb9e07cb9dc86ce6b155fd1f8167914), [`60341d9`](https://github.com/Effect-TS/effect-smol/commit/60341d9ca744d0473ce3fab621ca9bd225af3a39), [`1105ab5`](https://github.com/Effect-TS/effect-smol/commit/1105ab56cb724212f7ea7b431396ce82e8fd0484), [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36)]:
- effect@4.0.0-beta.81
## 4.0.0-beta.80
### Patch Changes
- Updated dependencies [[`d944330`](https://github.com/Effect-TS/effect-smol/commit/d94433090ee03f426d43e13b883abae4494e55e6), [`f48659f`](https://github.com/Effect-TS/effect-smol/commit/f48659fdcc84930ebc1e5b45b540c0f973389182), [`7652aaa`](https://github.com/Effect-TS/effect-smol/commit/7652aaa3bdbc39f241fe58b54b9a43b713e22e12), [`98630b7`](https://github.com/Effect-TS/effect-smol/commit/98630b7c8f679c352ba6796636c85688fa009d8d), [`90ae23c`](https://github.com/Effect-TS/effect-smol/commit/90ae23cf07284da5e1bcd9dffa882e85df7e617b)]:
- effect@4.0.0-beta.80
## 4.0.0-beta.79
### Patch Changes
- [#2357](https://github.com/Effect-TS/effect-smol/pull/2357) [`3efe308`](https://github.com/Effect-TS/effect-smol/commit/3efe308fcc6ac642c65560efbd1b3afc68052553) Thanks @jbmusso! - Mark the Claude 4-6 generation as supporting native structured output in `getModelCapabilities`. `claude-opus-4-6` and `claude-sonnet-4-6` support Anthropic's constrained-decoding structured output (verified against the live API), but were classified as `supportsStructuredOutput: false`, so `generateObject` fell back to a forced JSON tool instead of requesting `output_config.format` (`json_schema`). `claude-opus-4-7` / `claude-opus-4-8` are classified the same way for when the generated `Model` enum picks them up.
- Updated dependencies [[`b9704dc`](https://github.com/Effect-TS/effect-smol/commit/b9704dc9de9f1649ad502371014fe869b69a49a3), [`a207113`](https://github.com/Effect-TS/effect-smol/commit/a207113f66837bb54416926718a9a7d66774d079), [`5e9b9e2`](https://github.com/Effect-TS/effect-smol/commit/5e9b9e217b164ebfd4a002dd4380b3b1563200c3), [`7c128ae`](https://github.com/Effect-TS/effect-smol/commit/7c128aef458a1e2d224712e51c483c9badad1d44), [`0ada457`](https://github.com/Effect-TS/effect-smol/commit/0ada457c0513d8d908254ab77ebb7d29d2b523d6), [`d7cc5a2`](https://github.com/Effect-TS/effect-smol/commit/d7cc5a2bede3de10943aa0c6bdb4f26836a91efd), [`aad63be`](https://github.com/Effect-TS/effect-smol/commit/aad63becf65e0a6b076e94f8973be7bbe7fbd46f), [`09809f6`](https://github.com/Effect-TS/effect-smol/commit/09809f60f19ec98232f98b33e33e02ecb7e4fbd6), [`2fddda5`](https://github.com/Effect-TS/effect-smol/commit/2fddda5311929f46b61e503f0ade4fc749e8c77d), [`5f21768`](https://github.com/Effect-TS/effect-smol/commit/5f2176833399757c4500d8875b7f2fba0393de75), [`f27003e`](https://github.com/Effect-TS/effect-smol/commit/f27003e00524ff83f20dd9909f62b2f8795efe03)]:
- effect@4.0.0-beta.79
## 4.0.0-beta.78
### Patch Changes
- Updated dependencies [[`7836b8e`](https://github.com/Effect-TS/effect-smol/commit/7836b8eb8bb0f3e04cdf554ee070caccf74f00c1), [`35d49a3`](https://github.com/Effect-TS/effect-smol/commit/35d49a3a09bdba6b513de87ddcead9e61a1042ba), [`4093258`](https://github.com/Effect-TS/effect-smol/commit/40932580e65bafab5f23c5f14b520cb411d0b2cd)]:
- effect@4.0.0-beta.78
## 4.0.0-beta.77
### Patch Changes
- Updated dependencies [[`6e9a5ca`](https://github.com/Effect-TS/effect-smol/commit/6e9a5ca62a61156fd67b2518ad3ab14ac0d25f23), [`302f398`](https://github.com/Effect-TS/effect-smol/commit/302f3984ce206e35d86ddd99d3b72be144850a51)]:
- effect@4.0.0-beta.77
## 4.0.0-beta.76
### Patch Changes
- Updated dependencies [[`016108a`](https://github.com/Effect-TS/effect-smol/commit/016108a472af7048ddbbfd05f233e67529fafe12), [`95c03d2`](https://github.com/Effect-TS/effect-smol/commit/95c03d2c55930668c215b5a41c23cf7742fead84), [`07299a3`](https://github.com/Effect-TS/effect-smol/commit/07299a33c09fd52faa9810d30835a2622c752386)]:
- effect@4.0.0-beta.76
## 4.0.0-beta.75
### Patch Changes
- Updated dependencies [[`81b187c`](https://github.com/Effect-TS/effect-smol/commit/81b187c17a0d8817b58232826939154010ae49d7), [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e), [`a29c2e7`](https://github.com/Effect-TS/effect-smol/commit/a29c2e7e3570920156702671d6f3367cd0195f6c), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`ffea4ec`](https://github.com/Effect-TS/effect-smol/commit/ffea4ecf2925f6a4c9fd13079d47584cbf2bed00), [`4255c9b`](https://github.com/Effect-TS/effect-smol/commit/4255c9ba78bb98c7838fbe9dccdd8465e9da5427)]:
- effect@4.0.0-beta.75
## 4.0.0-beta.74
### Patch Changes
- Updated dependencies [[`b1fc6a4`](https://github.com/Effect-TS/effect-smol/commit/b1fc6a4b4d0ca7fa9fd162799ae17c86f2f7ee8e)]:
- effect@4.0.0-beta.74
## 4.0.0-beta.73
### Patch Changes
- Updated dependencies [[`361ca30`](https://github.com/Effect-TS/effect-smol/commit/361ca30eb6e134feece547d6e00f82be4cb23f75), [`b9598c6`](https://github.com/Effect-TS/effect-smol/commit/b9598c6a209e75bfdb87ee3b024ecd1e3923ff6e)]:
- effect@4.0.0-beta.73
## 4.0.0-beta.72
### Patch Changes
- Updated dependencies [[`73e67d1`](https://github.com/Effect-TS/effect-smol/commit/73e67d119a84d697773eaecb4865c6a71eb1a9cb), [`01d71ec`](https://github.com/Effect-TS/effect-smol/commit/01d71ec5a75f3c2747a8d3b1ad9701d1e27b7ce5), [`fcd707e`](https://github.com/Effect-TS/effect-smol/commit/fcd707e091a16e1b35343c901cc4052274e32239)]:
- effect@4.0.0-beta.72
## 4.0.0-beta.71
### Patch Changes
- Updated dependencies [[`d8ac76b`](https://github.com/Effect-TS/effect-smol/commit/d8ac76b5bad458c42cebe8a0c1b3843f955ac293), [`2c3c00a`](https://github.com/Effect-TS/effect-smol/commit/2c3c00af6faba7b7d422af26a7a2bbc35636d230), [`3751e7c`](https://github.com/Effect-TS/effect-smol/commit/3751e7cf353e7a54cd692c37401207d9afba1e63), [`fc5f25b`](https://github.com/Effect-TS/effect-smol/commit/fc5f25b03ada5fc2431987768a74d3d3e75ca485), [`7ccced4`](https://github.com/Effect-TS/effect-smol/commit/7ccced42867c14c013b01160b3d292f14c05bd04), [`a2e1fe5`](https://github.com/Effect-TS/effect-smol/commit/a2e1fe5835c98c8ee4393a091b1d11b75126e349), [`4a4a36b`](https://github.com/Effect-TS/effect-smol/commit/4a4a36b10e6e616cad07584a43908f6a7e07e618), [`d350292`](https://github.com/Effect-TS/effect-smol/commit/d3502922b4740fa9d745797cbc3775cb67839b6d), [`730afb6`](https://github.com/Effect-TS/effect-smol/commit/730afb66696adf9bd5a328cbca29df9c05968771), [`df1b008`](https://github.com/Effect-TS/effect-smol/commit/df1b008f370f414c2a67a7b8139ef747af8e5fba), [`6d469d5`](https://github.com/Effect-TS/effect-smol/commit/6d469d567a7c41d7e5343bdee21d45b07b0e8190)]:
- effect@4.0.0-beta.71
## 4.0.0-beta.70
### Patch Changes
- Updated dependencies [[`af7782d`](https://github.com/Effect-TS/effect-smol/commit/af7782d3008d08b043f3a3f261516001514b2b4e), [`7212d70`](https://github.com/Effect-TS/effect-smol/commit/7212d701a3eee7b3553ff502e2c066126e52e839)]:
- effect@4.0.0-beta.70
## 4.0.0-beta.69
### Patch Changes
- Updated dependencies [[`70ea04a`](https://github.com/Effect-TS/effect-smol/commit/70ea04aa96a2a7859d738d414e1f0e3ed081a27a), [`d0ea8b0`](https://github.com/Effect-TS/effect-smol/commit/d0ea8b03f7d73ae076c1db12666141e480d11178), [`a57674b`](https://github.com/Effect-TS/effect-smol/commit/a57674b64845e9e75a456cf907bfdcb858859118), [`59aa334`](https://github.com/Effect-TS/effect-smol/commit/59aa334fbd0a504dda3c36f6d2ef1be7449b4b8b), [`8f4208e`](https://github.com/Effect-TS/effect-smol/commit/8f4208ee83bc7bdaa6793b5429847b45aab72470)]:
- effect@4.0.0-beta.69
## 4.0.0-beta.68
### Patch Changes
- Updated dependencies [[`af8267f`](https://github.com/Effect-TS/effect-smol/commit/af8267f2f3588c3fb611e9286f6f933f29ce1217), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`f136bb7`](https://github.com/Effect-TS/effect-smol/commit/f136bb763048cbc6b17edd26496dba3e2415b9fa), [`6f38f07`](https://github.com/Effect-TS/effect-smol/commit/6f38f07d5941a211b251383aaab0f4f55e8a6557), [`aec9c40`](https://github.com/Effect-TS/effect-smol/commit/aec9c401a53db227f18bf5e0c84db7130ad862d6)]:
- effect@4.0.0-beta.68
## 4.0.0-beta.67
### Patch Changes
- Updated dependencies [[`a42ef66`](https://github.com/Effect-TS/effect-smol/commit/a42ef6632abbddfa820995ae310ccc84ae8d9b6f), [`35594f8`](https://github.com/Effect-TS/effect-smol/commit/35594f811cafe471acd490114b103a1f8392c8d8), [`8bddd62`](https://github.com/Effect-TS/effect-smol/commit/8bddd628cb623f9533d345082583ff51cead6836), [`4be4c8d`](https://github.com/Effect-TS/effect-smol/commit/4be4c8d60862aa963869ee2ed9ffa048ffac0527), [`0c9d3ab`](https://github.com/Effect-TS/effect-smol/commit/0c9d3ab43eb721a370ed8306260cbac218c27e87), [`b156acc`](https://github.com/Effect-TS/effect-smol/commit/b156accd2691b4a051f823affdece7c39923ce85), [`d16c034`](https://github.com/Effect-TS/effect-smol/commit/d16c03434ee3e6dcd3bfc82b65d99e881d89025b), [`b559d68`](https://github.com/Effect-TS/effect-smol/commit/b559d68845f848a10153395778f035682d399075), [`a3de5d9`](https://github.com/Effect-TS/effect-smol/commit/a3de5d9215e5cc4a62e2666efbd7c1bf595eb84f), [`7e6c12e`](https://github.com/Effect-TS/effect-smol/commit/7e6c12ec9b3a5945f6c26e272cc8f6390541ad3e), [`098167a`](https://github.com/Effect-TS/effect-smol/commit/098167a220fe07da6f14455818733ab1b269c9dd)]:
- effect@4.0.0-beta.67
## 4.0.0-beta.66
### Patch Changes
- Updated dependencies [[`ca2498e`](https://github.com/Effect-TS/effect-smol/commit/ca2498e702ac2d83fb7187707b7eb069bdb261a2), [`cd7d1fb`](https://github.com/Effect-TS/effect-smol/commit/cd7d1fba7e2e2c5ac3ad64e1be433440a5bda436), [`19a7033`](https://github.com/Effect-TS/effect-smol/commit/19a703367ec817cffc41d152da9b594827408e2b), [`33d26b4`](https://github.com/Effect-TS/effect-smol/commit/33d26b4210b2e974f146a71e7eed962f8ce00900), [`856766b`](https://github.com/Effect-TS/effect-smol/commit/856766b2c506aaed6d2df1d63bf3a5b1b062e1d4), [`079c7df`](https://github.com/Effect-TS/effect-smol/commit/079c7df82559bb9ce10a86dffb85d25e6ce07dc3)]:
- effect@4.0.0-beta.66
## 4.0.0-beta.65
### Patch Changes
- Updated dependencies [[`6f11454`](https://github.com/Effect-TS/effect-smol/commit/6f11454a9b6c3bd00f6b35fd7af14a2f2d63a0a2)]:
- effect@4.0.0-beta.65
## 4.0.0-beta.64
### Patch Changes
- Updated dependencies [[`7d4877a`](https://github.com/Effect-TS/effect-smol/commit/7d4877a1929cdb690280ea254326c04f2ec97ea5)]:
- effect@4.0.0-beta.64
## 4.0.0-beta.63
### Patch Changes
- Updated dependencies [[`7f927ff`](https://github.com/Effect-TS/effect-smol/commit/7f927ffb7a9801dcfc4096c29e369d13d65cd0ac), [`a696b3e`](https://github.com/Effect-TS/effect-smol/commit/a696b3e83a8504cdbe261a18c10a1cc0619ae102)]:
- effect@4.0.0-beta.63
## 4.0.0-beta.62
### Patch Changes
- Updated dependencies [[`4ab4b90`](https://github.com/Effect-TS/effect-smol/commit/4ab4b9007dc27a52ffabc6fcb37c96eeec795bf7)]:
- effect@4.0.0-beta.62
## 4.0.0-beta.61
### Patch Changes
- Updated dependencies [[`50790af`](https://github.com/Effect-TS/effect-smol/commit/50790af9b190c38d10fb0723837d49b66432638f), [`71f7c3d`](https://github.com/Effect-TS/effect-smol/commit/71f7c3df997deda92c84146d569696dab3bd645c), [`aae8797`](https://github.com/Effect-TS/effect-smol/commit/aae8797b9cb383be0c182dd58d03d787c354238b)]:
- effect@4.0.0-beta.61
## 4.0.0-beta.60
### Patch Changes
- Updated dependencies [[`f69d567`](https://github.com/Effect-TS/effect-smol/commit/f69d5675dcff9f4137295752baf066b7153fdc09), [`7909c95`](https://github.com/Effect-TS/effect-smol/commit/7909c954b8f6244a35a4b429f8dd0dff45dad620), [`bbb4dcc`](https://github.com/Effect-TS/effect-smol/commit/bbb4dcc6c406b83a416b4ad3541cc02037c420e4), [`7af2207`](https://github.com/Effect-TS/effect-smol/commit/7af2207901eabf3132c1b7010a69b3899c06fbbe), [`848b40a`](https://github.com/Effect-TS/effect-smol/commit/848b40a4bd4bf54a5098617d50c33c88eee8270a)]:
- effect@4.0.0-beta.60
## 4.0.0-beta.59
### Patch Changes
- Updated dependencies [[`56837ea`](https://github.com/Effect-TS/effect-smol/commit/56837ea2a338395b35550641374e9e589bd8b71d)]:
- effect@4.0.0-beta.59
## 4.0.0-beta.58
### Patch Changes
- Updated dependencies [[`11993d4`](https://github.com/Effect-TS/effect-smol/commit/11993d4934c66f5dc611b8bbf553f01d501ef8f7), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec)]:
- effect@4.0.0-beta.58
## 4.0.0-beta.57
### Patch Changes
- Updated dependencies [[`a971f5c`](https://github.com/Effect-TS/effect-smol/commit/a971f5cbd92dfe4274420bf0966595eb35531060), [`8e110c5`](https://github.com/Effect-TS/effect-smol/commit/8e110c5f02a429ccc43a91df8678e402138c0851)]:
- effect@4.0.0-beta.57
## 4.0.0-beta.56
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.56
## 4.0.0-beta.55
### Patch Changes
- Updated dependencies [[`42cc744`](https://github.com/Effect-TS/effect-smol/commit/42cc744570968deb365fb46d47b53d3277050c93), [`04855ce`](https://github.com/Effect-TS/effect-smol/commit/04855ceeca4d40c55a5750dd9893b691f8ea741a)]:
- effect@4.0.0-beta.55
## 4.0.0-beta.54
### Patch Changes
- Updated dependencies [[`e4b74f9`](https://github.com/Effect-TS/effect-smol/commit/e4b74f9c01a0e9b6cd58416de4af3a26d51da7c8), [`4c72808`](https://github.com/Effect-TS/effect-smol/commit/4c728081851c66dacf889a816535671bc841ae96)]:
- effect@4.0.0-beta.54
## 4.0.0-beta.53
### Patch Changes
- Updated dependencies [[`0768509`](https://github.com/Effect-TS/effect-smol/commit/07685094e931af07d104165195826a535b55fa7e), [`476aede`](https://github.com/Effect-TS/effect-smol/commit/476aede69c6efa06b5781ca5eb3e3b128ca29141), [`4f79c54`](https://github.com/Effect-TS/effect-smol/commit/4f79c542e7b508c235ff485d862cc8b29a8260c5), [`4be6a7c`](https://github.com/Effect-TS/effect-smol/commit/4be6a7cf35dab2a01d652f56dd35f0358c5a7e88), [`88927eb`](https://github.com/Effect-TS/effect-smol/commit/88927ebb896162cdba103b36553280b58e0facac)]:
- effect@4.0.0-beta.53
## 4.0.0-beta.52
### Patch Changes
- Updated dependencies [[`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`cf3a311`](https://github.com/Effect-TS/effect-smol/commit/cf3a311d863a8abb818840c3b80f847e621c43c1), [`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`131fdd5`](https://github.com/Effect-TS/effect-smol/commit/131fdd5b1f26531e265fe1a08f002002f47c276e)]:
- effect@4.0.0-beta.52
## 4.0.0-beta.51
### Patch Changes
- Updated dependencies [[`778d2af`](https://github.com/Effect-TS/effect-smol/commit/778d2afe9b5154bc1f9abae46d93ea7e54c87344), [`4e24dcf`](https://github.com/Effect-TS/effect-smol/commit/4e24dcf75037f65eebc1eb68623bc7cbf9d5512a), [`4b1c015`](https://github.com/Effect-TS/effect-smol/commit/4b1c0150e9bdb5559ed32d250deb66e17b4240c7), [`454f8ad`](https://github.com/Effect-TS/effect-smol/commit/454f8adad822929c3ef60f8280d0987226b049fd), [`6754a0c`](https://github.com/Effect-TS/effect-smol/commit/6754a0cd18626b06805a079cc5265525a5eb7d27), [`90f7fd5`](https://github.com/Effect-TS/effect-smol/commit/90f7fd5243871b30980964135db4512b8119fa82), [`d7e1519`](https://github.com/Effect-TS/effect-smol/commit/d7e151974934201fd93fa4c8a1192ee9a5d965a0), [`72a8122`](https://github.com/Effect-TS/effect-smol/commit/72a81228e09782bae512f7d041bbfbc78bc668d0)]:
- effect@4.0.0-beta.51
## 4.0.0-beta.50
### Patch Changes
- Updated dependencies [[`07be594`](https://github.com/Effect-TS/effect-smol/commit/07be594825de60f8e1b2102d21dbb9b8fc63b414), [`ae02433`](https://github.com/Effect-TS/effect-smol/commit/ae02433103ce28f53a0c9bfb4a44e75773289b7b)]:
- effect@4.0.0-beta.50
## 4.0.0-beta.49
### Patch Changes
- Updated dependencies [[`7d87873`](https://github.com/Effect-TS/effect-smol/commit/7d8787340ff549370f6f2a88b612e9ebbfd6ba45), [`c2f6f90`](https://github.com/Effect-TS/effect-smol/commit/c2f6f901b200a6e515b4f02c93ce8005b7bbf1c5), [`216f13c`](https://github.com/Effect-TS/effect-smol/commit/216f13c1fce454a21b489bb915714a17e791a1ac)]:
- effect@4.0.0-beta.49
## 4.0.0-beta.48
### Patch Changes
- Updated dependencies [[`4da56ec`](https://github.com/Effect-TS/effect-smol/commit/4da56ecff129b2da40137ffede23a73cc4e532d8), [`a5e6f77`](https://github.com/Effect-TS/effect-smol/commit/a5e6f774bab195cf50ecdc818240765f69a3bf4a), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070)]:
- effect@4.0.0-beta.48
## 4.0.0-beta.47
### Patch Changes
- Updated dependencies [[`c584726`](https://github.com/Effect-TS/effect-smol/commit/c58472674e750e6938df955044eab88feda95e45), [`86a91a4`](https://github.com/Effect-TS/effect-smol/commit/86a91a4f0c59286dfa9393232d8020dea70ed4db), [`131caf9`](https://github.com/Effect-TS/effect-smol/commit/131caf9525151a0cb29803a8f1dffa0f4f479d12), [`c3615c8`](https://github.com/Effect-TS/effect-smol/commit/c3615c88379b9daf252df0db72c6ac5a20326406)]:
- effect@4.0.0-beta.47
## 4.0.0-beta.46
### Patch Changes
- Updated dependencies [[`3a30b9e`](https://github.com/Effect-TS/effect-smol/commit/3a30b9e2ec2bd8b8193e1aa139f6878a07e3f5ee)]:
- effect@4.0.0-beta.46
## 4.0.0-beta.45
### Patch Changes
- Updated dependencies [[`5c3af6d`](https://github.com/Effect-TS/effect-smol/commit/5c3af6d554f60be34f8fc21d598d9a298ae11beb)]:
- effect@4.0.0-beta.45
## 4.0.0-beta.44
### Patch Changes
- [#1961](https://github.com/Effect-TS/effect-smol/pull/1961) [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970) Thanks @IMax153! - Rename the `ServiceMap` module to `Context` across exports, docs, and tests.
- Updated dependencies [[`e3f0621`](https://github.com/Effect-TS/effect-smol/commit/e3f0621454c3f5d11070d30619da27c9232cadc1), [`5b476ab`](https://github.com/Effect-TS/effect-smol/commit/5b476abc0bd7e9bb59135ea1bcad2e4936227ced), [`6b40e5a`](https://github.com/Effect-TS/effect-smol/commit/6b40e5a4a6bd2087c15a3d7374d25057fdedfa16), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`3b09fb3`](https://github.com/Effect-TS/effect-smol/commit/3b09fb31c40c2802b01f21c23bcdd1fe7fb0aa82), [`2370410`](https://github.com/Effect-TS/effect-smol/commit/237041062e5af4594d32db91597e34e70a632877), [`dabc272`](https://github.com/Effect-TS/effect-smol/commit/dabc272444a700eb629c07ba3e77671a841ca86e), [`08b63c3`](https://github.com/Effect-TS/effect-smol/commit/08b63c3df11bd35c9fd6090dbd166287fdc40664), [`dfff04c`](https://github.com/Effect-TS/effect-smol/commit/dfff04c4c2b1d352dfad83992a6dce1280c85cf9), [`9baed9e`](https://github.com/Effect-TS/effect-smol/commit/9baed9e17e84702e6e480fcef6f86404f9e24be9), [`7846792`](https://github.com/Effect-TS/effect-smol/commit/7846792adc7e1631d62d26d657bd7ba6139f369b), [`1556a24`](https://github.com/Effect-TS/effect-smol/commit/1556a247623636b7ebe438fb56d77f1a7bf957bb), [`7c11bc2`](https://github.com/Effect-TS/effect-smol/commit/7c11bc292ab8e46252fe8f7576fb685917bfb8b5), [`b5ea591`](https://github.com/Effect-TS/effect-smol/commit/b5ea5913ec1d45d0dd12a327b9dd966bda2f6d02), [`0853afa`](https://github.com/Effect-TS/effect-smol/commit/0853afaeb1633b2d7f8b66893bd01c3aa1ef2c22), [`ac845f3`](https://github.com/Effect-TS/effect-smol/commit/ac845f3ab40e0b8719576e7f9bc16ea2e0e02cd4), [`b80c462`](https://github.com/Effect-TS/effect-smol/commit/b80c46247480f47bb64fc480fab48a3f37bc8888), [`b3f535d`](https://github.com/Effect-TS/effect-smol/commit/b3f535d9a7ac13b5fb984c29f93561c57a081ff0), [`6fe2e93`](https://github.com/Effect-TS/effect-smol/commit/6fe2e93cc2f1b173ef89651d74b6a5d2626b3226), [`cda8004`](https://github.com/Effect-TS/effect-smol/commit/cda800451c1ffbdddfc08415aed7b2d91e0412ee), [`8335477`](https://github.com/Effect-TS/effect-smol/commit/8335477a8a936a24b5f3ee6203c1b268bd1bfc3c), [`8c836f9`](https://github.com/Effect-TS/effect-smol/commit/8c836f99ab1e896b9580a71d67773625baff2eaf), [`718ff6f`](https://github.com/Effect-TS/effect-smol/commit/718ff6fe3e3d3820cefd67d2bff1b2224fe08060), [`7eed84f`](https://github.com/Effect-TS/effect-smol/commit/7eed84fc33c5781a6fb11bf4fd189d424902ebd4), [`5df46fe`](https://github.com/Effect-TS/effect-smol/commit/5df46fe2f654d59ab5fc1578f4fc27fa40368ef9), [`82dd0f2`](https://github.com/Effect-TS/effect-smol/commit/82dd0f26c6442b07143762ef7bc33742d3978dd6), [`03ae41e`](https://github.com/Effect-TS/effect-smol/commit/03ae41e7304cffac9f18feea22b73468feafc43a), [`4677a0a`](https://github.com/Effect-TS/effect-smol/commit/4677a0a58f95eea38a211efcd3f345f237a9e44a), [`87e1fc8`](https://github.com/Effect-TS/effect-smol/commit/87e1fc8b67e4901d75f567b2fecc3841ab762cc4), [`c1af1b7`](https://github.com/Effect-TS/effect-smol/commit/c1af1b756f63291e9c0298cf95c98a6920a0c2a0), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`c8a877b`](https://github.com/Effect-TS/effect-smol/commit/c8a877b53e8f29616335719e5dd1c3992dddf780), [`7da961a`](https://github.com/Effect-TS/effect-smol/commit/7da961ae4916229d2246699a5d3b20e5b2dd2020)]:
- effect@4.0.0-beta.44
## 4.0.0-beta.43
### Patch Changes
- Updated dependencies [[`2ae33d0`](https://github.com/Effect-TS/effect-smol/commit/2ae33d050914915f7cb9c25ab0a020901e08d596), [`979811a`](https://github.com/Effect-TS/effect-smol/commit/979811a4c3f7ed21ed18ef560c49fb7f5569e80e), [`eb7dbef`](https://github.com/Effect-TS/effect-smol/commit/eb7dbeffa883386ad912815e62c0820cac1fdf8e), [`cf50eb4`](https://github.com/Effect-TS/effect-smol/commit/cf50eb49cb04706dae5185f624708117c413dee8), [`1d046fe`](https://github.com/Effect-TS/effect-smol/commit/1d046fe484560e23f3e22cb23eec6433f8f1fa02)]:
- effect@4.0.0-beta.43
## 4.0.0-beta.42
### Patch Changes
- Updated dependencies [[`924e216`](https://github.com/Effect-TS/effect-smol/commit/924e216caa7e0bbf22e994a0cd2ce8b1f0f0b3ee), [`80e7f0c`](https://github.com/Effect-TS/effect-smol/commit/80e7f0cd9116e811e97b0ce30a77a8d1ecd072aa), [`f8328bf`](https://github.com/Effect-TS/effect-smol/commit/f8328bf0314da3dc7f31d314f94a5840e8d5217f), [`66d1c06`](https://github.com/Effect-TS/effect-smol/commit/66d1c06039079129707a230f7ad8c676439d7133), [`bee800b`](https://github.com/Effect-TS/effect-smol/commit/bee800bf285192a01bec72a7b7b51bc1159434e6), [`8930441`](https://github.com/Effect-TS/effect-smol/commit/8930441dee6f94c59c583d18d3ebd677cf1f2623)]:
- effect@4.0.0-beta.42
## 4.0.0-beta.41
### Patch Changes
- Updated dependencies [[`36f5c21`](https://github.com/Effect-TS/effect-smol/commit/36f5c2174d31ab42c4598bf81f178f40d0802283), [`d8ce758`](https://github.com/Effect-TS/effect-smol/commit/d8ce758669d6297ae932ac3251d83e7b49b22f30), [`11aab4c`](https://github.com/Effect-TS/effect-smol/commit/11aab4c6d37d5691adafc2d33da1a631b28ce814), [`3bc1efb`](https://github.com/Effect-TS/effect-smol/commit/3bc1efb53dd75b4a40de46f1f80c7f8a7d50af86), [`70e724e`](https://github.com/Effect-TS/effect-smol/commit/70e724e604604d4be1061cd8da0d360494998c84), [`738dee7`](https://github.com/Effect-TS/effect-smol/commit/738dee7edfd70af82dc4d2376db3a8ebe603eb48), [`2111963`](https://github.com/Effect-TS/effect-smol/commit/2111963f19b4c28c800664a8fac9590c1321885f), [`198a553`](https://github.com/Effect-TS/effect-smol/commit/198a553d9ce45f6a00bfc4d65ed0640669602d95)]:
- effect@4.0.0-beta.41
## 4.0.0-beta.40
### Patch Changes
- Updated dependencies [[`f62860f`](https://github.com/Effect-TS/effect-smol/commit/f62860f0e5e45978fabf7256ae620a13152a772a), [`973f281`](https://github.com/Effect-TS/effect-smol/commit/973f2812529aadc1cc54598b2039799fa72b80f8)]:
- effect@4.0.0-beta.40
## 4.0.0-beta.39
### Patch Changes
- Updated dependencies [[`f91fd3d`](https://github.com/Effect-TS/effect-smol/commit/f91fd3db39fe5628439fd175fba201a65a1aa9d0), [`edaae9d`](https://github.com/Effect-TS/effect-smol/commit/edaae9d65f464f941d7eddd723cd33d324f4b071), [`b47db0b`](https://github.com/Effect-TS/effect-smol/commit/b47db0bd5802064b6a24b3ea27c6ff2e0520d513), [`82d3c8e`](https://github.com/Effect-TS/effect-smol/commit/82d3c8e4f3f49b00df611b25aa6f8f74ec21b59b), [`7c22b31`](https://github.com/Effect-TS/effect-smol/commit/7c22b315d198dcbf44ae8cdb8b37879e1c9e3996)]:
- effect@4.0.0-beta.39
## 4.0.0-beta.38
### Patch Changes
- Updated dependencies [[`f4dbe5b`](https://github.com/Effect-TS/effect-smol/commit/f4dbe5b26b9c2d33fae024bf44afbdf8541792cd), [`a71a607`](https://github.com/Effect-TS/effect-smol/commit/a71a607c89fb6669a12a562c2c23be81dfbe1adb), [`66a0494`](https://github.com/Effect-TS/effect-smol/commit/66a0494ed75cd12f2721dcbb1d8a072e3d9e14b6), [`5ef7218`](https://github.com/Effect-TS/effect-smol/commit/5ef7218fc559d57301fe929b8a0cab4033f4f1fd), [`472d260`](https://github.com/Effect-TS/effect-smol/commit/472d260655bc311fba5c2c6e23bb77d8f7e36ba0)]:
- effect@4.0.0-beta.38
## 4.0.0-beta.37
### Patch Changes
- Updated dependencies [[`f7a0b71`](https://github.com/Effect-TS/effect-smol/commit/f7a0b711da8fdd645597dee29cacc5619c6afcf2), [`1e223c3`](https://github.com/Effect-TS/effect-smol/commit/1e223c30ccf835dfbb21284535d78549efaeca80), [`53740f4`](https://github.com/Effect-TS/effect-smol/commit/53740f47aa76d114b7d535649fb50efc54a09608), [`8c7cf89`](https://github.com/Effect-TS/effect-smol/commit/8c7cf89f719e580cbce1bf6c24e6996f1992a0a6), [`b6b81a9`](https://github.com/Effect-TS/effect-smol/commit/b6b81a940eaafcbc792d25413d6c02c707de31b2), [`8f4c1f9`](https://github.com/Effect-TS/effect-smol/commit/8f4c1f97ed60f8810b0b327b50117ffb2d8260d4), [`f2479f9`](https://github.com/Effect-TS/effect-smol/commit/f2479f9d3113b1f012db17a3852b4e28f478cf9c), [`c919921`](https://github.com/Effect-TS/effect-smol/commit/c9199217fad65529421d2cf95ecfff41257090fd), [`7af90c2`](https://github.com/Effect-TS/effect-smol/commit/7af90c2e3c99038eafa39650433839523790e2fe), [`f3be185`](https://github.com/Effect-TS/effect-smol/commit/f3be18569e5ca57c25eabf00df3ca601ebab43c7)]:
- effect@4.0.0-beta.37
## 4.0.0-beta.36
### Patch Changes
- [#1775](https://github.com/Effect-TS/effect-smol/pull/1775) [`954e6d6`](https://github.com/Effect-TS/effect-smol/commit/954e6d655cd32d329b0bfeb872bb654f88b48a13) Thanks @tim-smart! - Add dynamic tool support to the Anthropic language model provider when preparing tool definitions for requests.
- Updated dependencies [[`60fcbcc`](https://github.com/Effect-TS/effect-smol/commit/60fcbcc43d09471e8f7e0969955d99dcefc5be81), [`0a60837`](https://github.com/Effect-TS/effect-smol/commit/0a6083713124440e630030375bab367e8d7df24e), [`49164d2`](https://github.com/Effect-TS/effect-smol/commit/49164d2c20a8d21b66514992c4a15d8521f6b36e), [`334b6e4`](https://github.com/Effect-TS/effect-smol/commit/334b6e4f76fe11941b516d61f57e268bc31f0ca6), [`5700695`](https://github.com/Effect-TS/effect-smol/commit/5700695f76ae6da6b94c9c87d4dd2b8054fb829b), [`f8f4456`](https://github.com/Effect-TS/effect-smol/commit/f8f445644f3aa7ec093cab7445198a62ba18a480), [`969d24f`](https://github.com/Effect-TS/effect-smol/commit/969d24fdfa48c4838e811983848d9cb4e9b3b12c), [`851eda0`](https://github.com/Effect-TS/effect-smol/commit/851eda0533946e39bacaaf581896320d7a4f3e8c), [`8059c1c`](https://github.com/Effect-TS/effect-smol/commit/8059c1c3eba9a90af7cd889ea261bcb8fff0c185), [`6f83295`](https://github.com/Effect-TS/effect-smol/commit/6f8329546a73eaddc7cb5e85ea8e37e73fbfb611), [`65f7f57`](https://github.com/Effect-TS/effect-smol/commit/65f7f5737575fed668987462c96d29a446707c32), [`e7fabd2`](https://github.com/Effect-TS/effect-smol/commit/e7fabd2265db690eae5cfc9b83730c84699aef61), [`89c3e98`](https://github.com/Effect-TS/effect-smol/commit/89c3e985401eb38f33a3ae21a94ad27de3c1d28b), [`53794ab`](https://github.com/Effect-TS/effect-smol/commit/53794ab7af30aa5c5004ecf53659fafbe4b10542)]:
- effect@4.0.0-beta.36
## 4.0.0-beta.35
### Patch Changes
- Updated dependencies [[`9252b43`](https://github.com/Effect-TS/effect-smol/commit/9252b43560f507709c2985abcf52a7837b23ddf8), [`7daf387`](https://github.com/Effect-TS/effect-smol/commit/7daf3870a656882a488a60f67881e6808c8f4d04), [`e1664a3`](https://github.com/Effect-TS/effect-smol/commit/e1664a38bc31ef4ceb4e9324c7226e1e99bf9c07), [`fdaa6e0`](https://github.com/Effect-TS/effect-smol/commit/fdaa6e0a41b6b6605438fa8557441792135380a2), [`19aa47e`](https://github.com/Effect-TS/effect-smol/commit/19aa47ef7b470e427620edca8970dd9cdd551216), [`c667dad`](https://github.com/Effect-TS/effect-smol/commit/c667dad07777b860e4764a3ba9a6cc41c236cd98), [`764d150`](https://github.com/Effect-TS/effect-smol/commit/764d1501bc5026b60fc8aef6cb02a5a87c762801), [`3c27098`](https://github.com/Effect-TS/effect-smol/commit/3c27098b5685a63db2c2eff654a250c94d3fcfa7)]:
- effect@4.0.0-beta.35
## 4.0.0-beta.34
### Patch Changes
- [#1763](https://github.com/Effect-TS/effect-smol/pull/1763) [`2fa940d`](https://github.com/Effect-TS/effect-smol/commit/2fa940d0709769c5fd1337c1f88400867f91989b) Thanks @teeverc! - Remove duplicate `ToolApprovalResponsePartOptions` from Anthropic package
- Updated dependencies [[`f2f75ee`](https://github.com/Effect-TS/effect-smol/commit/f2f75ee564bce1cd95f5189c7bdeeed4f92dacb1), [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f), [`5d704ee`](https://github.com/Effect-TS/effect-smol/commit/5d704ee10d20e8eb107e34bb8a21feb5aa4a7685), [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e), [`58217d3`](https://github.com/Effect-TS/effect-smol/commit/58217d318a7d716ccd707cce0f41573946939c28), [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2), [`e3b44b6`](https://github.com/Effect-TS/effect-smol/commit/e3b44b6a2af9ee21dc5c1e928f0c20af857fa7a9), [`e1472b7`](https://github.com/Effect-TS/effect-smol/commit/e1472b7525c5d57a48bdec2353c3b742f7f916c0), [`7686320`](https://github.com/Effect-TS/effect-smol/commit/7686320cd123fa352b5c3d076fb18a3cac0a9bba)]:
- effect@4.0.0-beta.34
## 4.0.0-beta.33
### Patch Changes
- Updated dependencies [[`571447d`](https://github.com/Effect-TS/effect-smol/commit/571447da67334449f8ae3d6ecb3d77ea4e0c4295)]:
- effect@4.0.0-beta.33
## 4.0.0-beta.32
### Patch Changes
- Updated dependencies [[`bf8fff8`](https://github.com/Effect-TS/effect-smol/commit/bf8fff8a5f54b6df74cb7bbb42346fe9ba52435a), [`1af3ef3`](https://github.com/Effect-TS/effect-smol/commit/1af3ef3e3ca7fd417d0fc15f8ca8fe207eba4f74), [`27fea0f`](https://github.com/Effect-TS/effect-smol/commit/27fea0f66910de5905f40fd63f8ddbb6f7ac5aba), [`2ad6c1b`](https://github.com/Effect-TS/effect-smol/commit/2ad6c1b2c85a3a0fe351e3d56636a75eb76b4b4e), [`398ac3e`](https://github.com/Effect-TS/effect-smol/commit/398ac3e01cb75efce0e4e2913d1450cf65866732), [`51fe22f`](https://github.com/Effect-TS/effect-smol/commit/51fe22f3266e417b6c541aaed4b75d246fac91e7), [`4605db6`](https://github.com/Effect-TS/effect-smol/commit/4605db69cfacddbdbf1525865ddfde135158090c), [`f4de1b0`](https://github.com/Effect-TS/effect-smol/commit/f4de1b087c998d0bad1d9468f70b7d16c13b9f6f), [`60214f2`](https://github.com/Effect-TS/effect-smol/commit/60214f2080b2aeb091f691140eb20acb741691c3), [`c4b8b0f`](https://github.com/Effect-TS/effect-smol/commit/c4b8b0ffa8efb47c4cd7578a8943d6868509373f), [`6d9393a`](https://github.com/Effect-TS/effect-smol/commit/6d9393a0770a18722d23340e77f15455de341245), [`6de4efe`](https://github.com/Effect-TS/effect-smol/commit/6de4efe463c783614ceb0c094d77a336a899cbe0), [`4f969d1`](https://github.com/Effect-TS/effect-smol/commit/4f969d1563ba755ffa116c8ae409bb3436bd881d), [`6cc67c8`](https://github.com/Effect-TS/effect-smol/commit/6cc67c855e054ee3f3ac3485dca5f7805e79e8fb), [`8531a22`](https://github.com/Effect-TS/effect-smol/commit/8531a22ffbb52e11a030b09f358cafbfdf5edff7), [`b226760`](https://github.com/Effect-TS/effect-smol/commit/b22676067617f15c00722a3a63fd7c2c172c3d45), [`47a51ab`](https://github.com/Effect-TS/effect-smol/commit/47a51aba0ecdf3ef478bfa28a498bca188399bd4), [`1521d02`](https://github.com/Effect-TS/effect-smol/commit/1521d02e1f19f1d795edaaf862c1a1031d9c755e)]:
- effect@4.0.0-beta.32
## 4.0.0-beta.31
### Patch Changes
- Updated dependencies [[`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6f23f0e`](https://github.com/Effect-TS/effect-smol/commit/6f23f0ed4cba573cd9395c2e582f582fe7271544), [`654aaec`](https://github.com/Effect-TS/effect-smol/commit/654aaec593305521b65dd042c204d761cc6e8c28), [`2958a42`](https://github.com/Effect-TS/effect-smol/commit/2958a42078966a8713a98f00485ab36484d5eccf), [`95d27a2`](https://github.com/Effect-TS/effect-smol/commit/95d27a239ed5147302605ab0b3147a056541b0c7), [`0fbaea8`](https://github.com/Effect-TS/effect-smol/commit/0fbaea8f9555a8044cec31a770394db613fc78e2), [`21d5d5e`](https://github.com/Effect-TS/effect-smol/commit/21d5d5e0439fd4d9bb6e508377215b1087555d45), [`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6e49959`](https://github.com/Effect-TS/effect-smol/commit/6e499590357a104c81779b3176cd3f84e4f91064), [`8f5805d`](https://github.com/Effect-TS/effect-smol/commit/8f5805dbdd0d1bc0ff0727cc398c8d80e544edee), [`990df2c`](https://github.com/Effect-TS/effect-smol/commit/990df2c3ceeb32e659acc10cc9485617f7b3c423)]:
- effect@4.0.0-beta.31
## 4.0.0-beta.30
### Patch Changes
- Updated dependencies [[`c88e5b7`](https://github.com/Effect-TS/effect-smol/commit/c88e5b723ff09da4edaef6ce14d927ca01104a32), [`947d0e4`](https://github.com/Effect-TS/effect-smol/commit/947d0e4268ba5c4020ead380aa80812c7342408f), [`7517908`](https://github.com/Effect-TS/effect-smol/commit/75179085d159b88a1ab0bce70669d76dcf0d79a4), [`a49ecd5`](https://github.com/Effect-TS/effect-smol/commit/a49ecd5a183d7e7d33f47ff95e9d2dea5a12ead5), [`6993e33`](https://github.com/Effect-TS/effect-smol/commit/6993e3329122c834c20bacea72d8678232f4f103), [`514f2a2`](https://github.com/Effect-TS/effect-smol/commit/514f2a2ae54580fcacdbe2ea2196a83a852d0748), [`3214b47`](https://github.com/Effect-TS/effect-smol/commit/3214b47676de2d33fddc5fecfc2d226e6e83cc7b), [`95ec5ed`](https://github.com/Effect-TS/effect-smol/commit/95ec5ed345de77c893049e182d37a37cf164a268)]:
- effect@4.0.0-beta.30
## 4.0.0-beta.29
### Patch Changes
- Updated dependencies [[`9d93adb`](https://github.com/Effect-TS/effect-smol/commit/9d93adb1c1795d1978391b30d7d2972c88052662), [`b52721c`](https://github.com/Effect-TS/effect-smol/commit/b52721cf0d11a567722b060c8536e3bdd4161f07), [`a891c7b`](https://github.com/Effect-TS/effect-smol/commit/a891c7b12f415b2287613dd4b91a09dfd38ef30d), [`ef26cdf`](https://github.com/Effect-TS/effect-smol/commit/ef26cdfb65d9955fc7e161629191930c2cc2c63f), [`82fd3ed`](https://github.com/Effect-TS/effect-smol/commit/82fd3ed922063ee5a34f96f3993c15c7515e4f67)]:
- effect@4.0.0-beta.29
## 4.0.0-beta.28
### Patch Changes
- Updated dependencies [[`ff533f2`](https://github.com/Effect-TS/effect-smol/commit/ff533f203cd06302ad08032a27e01269b4a2d4c6), [`dc803ee`](https://github.com/Effect-TS/effect-smol/commit/dc803ee52ebd3e9f931118f0dfcb804542847556), [`d660b1c`](https://github.com/Effect-TS/effect-smol/commit/d660b1c99cb93d4f79715e91c7a4486801c0eefa), [`93a05e3`](https://github.com/Effect-TS/effect-smol/commit/93a05e3eaa624058b162aedd66aad70102837270), [`2a65cf6`](https://github.com/Effect-TS/effect-smol/commit/2a65cf6fd81ef63d944e6fb51f058d439bf4a834), [`a561a40`](https://github.com/Effect-TS/effect-smol/commit/a561a40cc41c548c2cf3153aca065ee92ee8aa57), [`29cd24d`](https://github.com/Effect-TS/effect-smol/commit/29cd24d1fe78480a72eeb38a90281ffddc0530bc), [`662a8e6`](https://github.com/Effect-TS/effect-smol/commit/662a8e6857dac64a7cd13bd8df4b0674654622f8), [`d2b52ba`](https://github.com/Effect-TS/effect-smol/commit/d2b52bae5b9336cf59729fbdcc4d7f09512b0cbf), [`407c3b4`](https://github.com/Effect-TS/effect-smol/commit/407c3b43a5d1414558e0e33b6f1fc0e6a6d489cc), [`42bc7ce`](https://github.com/Effect-TS/effect-smol/commit/42bc7ce5480f6f2953c39f8cb5c850d61df6f5a2), [`e741322`](https://github.com/Effect-TS/effect-smol/commit/e74132226cbfee24234311c7c1c13e6b7391384e), [`5c75fa8`](https://github.com/Effect-TS/effect-smol/commit/5c75fa8fb71163bc4c035ba1a215574dfd4badfc), [`747177b`](https://github.com/Effect-TS/effect-smol/commit/747177b0602f12d4461a843e953dfdffbeb0a429), [`326cd48`](https://github.com/Effect-TS/effect-smol/commit/326cd4828bce573fe985f35152155464bf4c5a70), [`627e922`](https://github.com/Effect-TS/effect-smol/commit/627e922b8d1e9521eae5e1caa5d667ad00b1619a), [`662287e`](https://github.com/Effect-TS/effect-smol/commit/662287e9abc76c941ccc2ee330aa07904d571341)]:
- effect@4.0.0-beta.28
## 4.0.0-beta.27
### Patch Changes
- Updated dependencies [[`903a839`](https://github.com/Effect-TS/effect-smol/commit/903a839e94239e6ec4568315af28e405bcad95f4), [`91a0168`](https://github.com/Effect-TS/effect-smol/commit/91a016836680a6669308ecf464d3584bcc4ae1b7), [`c890f9a`](https://github.com/Effect-TS/effect-smol/commit/c890f9a1b3a989ed22528bd5a43326342e05b142), [`1e985f2`](https://github.com/Effect-TS/effect-smol/commit/1e985f237d250b51b91de22dde77160c1e778ce7)]:
- effect@4.0.0-beta.27
## 4.0.0-beta.26
### Patch Changes
- Updated dependencies [[`fb21462`](https://github.com/Effect-TS/effect-smol/commit/fb21462642cdd5b1bada92f3eba18ae20445be42), [`2ed26b1`](https://github.com/Effect-TS/effect-smol/commit/2ed26b139805700e3df39efaa768ff01565e5c86), [`e832a57`](https://github.com/Effect-TS/effect-smol/commit/e832a57b570fe38f010c1fd99bceac5a325a9e07), [`7f01be7`](https://github.com/Effect-TS/effect-smol/commit/7f01be7f8db363d4b2e88e6b5571e96bb815786f), [`e965143`](https://github.com/Effect-TS/effect-smol/commit/e9651431e114479e6becf8ca7b1ed99ac7e91ccc), [`b9b80f1`](https://github.com/Effect-TS/effect-smol/commit/b9b80f1f15e152ceef0a727d150b7dc230abae99), [`98252aa`](https://github.com/Effect-TS/effect-smol/commit/98252aa0c0b17fc73fbdad65d0a1104965f9fc0f), [`56fbd94`](https://github.com/Effect-TS/effect-smol/commit/56fbd94311ad19a05001ad649d9e34ab00c74541), [`3faa109`](https://github.com/Effect-TS/effect-smol/commit/3faa109b7d093fbf14ad410d3e11d663f16e28f1), [`692ecfe`](https://github.com/Effect-TS/effect-smol/commit/692ecfed99fe58056b7a5afe001f4fcd1a61c446), [`1e70b72`](https://github.com/Effect-TS/effect-smol/commit/1e70b72d0b210474d0e96a15a5cfc279eae37e0c), [`ecf0782`](https://github.com/Effect-TS/effect-smol/commit/ecf07829ef2dfc01d8943c96c4fe9c1b44b97926)]:
- effect@4.0.0-beta.26
## 4.0.0-beta.25
### Patch Changes
- Updated dependencies [[`fa17bb5`](https://github.com/Effect-TS/effect-smol/commit/fa17bb5be9f2533d01e11322b14804c7dec43714), [`f46e5b5`](https://github.com/Effect-TS/effect-smol/commit/f46e5b5ca2a918ee4d9270167e79db223077c96f), [`ce4767c`](https://github.com/Effect-TS/effect-smol/commit/ce4767cadcacc6ce8ff4c3a0d0fbc82ede655f63), [`c830a8b`](https://github.com/Effect-TS/effect-smol/commit/c830a8b6c292a6528d7f9318759d34800b00372d)]:
- effect@4.0.0-beta.25
## 4.0.0-beta.24
### Patch Changes
- Updated dependencies [[`a909e1c`](https://github.com/Effect-TS/effect-smol/commit/a909e1c1ac2bc707527f5073776e3e7d239688d9), [`8814a4e`](https://github.com/Effect-TS/effect-smol/commit/8814a4ef78d67144d27689370af10099ea210399), [`3f942c5`](https://github.com/Effect-TS/effect-smol/commit/3f942c51cefa7b2ffa7c49e8c8a2c887570ba4c0), [`774ed59`](https://github.com/Effect-TS/effect-smol/commit/774ed59c52b2ab578bbb897c4f551f812231e1d2), [`f54b8d3`](https://github.com/Effect-TS/effect-smol/commit/f54b8d398fedad1815fd1f4c49814ab938cfc385)]:
- effect@4.0.0-beta.24
## 4.0.0-beta.23
### Patch Changes
- Updated dependencies [[`5c73c41`](https://github.com/Effect-TS/effect-smol/commit/5c73c41b69eaeab80fcd62c9bfda490b446d1966)]:
- effect@4.0.0-beta.23
## 4.0.0-beta.22
### Patch Changes
- Updated dependencies [[`0874332`](https://github.com/Effect-TS/effect-smol/commit/0874332f7c81118b06ac2eb105e0710211631479), [`c592dcd`](https://github.com/Effect-TS/effect-smol/commit/c592dcde0697e322065c8f418c0480ef910cb183), [`1dbe28d`](https://github.com/Effect-TS/effect-smol/commit/1dbe28dac8299cd3e218c9768450cfd173b5e294), [`564d730`](https://github.com/Effect-TS/effect-smol/commit/564d730b6bbf38dd8548a3b046e7a693b28699a4), [`3cfadc4`](https://github.com/Effect-TS/effect-smol/commit/3cfadc458b070c6cba6c5674b72a059f1e49118b), [`6634fd0`](https://github.com/Effect-TS/effect-smol/commit/6634fd07da067d80b8261fb2959d1a952b9e412e), [`d10dabe`](https://github.com/Effect-TS/effect-smol/commit/d10dabeb7af9a368f995829cd36ad08167cd8f95), [`f82f549`](https://github.com/Effect-TS/effect-smol/commit/f82f549a09e950e9d4987f279a800f4d953f0939), [`78a3382`](https://github.com/Effect-TS/effect-smol/commit/78a3382ddfbe034408f7480fa794733d9e82147b)]:
- effect@4.0.0-beta.22
## 4.0.0-beta.21
### Patch Changes
- Updated dependencies [[`e691909`](https://github.com/Effect-TS/effect-smol/commit/e691909495ccb162ea7bfa351dd74632b99997cb), [`d5f413f`](https://github.com/Effect-TS/effect-smol/commit/d5f413f3c8fc57f2413cc5649c2003d6d4e5a6d7), [`139d152`](https://github.com/Effect-TS/effect-smol/commit/139d152941e562a073b5be12e8d66c8a4d4a8a57), [`947e3d4`](https://github.com/Effect-TS/effect-smol/commit/947e3d436ab8a017efda9b29be523efd1ca8df28), [`84b2cce`](https://github.com/Effect-TS/effect-smol/commit/84b2ccefe2aa3a7413b86738a4dc33cdb311ca55), [`7f5305e`](https://github.com/Effect-TS/effect-smol/commit/7f5305e69f5a33309e77b08a576edb25d7daaee2), [`9e6fd84`](https://github.com/Effect-TS/effect-smol/commit/9e6fd8471c93a3c643929151a3bdb62cb9c0ca0e), [`fdb8a4b`](https://github.com/Effect-TS/effect-smol/commit/fdb8a4b172721fbefe98bd5aa6fe4f0efd1da3eb), [`0f986ef`](https://github.com/Effect-TS/effect-smol/commit/0f986ef22f196fe091a7afdbd179485a7d888882), [`9355fc0`](https://github.com/Effect-TS/effect-smol/commit/9355fc0ffb5b7382146a5aed9eea83974b10d007)]:
- effect@4.0.0-beta.21
## 4.0.0-beta.20
### Patch Changes
- [#1529](https://github.com/Effect-TS/effect-smol/pull/1529) [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8) Thanks @tim-smart! - Add dedicated AiError metadata interfaces per reason so provider packages can safely augment metadata without conflicting module declarations.
- [#1528](https://github.com/Effect-TS/effect-smol/pull/1528) [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34) Thanks @tim-smart! - Add `Model.ModelName` and provide it from AI model constructors.
- Updated dependencies [[`842a624`](https://github.com/Effect-TS/effect-smol/commit/842a624f79d5e1407460b0ef3ab27d14d48ccf74), [`4785eef`](https://github.com/Effect-TS/effect-smol/commit/4785eef5d7cf1edb96ef2509aed2ba4d1edf3862), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`12ee8e2`](https://github.com/Effect-TS/effect-smol/commit/12ee8e27df7eb393d83a5e403390d0cfc82ca732), [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34), [`989d1cc`](https://github.com/Effect-TS/effect-smol/commit/989d1cca936fce0cc459057825ba40e3f5ef3827)]:
- effect@4.0.0-beta.20
## 4.0.0-beta.19
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.19
## 4.0.0-beta.18
### Patch Changes
- Updated dependencies [[`01e31fd`](https://github.com/Effect-TS/effect-smol/commit/01e31fdf8e5206849d23cbafd23a346f2f177ab8), [`0890aab`](https://github.com/Effect-TS/effect-smol/commit/0890aab15ed9c5ba52c383a72fdc6a444d7504d5), [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d)]:
- effect@4.0.0-beta.18
## 4.0.0-beta.17
### Patch Changes
- Updated dependencies [[`8f59c32`](https://github.com/Effect-TS/effect-smol/commit/8f59c32922597a48392744f7203e284866747781)]:
- effect@4.0.0-beta.17
## 4.0.0-beta.16
### Patch Changes
- Updated dependencies [[`bf9096c`](https://github.com/Effect-TS/effect-smol/commit/bf9096c52a7d8791d93d232739e523eb84f6625a), [`29f81ca`](https://github.com/Effect-TS/effect-smol/commit/29f81ca07c67dba265804b140a7487fb15a5fc6b), [`68eb28c`](https://github.com/Effect-TS/effect-smol/commit/68eb28c2b0fc67a9f6204ade9bd16c5b37803bfb)]:
- effect@4.0.0-beta.16
## 4.0.0-beta.15
### Patch Changes
- [#1502](https://github.com/Effect-TS/effect-smol/pull/1502) [`285b7e6`](https://github.com/Effect-TS/effect-smol/commit/285b7e667167566d5788367d5155b19c79f1bf22) Thanks @tim-smart! - allow undefined for ai config
- Updated dependencies [[`24ae609`](https://github.com/Effect-TS/effect-smol/commit/24ae60995d2fd7d621be356cdfdfd328c79639ba), [`0e3c059`](https://github.com/Effect-TS/effect-smol/commit/0e3c059987caa55ebd0c134f7c7b147c639c328e), [`e843b0a`](https://github.com/Effect-TS/effect-smol/commit/e843b0a7d7e7b600a0b3bd477f24e2e4cd26bc8b), [`f4389a2`](https://github.com/Effect-TS/effect-smol/commit/f4389a2cca3c5bbf00d69779f52ce41255f15a28), [`5b73de0`](https://github.com/Effect-TS/effect-smol/commit/5b73de095b3402d0c5c74092ace6ce18ebfad566), [`595d2d6`](https://github.com/Effect-TS/effect-smol/commit/595d2d6e7d50419f3532bd39266191532ace38f2)]:
- effect@4.0.0-beta.15
## 4.0.0-beta.14
### Patch Changes
- Updated dependencies [[`c414700`](https://github.com/Effect-TS/effect-smol/commit/c414700ef1932e4b67d0102856de417336912350), [`a30c969`](https://github.com/Effect-TS/effect-smol/commit/a30c9699c0d736cf3952041e45d508b7d58907a9)]:
- effect@4.0.0-beta.14
## 4.0.0-beta.13
### Patch Changes
- Updated dependencies [[`368f4c3`](https://github.com/Effect-TS/effect-smol/commit/368f4c363dd117e6f5a19ad77b161176cfd29fdd), [`db8a579`](https://github.com/Effect-TS/effect-smol/commit/db8a579e93e93ff73b1e60712732e03b597b916b), [`668b703`](https://github.com/Effect-TS/effect-smol/commit/668b70337e9ddbb0d1ae2282a95c282ce404e562), [`d40e76b`](https://github.com/Effect-TS/effect-smol/commit/d40e76b973543979e60e04a6baca04a8c65bdfc2), [`6e18cf8`](https://github.com/Effect-TS/effect-smol/commit/6e18cf883e9905ca718a6697b6a2a4bbd42739aa), [`86062e8`](https://github.com/Effect-TS/effect-smol/commit/86062e8a0c61bca5412fc40d2cf151d676901f08), [`c27ce75`](https://github.com/Effect-TS/effect-smol/commit/c27ce75d34c74dcfc6dba1bf77f1ce88f410a0de), [`e2d4fbf`](https://github.com/Effect-TS/effect-smol/commit/e2d4fbfeeda6a5d2a4c5aeb0501d8240c248b9eb), [`114ab42`](https://github.com/Effect-TS/effect-smol/commit/114ab42ad0edc590d29169675a493e0e915aa58f), [`484caec`](https://github.com/Effect-TS/effect-smol/commit/484caec47cccac8b86db2910742e406dfc7173ab)]:
- effect@4.0.0-beta.13
## 4.0.0-beta.12
### Patch Changes
- Updated dependencies [[`70a74e8`](https://github.com/Effect-TS/effect-smol/commit/70a74e88a8767c9d4acdb9e5f25aec9a33588d07), [`b5b6e10`](https://github.com/Effect-TS/effect-smol/commit/b5b6e10621d54bf8c9857fec0d647ced78ecd857), [`f5ce5a9`](https://github.com/Effect-TS/effect-smol/commit/f5ce5a915359c6ebf254079e1da23cab6cde34fb), [`a29eb70`](https://github.com/Effect-TS/effect-smol/commit/a29eb702ffe3fc58bd28c4d7857298cd65d73668), [`c7b36e5`](https://github.com/Effect-TS/effect-smol/commit/c7b36e541a23e9a00f64e25b23851e51a37dfce5), [`9381d6d`](https://github.com/Effect-TS/effect-smol/commit/9381d6d4d9d819a81a46e56d0364c76e92a4fbca), [`88439f1`](https://github.com/Effect-TS/effect-smol/commit/88439f13ca13549f3e4822c48c4f019c14fc2bcc), [`e35307d`](https://github.com/Effect-TS/effect-smol/commit/e35307dbeb8eb26a9923f958b894a8eaaf259bf2), [`c7df4bc`](https://github.com/Effect-TS/effect-smol/commit/c7df4bce34009474c63d62a807abfdafb76971eb), [`accaf3b`](https://github.com/Effect-TS/effect-smol/commit/accaf3be7ac8da36e2334c509c23b8c9e88ea160), [`3e1c270`](https://github.com/Effect-TS/effect-smol/commit/3e1c2707bbdf67720af1509642b8ced195790882), [`6cd81f7`](https://github.com/Effect-TS/effect-smol/commit/6cd81f73baad86f5bbfa455a55d75cde71e9611a), [`f222da3`](https://github.com/Effect-TS/effect-smol/commit/f222da3cdb44554f3324c2c52d0d005ee575053e), [`61f901d`](https://github.com/Effect-TS/effect-smol/commit/61f901d830005b66e22d1de889fda132aeea97cd)]:
- effect@4.0.0-beta.12
## 4.0.0-beta.11
### Patch Changes
- Updated dependencies [[`88659ed`](https://github.com/Effect-TS/effect-smol/commit/88659edb26e3623d557dccfe914c2c949672da16), [`f2915e8`](https://github.com/Effect-TS/effect-smol/commit/f2915e8e2efe80d50c281e53f297b9701d6dc199), [`eb71ace`](https://github.com/Effect-TS/effect-smol/commit/eb71acebbe0f228e4920278013beee3b67d62310), [`2a16999`](https://github.com/Effect-TS/effect-smol/commit/2a169996c7513d377ac47adbfd68e1490457135c), [`d42dd52`](https://github.com/Effect-TS/effect-smol/commit/d42dd52f11203f8e749fb5d3ecf7153e4a5a6814), [`339adaf`](https://github.com/Effect-TS/effect-smol/commit/339adaf850a62a892adebcb208c2d9dddf3b97b3), [`de19645`](https://github.com/Effect-TS/effect-smol/commit/de1964526d01102dd1cb99c8cfdd3e8df1f49ef1), [`9b1dc3b`](https://github.com/Effect-TS/effect-smol/commit/9b1dc3bcf2a1b68d0a67e3465db5ad01a1a56997), [`e4cb2f5`](https://github.com/Effect-TS/effect-smol/commit/e4cb2f55b30f4771ec1bf613ced36d6d96464dd5), [`8bced95`](https://github.com/Effect-TS/effect-smol/commit/8bced954ecb35d4489197a57b0efe927e7d75f49), [`9431420`](https://github.com/Effect-TS/effect-smol/commit/94314207c8019918200fbcb97aec992219f801f0), [`948dca2`](https://github.com/Effect-TS/effect-smol/commit/948dca22e4f672ba7a6db57f9899272bec7c08b8), [`d18e327`](https://github.com/Effect-TS/effect-smol/commit/d18e32765a2665e31ffb31e746bf983fcfac34c5), [`ab512f7`](https://github.com/Effect-TS/effect-smol/commit/ab512f7be1c0e6b359da921e22cd4944e4c57d3e)]:
- effect@4.0.0-beta.11
## 4.0.0-beta.10
### Patch Changes
- Updated dependencies [[`371acab`](https://github.com/Effect-TS/effect-smol/commit/371acabb58d56f3a7a5e3e33d3d5fdc9f5573c74), [`856d774`](https://github.com/Effect-TS/effect-smol/commit/856d7741f1e296dd5048c6ff2b44b95d023e6ae4), [`b9e9202`](https://github.com/Effect-TS/effect-smol/commit/b9e92023c38caa322975d77cfe83e2d34ac9305a), [`1d1a974`](https://github.com/Effect-TS/effect-smol/commit/1d1a974bd280c81bff5d4505491cda03ba7a3f36), [`6bfe2a6`](https://github.com/Effect-TS/effect-smol/commit/6bfe2a659bc6335db75709931f405da45301cba2), [`b12c811`](https://github.com/Effect-TS/effect-smol/commit/b12c81157be287b1649c210616a244b50ec094d2), [`d17d98a`](https://github.com/Effect-TS/effect-smol/commit/d17d98ad78e2b44d95ef434adab79ac3c35e75ab), [`68c3c7c`](https://github.com/Effect-TS/effect-smol/commit/68c3c7cb1e06ed94fa5c4c123a234b4ccbfdecd8)]:
- effect@4.0.0-beta.10
## 4.0.0-beta.9
### Patch Changes
- Updated dependencies [[`3386557`](https://github.com/Effect-TS/effect-smol/commit/338655731564a7be9f8859dedbf4d5bcac6eb350), [`b6666e3`](https://github.com/Effect-TS/effect-smol/commit/b6666e3cf6bd44ba1a8704e65c256c30359cb422)]:
- effect@4.0.0-beta.9
## 4.0.0-beta.8
### Patch Changes
- [#1354](https://github.com/Effect-TS/effect-smol/pull/1354) [`b94962c`](https://github.com/Effect-TS/effect-smol/commit/b94962c249d46cf96cdf2e41188dc9feda41536a) Thanks @IMax153! - Fix the generated schemas for ai providers
- Updated dependencies [[`246e672`](https://github.com/Effect-TS/effect-smol/commit/246e672dbbd7848d60e0c78fd66671b2f10b3752), [`807dec0`](https://github.com/Effect-TS/effect-smol/commit/807dec03801b4c58a6d00c237b6d98d6386911df)]:
- effect@4.0.0-beta.8
## 4.0.0-beta.7
### Patch Changes
- Updated dependencies [[`a2bda6d`](https://github.com/Effect-TS/effect-smol/commit/a2bda6d4ef6de9d9b0c53ae2df5434f778d6161a), [`1f95a2b`](https://github.com/Effect-TS/effect-smol/commit/1f95a2b5aa9524bb38f4437f4691a664bf463ca1), [`a8d5e79`](https://github.com/Effect-TS/effect-smol/commit/a8d5e792fec201a83af0eb92fc79928d055125fd), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`06d8a03`](https://github.com/Effect-TS/effect-smol/commit/06d8a0391631e6130e3ab25227e59817852e227f), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`f9e883e`](https://github.com/Effect-TS/effect-smol/commit/f9e883e266fbda870336ee62f46b7ac85ba3de6e), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430)]:
- effect@4.0.0-beta.7
## 4.0.0-beta.6
### Patch Changes
- [#1333](https://github.com/Effect-TS/effect-smol/pull/1333) [`96260bf`](https://github.com/Effect-TS/effect-smol/commit/96260bf5331fad6e38d2a79e94d70fd9a502ec29) Thanks @IMax153! - Fix tool calling for the Anthropic Effect AI SDK provider integration
- Updated dependencies [[`3247da2`](https://github.com/Effect-TS/effect-smol/commit/3247da28331f345f68be5dbd2974a7e03d300fe1), [`f205705`](https://github.com/Effect-TS/effect-smol/commit/f2057050dbd034b8c186be2d40c3d03ee63a5a3b), [`f35022c`](https://github.com/Effect-TS/effect-smol/commit/f35022c212e4111527e1bb43f360a67b2b49fa85), [`8622721`](https://github.com/Effect-TS/effect-smol/commit/86227217b02d43680a3c6f3c21731b1d852c91f5), [`fc660ab`](https://github.com/Effect-TS/effect-smol/commit/fc660ab8b5ebae38b8d6b96cbf2f9b880cc09253), [`f37dc33`](https://github.com/Effect-TS/effect-smol/commit/f37dc335f64622fa9ce8d6d1d5dd8fc3f260257b), [`3662f32`](https://github.com/Effect-TS/effect-smol/commit/3662f328fcfa3b2fa01ffa79da40e12e93fcede8), [`a7d436f`](https://github.com/Effect-TS/effect-smol/commit/a7d436f438dcd7f49b9485e4e95a4511f31fad7d), [`6856a41`](https://github.com/Effect-TS/effect-smol/commit/6856a415d7eddd9d73d60919e976f1d071421be4), [`8c417d0`](https://github.com/Effect-TS/effect-smol/commit/8c417d03475e5e12d00dca0c4781d0af7e66b86c), [`5419570`](https://github.com/Effect-TS/effect-smol/commit/5419570ba47ce882a3a10882707b46f66e464906), [`449c5ed`](https://github.com/Effect-TS/effect-smol/commit/449c5ed5318e8a874e730420bcf52918fa2ec80f), [`4b5ec12`](https://github.com/Effect-TS/effect-smol/commit/4b5ec12f87f95f2a3cd8fe4d5b26c6eb0529381a), [`df87937`](https://github.com/Effect-TS/effect-smol/commit/df879375fc3b169c43f9c434b3775e12b80dffe4), [`5dbfca8`](https://github.com/Effect-TS/effect-smol/commit/5dbfca8d1dbb6d18d1605d4f8562e99c86e2ff11), [`e629497`](https://github.com/Effect-TS/effect-smol/commit/e6294973d55597ab6b6deca6babbe1e946b2c91d), [`981c991`](https://github.com/Effect-TS/effect-smol/commit/981c991cd78db34def815d5754379d737157f005), [`1ca2ed6`](https://github.com/Effect-TS/effect-smol/commit/1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe), [`45722bd`](https://github.com/Effect-TS/effect-smol/commit/45722bde974458311f11ad237711363a10ec6894), [`eb2a85e`](https://github.com/Effect-TS/effect-smol/commit/eb2a85ed4dc162b2535d304799333a5a20477fd0)]:
- effect@4.0.0-beta.6
## 4.0.0-beta.5
### Patch Changes
- Updated dependencies [[`f6e133e`](https://github.com/Effect-TS/effect-smol/commit/f6e133e9a16b32317bd09ff08c12b97a0ae44600), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8), [`a88e206`](https://github.com/Effect-TS/effect-smol/commit/a88e206e44dc66ca5a2b45bedc797877c5dbb083), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8)]:
- effect@4.0.0-beta.5
## 4.0.0-beta.4
### Patch Changes
- Updated dependencies [[`c5a18ef`](https://github.com/Effect-TS/effect-smol/commit/c5a18ef44171e3880bf983faee74529908974b32), [`bc6b885`](https://github.com/Effect-TS/effect-smol/commit/bc6b885b94d887a200657c0775dfa874dc15bc0c)]:
- effect@4.0.0-beta.4
## 4.0.0-beta.3
### Patch Changes
- Updated dependencies [[`3a0cf36`](https://github.com/Effect-TS/effect-smol/commit/3a0cf36eff106ba48d74e133c1598cd40613e530), [`c4da328`](https://github.com/Effect-TS/effect-smol/commit/c4da328d32fad1d61e0e538f5d371edf61521d7e)]:
- effect@4.0.0-beta.3
## 4.0.0-beta.2
### Patch Changes
- Updated dependencies [[`a22ce73`](https://github.com/Effect-TS/effect-smol/commit/a22ce73b2bd9305b7ba665694d2255c0e6d5a8d0), [`ebdabf7`](https://github.com/Effect-TS/effect-smol/commit/ebdabf79ff4e62c8384aa8cf9a8d2787d536ee78), [`8f663bb`](https://github.com/Effect-TS/effect-smol/commit/8f663bb121021bf12bd264e8ae385187cb7a5dae)]:
- effect@4.0.0-beta.2
## 4.0.0-beta.1
### Patch Changes
- Updated dependencies [[`0fecf70`](https://github.com/Effect-TS/effect-smol/commit/0fecf70048057623eed7c584a06671773a2b1743), [`709569e`](https://github.com/Effect-TS/effect-smol/commit/709569ed76bead9ebb0670599e4d890a07ca5a43)]:
- effect@4.0.0-beta.1
## 4.0.0-beta.0
### Major Changes
- [#1183](https://github.com/Effect-TS/effect-smol/pull/1183) [`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66) Thanks @tim-smart! - v4 beta
### Patch Changes
- Updated dependencies [[`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66)]:
- effect@4.0.0-beta.0

View File

@@ -0,0 +1,62 @@
# yaml-language-server: $schema=../../tools/ai-codegen/codegen.schema.json
spec:
type: stainless-stats
statsUrl: https://raw.githubusercontent.com/anthropics/anthropic-sdk-typescript/main/.stats.yml
output: src/Generated.ts
name: AnthropicClient
patches:
# Make iterations and speed optional in BetaUsage, iterations optional in BetaMessageDeltaUsage (API may omit these fields)
- '[{"op":"remove","path":"/components/schemas/BetaUsage/required/9"},{"op":"remove","path":"/components/schemas/BetaUsage/required/5"},{"op":"remove","path":"/components/schemas/BetaMessageDeltaUsage/required/3"}]'
header: |
/**
* @since 1.0.0
*/
excludeAnnotations:
- examples
replacements:
# Schema.Unknown doesn't work with Schema.toCodecJson (used by HttpClientResponse.schemaBodyJson)
# Replace with Schema.Json which properly handles arbitrary JSON values
- from: "Schema.Record(Schema.String, Schema.Unknown)"
to: "Schema.Record(Schema.String, Schema.Json)"
- from: "{ readonly [x: string]: unknown }"
to: "{ readonly [x: string]: Schema.Json }"
# Index signature in struct rest pattern
- from: "readonly [x: string]: unknown"
to: "readonly [x: string]: Schema.Json"
# Make citations optional in ResponseTextBlock (API may omit this field)
- from: 'export type ResponseTextBlock = { readonly "citations":'
to: 'export type ResponseTextBlock = { readonly "citations"?:'
- from: "export const ResponseTextBlock = Schema.Struct({ \"citations\": Schema.Union([Schema.Array(Schema.Union([ResponseCharLocationCitation, ResponsePageLocationCitation, ResponseContentBlockLocationCitation, ResponseWebSearchResultLocationCitation, ResponseSearchResultLocationCitation], { mode: \"oneOf\" })), Schema.Null]).annotate({ \"title\": \"Citations\", \"description\": \"Citations supporting the text block.\\n\\nThe type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`.\", \"default\": null }), \"text\":"
to: "export const ResponseTextBlock = Schema.Struct({ \"citations\": Schema.optionalKey(Schema.Union([Schema.Array(Schema.Union([ResponseCharLocationCitation, ResponsePageLocationCitation, ResponseContentBlockLocationCitation, ResponseWebSearchResultLocationCitation, ResponseSearchResultLocationCitation], { mode: \"oneOf\" })), Schema.Null]).annotate({ \"title\": \"Citations\", \"description\": \"Citations supporting the text block.\\n\\nThe type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`.\", \"default\": null })), \"text\":"
# Make citations optional in BetaResponseTextBlock (API may omit this field)
- from: 'export type BetaResponseTextBlock = { readonly "citations":'
to: 'export type BetaResponseTextBlock = { readonly "citations"?:'
- from: "export const BetaResponseTextBlock = Schema.Struct({ \"citations\": Schema.Union([Schema.Array(Schema.Union([BetaResponseCharLocationCitation, BetaResponsePageLocationCitation, BetaResponseContentBlockLocationCitation, BetaResponseWebSearchResultLocationCitation, BetaResponseSearchResultLocationCitation], { mode: \"oneOf\" })), Schema.Null]).annotate({ \"title\": \"Citations\", \"description\": \"Citations supporting the text block.\\n\\nThe type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`.\", \"default\": null }), \"text\":"
to: "export const BetaResponseTextBlock = Schema.Struct({ \"citations\": Schema.optionalKey(Schema.Union([Schema.Array(Schema.Union([BetaResponseCharLocationCitation, BetaResponsePageLocationCitation, BetaResponseContentBlockLocationCitation, BetaResponseWebSearchResultLocationCitation, BetaResponseSearchResultLocationCitation], { mode: \"oneOf\" })), Schema.Null]).annotate({ \"title\": \"Citations\", \"description\": \"Citations supporting the text block.\\n\\nThe type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`.\", \"default\": null })), \"text\":"
# Make server_tool_use optional in usage (API may omit this field)
- from: 'readonly "server_tool_use": ServerToolUsage | null'
to: 'readonly "server_tool_use"?: ServerToolUsage | null'
- from: 'readonly "server_tool_use": BetaServerToolUsage | null'
to: 'readonly "server_tool_use"?: BetaServerToolUsage | null'
- from: '"server_tool_use": Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null }),'
to: '"server_tool_use": Schema.optionalKey(Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })),'
- from: '"server_tool_use": Schema.Union([BetaServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null }),'
to: '"server_tool_use": Schema.optionalKey(Schema.Union([BetaServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })),'
# Without trailing comma (for last field in struct)
- from: '"server_tool_use": Schema.Union([BetaServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null }) })'
to: '"server_tool_use": Schema.optionalKey(Schema.Union([BetaServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })) })'
- from: '"server_tool_use": Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null }) })'
to: '"server_tool_use": Schema.optionalKey(Schema.Union([ServerToolUsage, Schema.Null]).annotate({ "description": "The number of server tool requests.", "default": null })) })'
# Make context_management and container optional in BetaMessage (API may omit these fields)
- from: 'readonly "context_management": BetaResponseContextManagement | null'
to: 'readonly "context_management"?: BetaResponseContextManagement | null'
- from: 'readonly "container": BetaContainer | null'
to: 'readonly "container"?: BetaContainer | null'
- from: "\"context_management\": Schema.Union([BetaResponseContextManagement, Schema.Null]).annotate({ \"description\": \"Context management response.\\n\\nInformation about context management strategies applied during the request.\", \"default\": null }),"
to: "\"context_management\": Schema.optionalKey(Schema.Union([BetaResponseContextManagement, Schema.Null]).annotate({ \"description\": \"Context management response.\\n\\nInformation about context management strategies applied during the request.\", \"default\": null })),"
- from: "\"container\": Schema.Union([BetaContainer, Schema.Null]).annotate({ \"description\": \"Information about the container used in this request.\\n\\nThis will be non-null if a container tool (e.g. code execution) was used.\", \"default\": null })"
to: "\"container\": Schema.optionalKey(Schema.Union([BetaContainer, Schema.Null]).annotate({ \"description\": \"Information about the container used in this request.\\n\\nThis will be non-null if a container tool (e.g. code execution) was used.\", \"default\": null }))"
# Make context_management optional in BetaMessageDeltaEvent (API may omit this field)
- from: '"context_management": Schema.Union([BetaResponseContextManagement, Schema.Null]).annotate({ "description": "Information about context management strategies applied during the request", "default": null }),'
to: '"context_management": Schema.optionalKey(Schema.Union([BetaResponseContextManagement, Schema.Null]).annotate({ "description": "Information about context management strategies applied during the request", "default": null })),'

View File

@@ -0,0 +1,25 @@
{
"$schema": "../../node_modules/@effect/docgen/schema.json",
"srcLink": "https://github.com/Effect-TS/effect/tree/main/packages/ai/anthropic/src/",
"exclude": ["src/internal/**/*.ts", "src/Generated.ts"],
"examplesCompilerOptions": {
"noEmit": true,
"strict": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"moduleResolution": "Bundler",
"module": "ES2022",
"target": "ES2022",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"types": ["node"],
"rewriteRelativeImportExtensions": true,
"allowImportingTsExtensions": true,
"paths": {
"effect": ["../../../effect/src/index.js"],
"effect/*": ["../../../effect/src/*.js"]
},
"plugins": [
{ "name": "@effect/language-service", "includeSuggestionsInTsc": false }
]
}
}

View File

@@ -0,0 +1,66 @@
{
"name": "@effect/ai-anthropic",
"version": "4.0.0-beta.99",
"type": "module",
"license": "MIT",
"description": "An Anthropic provider integration for Effect AI SDK",
"homepage": "https://effect.website",
"repository": {
"type": "git",
"url": "https://github.com/Effect-TS/effect.git",
"directory": "packages/ai/anthropic"
},
"bugs": {
"url": "https://github.com/Effect-TS/effect/issues"
},
"tags": [
"typescript",
"ai",
"anthropic"
],
"keywords": [
"typescript",
"ai",
"anthropic"
],
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts",
"./*": "./src/*.ts",
"./internal/*": null,
"./*/index": null
},
"files": [
"src/**/*.ts",
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map"
],
"publishConfig": {
"access": "public",
"provenance": true,
"exports": {
"./package.json": "./package.json",
".": "./dist/index.js",
"./*": "./dist/*.js",
"./internal/*": null,
"./*/index": null
}
},
"scripts": {
"codegen": "effect-utils codegen",
"build": "tsc -b tsconfig.json && pnpm babel",
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
"check": "tsc -b tsconfig.json",
"test": "vitest",
"coverage": "vitest --coverage"
},
"devDependencies": {
"effect": "workspace:^"
},
"peerDependencies": {
"effect": "workspace:^"
}
}

View File

@@ -0,0 +1,432 @@
/**
* The `AnthropicClient` module defines the low-level Effect service for
* Anthropic's Messages API. It builds a generated Anthropic HTTP client with
* authentication headers, API version headers, response decoding, and error
* mapping, then exposes helpers for regular and streaming message requests.
*
* @since 4.0.0
*/
import * as Array from "effect/Array"
import type * as Config from "effect/Config"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { identity } from "effect/Function"
import * as Layer from "effect/Layer"
import * as Predicate from "effect/Predicate"
import * as Redacted from "effect/Redacted"
import * as Schema from "effect/Schema"
import * as Stream from "effect/Stream"
import type * as AiError from "effect/unstable/ai/AiError"
import * as Sse from "effect/unstable/encoding/Sse"
import * as Headers from "effect/unstable/http/Headers"
import * as HttpBody from "effect/unstable/http/HttpBody"
import * as HttpClient from "effect/unstable/http/HttpClient"
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import { AnthropicConfig } from "./AnthropicConfig.ts"
import * as Generated from "./Generated.ts"
import * as Errors from "./internal/errors.ts"
// =============================================================================
// Service Interface
// =============================================================================
/**
* Represents the Anthropic client service with methods for the Messages API, including regular and streaming message
* creation.
*
* @category models
* @since 4.0.0
*/
export interface Service {
/**
* The underlying generated Anthropic client that exposes all API endpoints.
*/
readonly client: Generated.AnthropicClient
/**
* Executes a low-level streaming HTTP request and decodes the Server-Sent Events response using the provided schema.
*/
readonly streamRequest: <S extends Sse.EventCodec>(
schema: S
) => (request: HttpClientRequest.HttpClientRequest) => Stream.Stream<
S["Type"],
HttpClientError.HttpClientError | Schema.SchemaError | Sse.Retry,
S["DecodingServices"]
>
/**
* Creates a message using the Anthropic Messages API and maps all errors to the unified `AiError` type.
*/
readonly createMessage: (options: {
readonly payload: typeof Generated.BetaCreateMessageParams.Encoded
readonly params?: typeof Generated.BetaMessagesPostParams.Encoded | undefined
}) => Effect.Effect<
[body: typeof Generated.BetaMessage.Type, response: HttpClientResponse.HttpClientResponse],
AiError.AiError
>
/**
* Creates a streaming message using the Anthropic Messages API and maps all errors to the unified `AiError` type.
*
* **Details**
*
* The returned Effect yields the HTTP response and a stream of events as the model generates its response. The stream
* automatically terminates when a `message_stop` event is received.
*/
readonly createMessageStream: (options: {
readonly payload: Omit<typeof Generated.BetaCreateMessageParams.Encoded, "stream">
readonly params?: typeof Generated.BetaMessagesPostParams.Encoded | undefined
}) => Effect.Effect<
[response: HttpClientResponse.HttpClientResponse, stream: Stream.Stream<MessageStreamEvent, AiError.AiError>],
AiError.AiError
>
}
/**
* Represents an event received from the Anthropic Messages API during a streaming request.
*
* **Details**
*
* Events include:
* - `message_start`: Initial event containing message metadata
* - `message_delta`: Incremental updates to the message (e.g., stop reason)
* - `message_stop`: Final event indicating the message is complete
* - `content_block_start`: Start of a content block
* - `content_block_delta`: Incremental content updates (text, tool use, etc.)
* - `content_block_stop`: End of a content block
* - `error`: Error events with type and message
*
* @category models
* @since 4.0.0
*/
export type MessageStreamEvent =
| typeof Generated.BetaMessageStartEvent.Type
| typeof Generated.BetaMessageDeltaEvent.Type
| typeof Generated.BetaMessageStopEvent.Type
| typeof Generated.BetaContentBlockStartEvent.Type
| typeof Generated.BetaContentBlockDeltaEvent.Type
| typeof Generated.BetaContentBlockStopEvent.Type
| typeof Generated.BetaErrorResponse.Type
// =============================================================================
// Service Identifier
// =============================================================================
/**
* Service tag for the Anthropic client.
*
* **When to use**
*
* Use when accessing or providing the Anthropic client service through Effect's
* context.
*
* @see {@link make} for constructing an Anthropic client effectfully
* @see {@link layer} for providing a client from explicit options
* @see {@link layerConfig} for providing a client from `Config`
*
* @category services
* @since 4.0.0
*/
export class AnthropicClient extends Context.Service<AnthropicClient, Service>()(
"@effect/ai-anthropic/AnthropicClient"
) {}
// =============================================================================
// Options
// =============================================================================
/**
* Configuration for creating an Anthropic client.
*
* **When to use**
*
* Use when the Anthropic client settings are already available as values and
* should be passed directly to `make` or `layer`.
*
* **Details**
*
* These options configure the base Anthropic URL, the `x-api-key`
* authentication header, the `anthropic-version` header, and an optional
* transformation of the underlying `HttpClient`.
*
* @see {@link make} for constructing an Anthropic client from explicit options
* @see {@link layer} for providing an Anthropic client from explicit options
* @see {@link layerConfig} for loading Anthropic client settings from `Config`
*
* @category options
* @since 4.0.0
*/
export type Options = {
/**
* The Anthropic API key for authentication. Requests are made without authentication when this is omitted, which is
* useful for proxied setups or testing.
*/
readonly apiKey?: Redacted.Redacted<string> | undefined
/**
* The base URL for the Anthropic API. Override this to use a proxy or a different API-compatible endpoint.
*
* @default "https://api.anthropic.com"
*/
readonly apiUrl?: string | undefined
/**
* The Anthropic API version header value. This controls which version of the API to use.
*
* @default "2023-06-01"
*/
readonly apiVersion?: string | undefined
/**
* Optional transformer for the underlying HTTP client, such as middleware, logging, or custom request/response
* handling.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}
// =============================================================================
// Constructor
// =============================================================================
const RedactedAnthropicHeaders = {
AnthropicApiKey: "x-api-key"
}
/**
* Creates an Anthropic client service with the given options.
*
* **When to use**
*
* Use when you have explicit configuration values and need an `Effect` that
* constructs the Anthropic client service, rather than providing it as a `Layer`.
*
* **Details**
*
* The client handles API key authentication via the `x-api-key` header, API versioning via the `anthropic-version`
* header, error mapping to the unified `AiError` type, and request/response transformations via `AnthropicConfig`. It
* requires an `HttpClient` in the context.
*
* @see {@link layer} for providing the client as a `Layer` from explicit options
* @see {@link layerConfig} for providing the client as a `Layer` with `Config`-based settings
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(
function*(options: Options): Effect.fn.Return<Service, never, HttpClient.HttpClient> {
const baseClient = yield* HttpClient.HttpClient
const apiVersion = options.apiVersion ?? "2023-06-01"
const httpClient = baseClient.pipe(
HttpClient.mapRequest((request) =>
request.pipe(
HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.anthropic.com"),
Predicate.isNotUndefined(options.apiKey)
? HttpClientRequest.setHeader(
RedactedAnthropicHeaders.AnthropicApiKey,
Redacted.value(options.apiKey)
)
: identity,
HttpClientRequest.setHeader("anthropic-version", apiVersion),
HttpClientRequest.acceptJson
)
),
Predicate.isNotUndefined(options.transformClient)
? options.transformClient
: identity
)
const client = Generated.make(httpClient, {
transformClient: Effect.fnUntraced(function*(client) {
const config = yield* AnthropicConfig.getOrUndefined
if (Predicate.isNotUndefined(config?.transformClient)) {
return config.transformClient(client)
}
return client
})
})
const httpClientOk = HttpClient.filterStatusOk(httpClient)
const streamRequest =
<S extends Sse.EventCodec>(schema: S) =>
(request: HttpClientRequest.HttpClientRequest): Stream.Stream<
S["Type"],
HttpClientError.HttpClientError | Schema.SchemaError | Sse.Retry,
S["DecodingServices"]
> =>
httpClientOk.execute(request).pipe(
Effect.map((response) => response.stream),
Stream.unwrap,
Stream.decodeText,
Stream.pipeThroughChannel(Sse.decodeSchema(schema))
)
const createMessage = (options: {
readonly payload: typeof Generated.BetaCreateMessageParams.Encoded
readonly params?: typeof Generated.BetaMessagesPostParams.Encoded | undefined
}): Effect.Effect<
[body: typeof Generated.BetaMessage.Type, response: HttpClientResponse.HttpClientResponse],
AiError.AiError
> =>
client.betaMessagesPost({ ...options, config: { includeResponse: true } }).pipe(
Effect.catchTags({
BetaMessagesPost4XX: (error) => Effect.fail(Errors.mapClientError(error, "createMessage")),
HttpClientError: (error) => Errors.mapHttpClientError(error, "createMessage"),
SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createMessage"))
})
)
const PingEvent = Schema.Struct({
type: Schema.Literal("ping")
})
const MessageEvent = Schema.Union([
PingEvent,
Generated.BetaMessageStartEvent,
Generated.BetaMessageDeltaEvent,
Generated.BetaMessageStopEvent,
Generated.BetaContentBlockStartEvent,
Generated.BetaContentBlockDeltaEvent,
Generated.BetaContentBlockStopEvent,
Generated.BetaErrorResponse
])
const buildMessageStream = (
response: HttpClientResponse.HttpClientResponse
): [HttpClientResponse.HttpClientResponse, Stream.Stream<MessageStreamEvent, AiError.AiError>] => {
const stream = response.stream.pipe(
Stream.decodeText,
Stream.pipeThroughChannel(Sse.decodeDataSchema(MessageEvent)),
Stream.takeUntil((event) => event.data.type === "message_stop"),
Stream.map((event) => event.data),
Stream.filter((event): event is MessageStreamEvent => event.type !== "ping"),
Stream.catchTags({
// TODO: handle SSE retries
Retry: (error) => Stream.die(error),
HttpClientError: (error) => Stream.fromEffect(Errors.mapHttpClientError(error, "createMessageStream")),
SchemaError: (error) => Stream.fail(Errors.mapSchemaError(error, "createMessageStream"))
})
) as any
return [response, stream]
}
const createMessageStream: Service["createMessageStream"] = (options) => {
const request = HttpClientRequest.post("/v1/messages", {
headers: Headers.fromInput({
"anthropic-beta": options.params?.["anthropic-beta"] ?? undefined,
"anthropic-version": options.params?.["anthropic-version"] ?? apiVersion
}),
body: HttpBody.jsonUnsafe({
...options.payload,
stream: true
})
})
return httpClientOk.execute(request).pipe(
Effect.map(buildMessageStream),
Effect.catchTag(
"HttpClientError",
(error) => Errors.mapHttpClientError(error, "createMessageStream")
)
)
}
return AnthropicClient.of({
client,
streamRequest,
createMessage,
createMessageStream
})
},
Effect.updateService(
Headers.CurrentRedactedNames,
Array.appendAll(Object.values(RedactedAnthropicHeaders))
)
)
// =============================================================================
// Layers
// =============================================================================
/**
* Creates a layer for the Anthropic client with the given options.
*
* **When to use**
*
* Use when you already have explicit `Options` values, such as an API key or
* custom API URL, and want to provide `AnthropicClient` as a `Layer`.
*
* @see {@link make} for constructing the client service effectfully
* @see {@link layerConfig} for loading client settings from `Config`
*
* @category layers
* @since 4.0.0
*/
export const layer = (options: Options): Layer.Layer<AnthropicClient, never, HttpClient.HttpClient> =>
Layer.effect(AnthropicClient, make(options))
/**
* Creates a layer for the Anthropic client, loading the requisite configuration
* via Effect's `Config` module.
*
* **When to use**
*
* Use when you want to provide the Anthropic client as a `Layer` with
* configuration loaded from Effect's `Config` module, such as from environment
* variables or a secrets provider.
*
* @see {@link layer} for providing the client from explicit options instead of `Config`
* @see {@link make} for constructing the client service effectfully
*
* @category layers
* @since 4.0.0
*/
export const layerConfig = (options?: {
/**
* The Anthropic API key for authentication. Requests are made without authentication when this is omitted, which is
* useful for proxied setups or testing.
*/
readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The base URL for the Anthropic API. Override this to use a proxy or a different API-compatible endpoint.
*
* @default "https://api.anthropic.com"
*/
readonly apiUrl?: Config.Config<string> | undefined
/**
* The Anthropic API version header value. This controls which version of the API to use.
*
* @default "2023-06-01"
*/
readonly apiVersion?: Config.Config<string> | undefined
/**
* Optional transformer for the underlying HTTP client, such as middleware, logging, or custom request/response
* handling.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}): Layer.Layer<AnthropicClient, Config.ConfigError, HttpClient.HttpClient> =>
Layer.effect(
AnthropicClient,
Effect.gen(function*() {
const apiKey = Predicate.isNotUndefined(options?.apiKey)
? yield* options.apiKey :
undefined
const apiUrl = Predicate.isNotUndefined(options?.apiUrl)
? yield* options.apiUrl :
undefined
const apiVersion = Predicate.isNotUndefined(options?.apiVersion)
? yield* options.apiVersion :
undefined
return yield* make({
apiKey,
apiUrl,
apiVersion,
transformClient: options?.transformClient
})
})
)

View File

@@ -0,0 +1,84 @@
/**
* The `AnthropicConfig` module lets a workflow temporarily customize the HTTP
* client used by generated Anthropic requests. It is used by
* `AnthropicClient` when request helpers run, so code can add middleware,
* logging, or other client changes without rebuilding the client layer.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import type { HttpClient } from "effect/unstable/http/HttpClient"
/**
* Service tag for Anthropic client configuration overrides, such as transformations applied to the generated HTTP client.
*
* **When to use**
*
* Use when you need to provide or read Anthropic client configuration through
* Effect's context from a layer or integration.
*
* @see {@link withClientTransform} for scoping an HTTP client transformation
*
* @category services
* @since 4.0.0
*/
export class AnthropicConfig extends Context.Service<
AnthropicConfig,
AnthropicConfig.Service
>()("@effect/ai-anthropic/AnthropicConfig") {
/**
* Gets the configured Anthropic service from the current context when present.
*
* @since 4.0.0
*/
static readonly getOrUndefined: Effect.Effect<typeof AnthropicConfig.Service | undefined> = Effect.map(
Effect.context<never>(),
(services) => services.mapUnsafe.get(AnthropicConfig.key)
)
}
/**
* Namespace containing types associated with the `AnthropicConfig` service.
*
* @since 4.0.0
*/
export declare namespace AnthropicConfig {
/**
* Configuration provided through `AnthropicConfig`.
*
* **Details**
*
* Use `transformClient` to wrap or replace the `HttpClient` used by generated Anthropic API requests.
*
* @category models
* @since 4.0.0
*/
export interface Service {
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined
}
}
/**
* Runs an effect with an `AnthropicConfig` override that transforms the underlying `HttpClient` used by generated Anthropic requests.
*
* **When to use**
*
* Use when you need to apply a temporary `HttpClient` transformation, such as adding middleware or logging, to a
* specific scope of an effectful program.
*
* @category configuration
* @since 4.0.0
*/
export const withClientTransform: {
(transform: (client: HttpClient) => HttpClient): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<A, E, R>(self: Effect.Effect<A, E, R>, transform: (client: HttpClient) => HttpClient): Effect.Effect<A, E, R>
} = dual(2, <A, E, R>(
self: Effect.Effect<A, E, R>,
transformClient: (client: HttpClient) => HttpClient
) =>
Effect.flatMap(
AnthropicConfig.getOrUndefined,
(config) => Effect.provideService(self, AnthropicConfig, { ...config, transformClient })
))

View File

@@ -0,0 +1,212 @@
/**
* Anthropic error metadata augmentation.
*
* Provides Anthropic-specific metadata fields for AI error types through module
* augmentation, enabling typed access to Anthropic error details.
*
* @since 4.0.0
*/
/**
* Anthropic-specific error metadata fields.
*
* **Details**
*
* Contains the Anthropic error type and request identifier copied from provider
* error responses when available. Either field may be `null` when Anthropic
* does not include it or the response cannot be decoded.
*
* @see {@link AnthropicRateLimitMetadata} for rate-limit responses that also include parsed Anthropic rate-limit headers
*
* @category models
* @since 4.0.0
*/
export type AnthropicErrorMetadata = {
/**
* The Anthropic error type returned by the API.
*/
readonly errorType: string | null
/**
* The unique request ID for debugging with Anthropic support.
*/
readonly requestId: string | null
}
/**
* Anthropic-specific rate limit metadata fields.
*
* **Details**
*
* Extends base error metadata with rate limit-specific information from Anthropic's rate limit headers.
*
* @category models
* @since 4.0.0
*/
export type AnthropicRateLimitMetadata = AnthropicErrorMetadata & {
/**
* Number of requests allowed in the current period.
*/
readonly requestsLimit: number | null
/**
* Number of requests remaining in the current period.
*/
readonly requestsRemaining: number | null
/**
* Time when the request rate limit resets.
*/
readonly requestsReset: string | null
/**
* Number of tokens allowed in the current period.
*/
readonly tokensLimit: number | null
/**
* Number of tokens remaining in the current period.
*/
readonly tokensRemaining: number | null
/**
* Time when the token rate limit resets.
*/
readonly tokensReset: string | null
}
declare module "effect/unstable/ai/AiError" {
/**
* Anthropic metadata attached to `RateLimitError` values.
*
* **Details**
*
* Includes request identifiers, Anthropic error types, and parsed request or token limit headers when the provider rejects a request due to rate limits.
*
* @category configuration
* @since 4.0.0
*/
export interface RateLimitErrorMetadata {
readonly anthropic?: AnthropicRateLimitMetadata | null
}
/**
* Anthropic metadata attached to `QuotaExhaustedError` values.
*
* **Details**
*
* Captures the Anthropic error type and request identifier for failures where the account or workspace has exhausted its available quota.
*
* @category configuration
* @since 4.0.0
*/
export interface QuotaExhaustedErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `AuthenticationError` values.
*
* **Details**
*
* Preserves Anthropic error details for missing, invalid, or unauthorized API credentials while keeping the error in the shared AI error model.
*
* @category configuration
* @since 4.0.0
*/
export interface AuthenticationErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `ContentPolicyError` values.
*
* **Details**
*
* Records Anthropic error details returned when a request or response is rejected by Anthropic safety or content policy enforcement.
*
* @category configuration
* @since 4.0.0
*/
export interface ContentPolicyErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `InvalidRequestError` values.
*
* **Details**
*
* Provides the Anthropic error type and request identifier for malformed or unsupported requests rejected before model execution.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidRequestErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `InternalProviderError` values.
*
* **Details**
*
* Preserves Anthropic request correlation data for provider-side failures that should be reported or investigated with Anthropic support.
*
* @category configuration
* @since 4.0.0
*/
export interface InternalProviderErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `InvalidOutputError` values.
*
* **Details**
*
* Describes Anthropic-specific context for responses that could not be decoded or interpreted as valid AI output.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidOutputErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `StructuredOutputError` values.
*
* **Details**
*
* Captures Anthropic error details for structured-output failures, including request correlation data useful when diagnosing schema-related responses.
*
* @category configuration
* @since 4.0.0
*/
export interface StructuredOutputErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `UnsupportedSchemaError` values.
*
* **Details**
*
* Provides Anthropic error details for schemas that cannot be represented by or submitted to the Anthropic API.
*
* @category configuration
* @since 4.0.0
*/
export interface UnsupportedSchemaErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
/**
* Anthropic metadata attached to `UnknownError` values.
*
* **Details**
*
* Retains the Anthropic error type and request identifier when a provider response cannot be classified as a more specific AI error.
*
* @category configuration
* @since 4.0.0
*/
export interface UnknownErrorMetadata {
readonly anthropic?: AnthropicErrorMetadata | null
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,132 @@
/**
* The `AnthropicTelemetry` module defines Anthropic-specific telemetry
* attributes and a helper for adding them to a tracing span. It keeps the
* standard GenAI telemetry attributes and adds request and response metadata
* under the `gen_ai.anthropic.*` OpenTelemetry namespaces.
*
* @since 4.0.0
*/
import { dual } from "effect/Function"
import * as String from "effect/String"
import type { Span } from "effect/Tracer"
import type { Simplify } from "effect/Types"
import * as Telemetry from "effect/unstable/ai/Telemetry"
/**
* The attributes used to describe telemetry in the context of Generative
* Artificial Intelligence (GenAI) Models requests and responses.
*
* **Details**
*
* These attributes follow the OpenTelemetry generative AI semantic
* conventions:
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/
*
* @category models
* @since 4.0.0
*/
export type AnthropicTelemetryAttributes = Simplify<
& Telemetry.GenAITelemetryAttributes
& Telemetry.AttributesWithPrefix<RequestAttributes, "gen_ai.anthropic.request">
& Telemetry.AttributesWithPrefix<ResponseAttributes, "gen_ai.anthropic.response">
>
/**
* All telemetry attributes which are part of the GenAI specification,
* including the Anthropic-specific attributes.
*
* @category models
* @since 4.0.0
*/
export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.anthropic.request`.
*
* @category models
* @since 4.0.0
*/
export interface RequestAttributes {
/**
* Whether extended thinking is enabled.
*/
readonly extendedThinking?: boolean | null | undefined
/**
* The budget tokens for extended thinking.
*/
readonly thinkingBudgetTokens?: number | null | undefined
}
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.anthropic.response`.
*
* @category models
* @since 4.0.0
*/
export interface ResponseAttributes {
/**
* The stop reason from the response.
*/
readonly stopReason?: string | null | undefined
/**
* Number of cache creation input tokens.
*/
readonly cacheCreationInputTokens?: number | null | undefined
/**
* Number of cache read input tokens.
*/
readonly cacheReadInputTokens?: number | null | undefined
}
/**
* Options accepted by `addGenAIAnnotations`, combining standard GenAI telemetry attributes with optional Anthropic request and response attributes.
*
* @category options
* @since 4.0.0
*/
export type AnthropicTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeOptions & {
anthropic?: {
request?: RequestAttributes | undefined
response?: ResponseAttributes | undefined
} | undefined
}
const addAnthropicRequestAttributes = Telemetry.addSpanAttributes("gen_ai.anthropic.request", String.camelToSnake)<
RequestAttributes
>
const addAnthropicResponseAttributes = Telemetry.addSpanAttributes("gen_ai.anthropic.response", String.camelToSnake)<
ResponseAttributes
>
/**
* Applies the specified Anthropic GenAI telemetry attributes to the provided
* `Span`.
*
* **When to use**
*
* Use to annotate an Anthropic model span with standard GenAI telemetry
* attributes and Anthropic-specific request or response metadata.
*
* **Gotchas**
*
* This method mutates the `Span` in place.
*
* @category annotations
* @since 4.0.0
*/
export const addGenAIAnnotations: {
(options: AnthropicTelemetryAttributeOptions): (span: Span) => void
(span: Span, options: AnthropicTelemetryAttributeOptions): void
} = dual(2, (span: Span, options: AnthropicTelemetryAttributeOptions) => {
Telemetry.addGenAIAnnotations(span, options)
if (options.anthropic != null) {
if (options.anthropic.request != null) {
addAnthropicRequestAttributes(span, options.anthropic.request)
}
if (options.anthropic.response != null) {
addAnthropicResponseAttributes(span, options.anthropic.response)
}
}
})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
/**
* @since 4.0.0
*/
// @barrel: Auto-generated exports. Do not edit manually.
/**
* @since 4.0.0
*/
export * as AnthropicClient from "./AnthropicClient.ts"
/**
* @since 4.0.0
*/
export * as AnthropicConfig from "./AnthropicConfig.ts"
/**
* @since 4.0.0
*/
export * as AnthropicError from "./AnthropicError.ts"
/**
* @since 4.0.0
*/
export * as AnthropicLanguageModel from "./AnthropicLanguageModel.ts"
/**
* @since 4.0.0
*/
export * as AnthropicTelemetry from "./AnthropicTelemetry.ts"
/**
* @since 4.0.0
*/
export * as AnthropicTool from "./AnthropicTool.ts"
/**
* @since 4.0.0
*/
export * as Generated from "./Generated.ts"

View File

@@ -0,0 +1,370 @@
import * as Duration from "effect/Duration"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Number from "effect/Number"
import * as Option from "effect/Option"
import * as Predicate from "effect/Predicate"
import * as Redactable from "effect/Redactable"
import * as Schema from "effect/Schema"
import * as AiError from "effect/unstable/ai/AiError"
import type * as Response from "effect/unstable/ai/Response"
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import type { AnthropicErrorMetadata } from "../AnthropicError.ts"
import type * as Generated from "../Generated.ts"
// =============================================================================
// Anthropic Error Body Schema
// =============================================================================
/** @internal */
export const AnthropicErrorBody = Schema.Struct({
type: Schema.Literal("error"),
error: Schema.Struct({
type: Schema.String,
message: Schema.String
})
})
/** @internal */
export type AnthropicClientErrorBody = {
readonly type: "error"
readonly error: {
readonly type: string
readonly message: string
}
readonly request_id: string | null
}
// =============================================================================
// Error Mappers
// =============================================================================
/** @internal */
export const mapSchemaError = dual<
(method: string) => (error: Schema.SchemaError) => AiError.AiError,
(error: Schema.SchemaError, method: string) => AiError.AiError
>(2, (error, method) =>
AiError.make({
module: "AnthropicClient",
method,
reason: AiError.InvalidOutputError.fromSchemaError(error)
}))
/** @internal */
export const mapClientError = dual<
(method: string) => (error: Generated.AnthropicClientError<string, AnthropicClientErrorBody>) => AiError.AiError,
(error: Generated.AnthropicClientError<string, AnthropicClientErrorBody>, method: string) => AiError.AiError
>(2, (error, method) => {
const { request, response, cause } = error
const status = response.status
const headers = response.headers as Record<string, string>
const metadata: AnthropicErrorMetadata = {
errorType: cause.error.type,
requestId: cause.request_id
}
const http = buildHttpContext({ request, response, body: JSON.stringify(cause) })
const reason = mapStatusCodeToReason({
status,
headers,
message: cause.error.message,
metadata,
http
})
return AiError.make({ module: "AnthropicClient", method, reason })
})
/** @internal */
export const mapHttpClientError = dual<
(method: string) => (error: HttpClientError.HttpClientError) => Effect.Effect<never, AiError.AiError>,
(error: HttpClientError.HttpClientError, method: string) => Effect.Effect<never, AiError.AiError>
>(2, (error, method) => {
const reason = error.reason
switch (reason._tag) {
case "TransportError": {
return Effect.fail(AiError.make({
module: "AnthropicClient",
method,
reason: new AiError.NetworkError({
reason: "TransportError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "EncodeError": {
return Effect.fail(AiError.make({
module: "AnthropicClient",
method,
reason: new AiError.NetworkError({
reason: "EncodeError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "InvalidUrlError": {
return Effect.fail(AiError.make({
module: "AnthropicClient",
method,
reason: new AiError.NetworkError({
reason: "InvalidUrlError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "StatusCodeError": {
return mapStatusCodeError(reason, method)
}
case "DecodeError": {
return Effect.fail(AiError.make({
module: "AnthropicClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Failed to decode response"
})
}))
}
case "EmptyBodyError": {
return Effect.fail(AiError.make({
module: "AnthropicClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Response body was empty"
})
}))
}
}
})
/** @internal */
const mapStatusCodeError = Effect.fnUntraced(function*(
error: HttpClientError.StatusCodeError,
method: string
) {
const { request, response, description } = error
const status = response.status
const headers = response.headers as Record<string, string>
const requestId = headers["request-id"]
let body: string | undefined = description
if (!description || !description.startsWith("{")) {
const responseBody = yield* Effect.option(response.text)
if (Option.isSome(responseBody) && responseBody.value) {
body = responseBody.value
}
}
let json: unknown = undefined
// @effect-diagnostics effect/tryCatchInEffectGen:off
try {
json = Predicate.isNotUndefined(body) ? JSON.parse(body) : undefined
} catch {
json = undefined
}
const decoded = Schema.decodeUnknownOption(AnthropicErrorBody)(json)
const reason = mapStatusCodeToReason({
status,
headers,
message: Option.isSome(decoded) ? decoded.value.error.message : undefined,
http: buildHttpContext({ request, response, body }),
metadata: {
errorType: Option.isSome(decoded) ? decoded.value.error.type : null,
requestId: requestId ?? null
}
})
return yield* AiError.make({ module: "AnthropicClient", method, reason })
})
// =============================================================================
// Rate Limits
// =============================================================================
/** @internal */
export const parseRateLimitHeaders = (headers: Record<string, string>) => {
const retryAfterRaw = headers["retry-after"]
let retryAfter: Duration.Duration | undefined
if (Predicate.isNotUndefined(retryAfterRaw)) {
const parsed = Number.parse(retryAfterRaw)
if (Option.isSome(parsed)) {
retryAfter = Duration.seconds(parsed.value)
}
}
const requestsLimitRaw = headers["anthropic-ratelimit-requests-limit"]
const requestsRemainingRaw = headers["anthropic-ratelimit-requests-remaining"]
const tokensLimitRaw = headers["anthropic-ratelimit-tokens-limit"]
const tokensRemainingRaw = headers["anthropic-ratelimit-tokens-remaining"]
return {
retryAfter,
requestsLimit: Predicate.isNotUndefined(requestsLimitRaw) ? Option.getOrNull(Number.parse(requestsLimitRaw)) : null,
requestsRemaining: Predicate.isNotUndefined(requestsRemainingRaw)
? Option.getOrNull(Number.parse(requestsRemainingRaw))
: null,
requestsReset: headers["anthropic-ratelimit-requests-reset"] ?? null,
tokensLimit: Predicate.isNotUndefined(tokensLimitRaw) ? Option.getOrNull(Number.parse(tokensLimitRaw)) : null,
tokensRemaining: Predicate.isNotUndefined(tokensRemainingRaw)
? Option.getOrNull(Number.parse(tokensRemainingRaw))
: null,
tokensReset: headers["anthropic-ratelimit-tokens-reset"] ?? null
}
}
// =============================================================================
// HTTP Context
// =============================================================================
/** @internal */
export const buildHttpRequestDetails = (
request: HttpClientRequest.HttpClientRequest
): typeof Response.HttpRequestDetails.Type => ({
method: request.method,
url: request.url,
urlParams: Array.from(request.urlParams),
hash: Option.getOrUndefined(request.hash),
headers: Redactable.redact(request.headers) as Record<string, string>
})
/** @internal */
export const buildHttpContext = (params: {
readonly request: HttpClientRequest.HttpClientRequest
readonly response?: HttpClientResponse.HttpClientResponse
readonly body?: string | undefined
}): typeof AiError.HttpContext.Type => ({
request: buildHttpRequestDetails(params.request),
response: Predicate.isNotUndefined(params.response)
? {
status: params.response.status,
headers: Redactable.redact(params.response.headers) as Record<string, string>
}
: undefined,
body: params.body
})
// =============================================================================
// HTTP Status Code
// =============================================================================
const buildInvalidRequestDescription = (params: {
readonly status: number
readonly message: string | undefined
readonly method: string
readonly url: string
readonly errorType: string | null
readonly requestId: string | null
readonly body: string | undefined
}): string => {
const parts: Array<string> = []
if (params.message) {
parts.push(params.message)
} else {
parts.push(`HTTP ${params.status}`)
}
parts.push(`(${params.method} ${params.url})`)
if (params.errorType) {
parts.push(`[type: ${params.errorType}]`)
}
if (params.requestId) {
parts.push(`[requestId: ${params.requestId}]`)
}
if (!params.message && params.body) {
const truncated = params.body.length > 200
? params.body.slice(0, 200) + "..."
: params.body
parts.push(`Response: ${truncated}`)
}
return parts.join(" ")
}
/** @internal */
export const mapStatusCodeToReason = ({ status, headers, message, metadata, http }: {
readonly status: number
readonly headers: Record<string, string>
readonly message: string | undefined
readonly metadata: AnthropicErrorMetadata
readonly http: typeof AiError.HttpContext.Type
}): AiError.AiErrorReason => {
const invalidRequestDescription = buildInvalidRequestDescription({
status,
message,
method: http.request.method,
url: http.request.url,
errorType: metadata.errorType,
requestId: metadata.requestId,
body: http.body
})
switch (status) {
case 400:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { anthropic: metadata },
http
})
case 401:
return new AiError.AuthenticationError({
kind: "InvalidKey",
metadata: { anthropic: metadata },
http
})
case 403:
return new AiError.AuthenticationError({
kind: "InsufficientPermissions",
metadata: { anthropic: metadata },
http
})
case 404:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { anthropic: metadata },
http
})
case 422:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { anthropic: metadata },
http
})
case 429: {
const { retryAfter, ...rateLimitMetadata } = parseRateLimitHeaders(headers)
return new AiError.RateLimitError({
retryAfter,
metadata: {
anthropic: {
...metadata,
...rateLimitMetadata
}
},
http
})
}
case 529:
return new AiError.InternalProviderError({
description: message ?? "Anthropic API is overloaded",
metadata: { anthropic: metadata },
http
})
default:
if (status >= 500) {
return new AiError.InternalProviderError({
description: message ?? "Server error",
metadata: { anthropic: metadata },
http
})
}
return new AiError.UnknownError({
description: message,
metadata: { anthropic: metadata },
http
})
}
}

View File

@@ -0,0 +1,26 @@
import * as Predicate from "effect/Predicate"
import type * as Response from "effect/unstable/ai/Response"
const finishReasonMap: Record<string, Response.FinishReason> = {
end_turn: "stop",
max_tokens: "length",
pause_turn: "pause",
refusal: "content-filter",
stop_sequence: "stop",
tool_use: "tool-calls"
}
/** @internal */
export const resolveFinishReason = (
finishReason: string,
isJsonResponse: boolean = false
): Response.FinishReason => {
const reason = finishReasonMap[finishReason]
if (Predicate.isUndefined(reason)) {
return "unknown"
}
if (isJsonResponse && reason === "tool-calls") {
return "stop"
}
return reason
}

View File

@@ -0,0 +1,526 @@
import { AnthropicClient, AnthropicLanguageModel, AnthropicTool } from "@effect/ai-anthropic"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Layer, Redacted, Schema, Stream } from "effect"
import { AnthropicStructuredOutput, LanguageModel, Tool, Toolkit } from "effect/unstable/ai"
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
describe("AnthropicLanguageModel", () => {
describe("streamText", () => {
it.effect("decodes tool call params in content_block_stop", () =>
Effect.gen(function*() {
const toolParams = { pattern: "*.ts" }
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(sseResponse(request, [
{
type: "message_start",
message: {
id: "msg_test_1",
type: "message",
role: "assistant",
model: "claude-sonnet-4-20250514",
content: [],
stop_reason: null,
stop_sequence: null,
usage: {
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
input_tokens: 10,
output_tokens: 0,
service_tier: null
}
}
},
{
type: "content_block_start",
index: 0,
content_block: {
type: "tool_use",
id: "toolu_test_1",
name: "GlobTool",
input: {}
}
},
{
type: "content_block_delta",
index: 0,
delta: {
type: "input_json_delta",
partial_json: JSON.stringify(toolParams)
}
},
{
type: "content_block_stop",
index: 0
},
{
type: "message_delta",
delta: {
stop_reason: "tool_use",
stop_sequence: null
},
usage: {
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
input_tokens: null,
output_tokens: 5
}
},
{
type: "message_stop"
}
]))
)
))
)
const GlobTool = Tool.make("GlobTool", {
description: "Search for files",
parameters: Schema.Struct({ pattern: Schema.String }),
success: Schema.String
})
const toolkit = Toolkit.make(GlobTool)
const toolkitLayer = toolkit.toLayer({
GlobTool: () => Effect.succeed("found.ts")
})
const partsChunk = yield* LanguageModel.streamText({
prompt: "find ts files",
toolkit,
disableToolCallResolution: true
}).pipe(
Stream.runCollect,
Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")),
Effect.provide(toolkitLayer),
Effect.provide(layer)
)
const parts = globalThis.Array.from(partsChunk)
const toolCall = parts.find((part) => part.type === "tool-call")
assert.isDefined(toolCall)
if (toolCall?.type !== "tool-call") {
return
}
assert.strictEqual(toolCall.name, "GlobTool")
assert.deepStrictEqual(toolCall.params, toolParams)
}))
// `Model` is an open enum in Anthropic's spec (`anyOf: [{ type: string }, ...consts]`), and it is
// $ref'd by response schemas. Responses must therefore decode for model ids that are newer than the
// generated literals, and the id must survive decoding unchanged.
it.effect("decodes responses for a model id that is not a known literal", () =>
Effect.gen(function*() {
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(sseResponse(request, [
{
type: "message_start",
message: {
id: "msg_test_1",
type: "message",
role: "assistant",
model: "claude-not-a-known-model-id",
content: [],
stop_reason: null,
stop_sequence: null,
usage: {
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
input_tokens: 10,
output_tokens: 0,
service_tier: null
}
}
},
{
type: "content_block_start",
index: 0,
content_block: { type: "text", text: "" }
},
{
type: "content_block_delta",
index: 0,
delta: { type: "text_delta", text: "Hello" }
},
{
type: "content_block_stop",
index: 0
},
{
type: "message_delta",
delta: {
stop_reason: "end_turn",
stop_sequence: null
},
usage: {
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
input_tokens: null,
output_tokens: 5
}
},
{
type: "message_stop"
}
]))
)
))
)
const partsChunk = yield* LanguageModel.streamText({
prompt: "say hello"
}).pipe(
Stream.runCollect,
Effect.provide(AnthropicLanguageModel.model("claude-not-a-known-model-id")),
Effect.provide(layer)
)
const parts = globalThis.Array.from(partsChunk)
const metadata = parts.find((part) => part.type === "response-metadata")
assert.isDefined(metadata)
if (metadata?.type !== "response-metadata") {
return
}
assert.strictEqual(metadata.modelId, "claude-not-a-known-model-id")
const text = parts.find((part) => part.type === "text-delta")
assert.isDefined(text)
if (text?.type !== "text-delta") {
return
}
assert.strictEqual(text.delta, "Hello")
}))
})
describe("generateText", () => {
it.effect("encodes dynamic tools", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined = undefined
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
id: "msg_test_1",
type: "message",
role: "assistant",
model: "claude-sonnet-4-20250514",
content: [{ type: "text", text: "Done" }],
stop_reason: "end_turn",
stop_sequence: null,
usage: {
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
input_tokens: 10,
output_tokens: 5,
service_tier: null
}
}))
})
))
)
const inputSchema = {
type: "object",
properties: {
query: { type: "string" },
limit: { type: "number" }
},
required: ["query"],
additionalProperties: false
} as const
const DynamicTool = Tool.dynamic("DynamicTool", {
description: "A dynamic tool",
parameters: inputSchema
})
yield* LanguageModel.generateText({
prompt: "Use the dynamic tool",
toolkit: Toolkit.make(DynamicTool),
disableToolCallResolution: true
}).pipe(
Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")),
Effect.provide(layer)
)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
const dynamicTool = body.tools.find((tool: any) => tool.name === "DynamicTool")
assert.isDefined(dynamicTool)
if (dynamicTool === undefined) {
return
}
assert.strictEqual(dynamicTool.description, "A dynamic tool")
assert.deepStrictEqual(dynamicTool.input_schema, inputSchema)
}))
})
describe("generateObject", () => {
// A model that supports native structured output requests it via `output_config.format` (json_schema)
// rather than falling back to a forced JSON tool.
const assertNativeStructuredOutput = (model: string) =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined = undefined
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
id: "msg_test_1",
type: "message",
role: "assistant",
model,
content: [{ type: "text", text: JSON.stringify({ name: "John", age: 30 }) }],
stop_reason: "end_turn",
stop_sequence: null,
usage: {
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
input_tokens: 10,
output_tokens: 5,
service_tier: null
}
}))
})
))
)
// Assert the request shape; the response outcome is irrelevant here.
yield* LanguageModel.generateObject({
prompt: "Give me a person",
schema: Schema.Struct({ name: Schema.String, age: Schema.Number })
}).pipe(
Effect.provide(AnthropicLanguageModel.model(model)),
Effect.provide(layer),
Effect.ignore
)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
assert.strictEqual(body.output_config?.format?.type, "json_schema")
})
it.effect("uses native json_schema output for claude-opus-4-6", () =>
assertNativeStructuredOutput("claude-opus-4-6"))
it.effect("uses native json_schema output for claude-sonnet-4-6", () =>
assertNativeStructuredOutput("claude-sonnet-4-6"))
})
// The packaged `Memory_20250818` tool ships `customName: "AnthropicMemory"` /
// `providerName: "memory"`, and is a client-executed provider tool. These
// tests cover the round-trip that was broken on beta.98 (see #2615):
// - the provider wire name ("memory") must resolve to the toolkit's custom
// name ("AnthropicMemory"), otherwise `makeResponse` raises ToolNotFound
// - `view_range` uses `Schema.optionalKey`, otherwise the Anthropic codec
// rejects the tool schema with "Unsupported AST Undefined"
// - `create` must carry `file_text`, otherwise the file body is dropped
describe("Memory tool", () => {
const memoryResponse = (request: HttpClientRequest.HttpClientRequest, input: unknown) =>
jsonResponse(request, {
id: "msg_test_1",
type: "message",
role: "assistant",
model: "claude-sonnet-4-20250514",
content: [{ type: "tool_use", id: "toolu_mem_1", name: "memory", input }],
stop_reason: "tool_use",
stop_sequence: null,
usage: {
cache_creation: null,
cache_creation_input_tokens: null,
cache_read_input_tokens: null,
inference_geo: null,
input_tokens: 10,
output_tokens: 5,
service_tier: null
}
})
it.effect("resolves the provider wire name to the tool's custom name (view)", () =>
Effect.gen(function*() {
let receivedParams: unknown = undefined
const toolkit = Toolkit.make(AnthropicTool.Memory_20250818({}))
const toolkitLayer = toolkit.toLayer({
AnthropicMemory: (params) => {
receivedParams = params
return Effect.succeed("memory listing")
}
})
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => Effect.succeed(memoryResponse(request, { command: "view", path: "/memories" })))
))
)
const response = yield* LanguageModel.generateText({
prompt: "check memory",
toolkit
}).pipe(
Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")),
Effect.provide(toolkitLayer),
Effect.provide(layer)
)
// Handler was resolved under the custom name and received the decoded command
assert.deepStrictEqual(receivedParams, { command: "view", path: "/memories" })
const toolResult = response.toolResults[0]
assert.isDefined(toolResult)
assert.strictEqual(toolResult.name, "AnthropicMemory")
assert.strictEqual(toolResult.result, "memory listing")
}))
it.effect("decodes the create command including file_text", () =>
Effect.gen(function*() {
let receivedParams: unknown = undefined
const toolkit = Toolkit.make(AnthropicTool.Memory_20250818({}))
const toolkitLayer = toolkit.toLayer({
AnthropicMemory: (params) => {
receivedParams = params
return Effect.succeed("File created successfully at: /memories/notes.txt")
}
})
const layer = AnthropicClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(memoryResponse(request, {
command: "create",
path: "/memories/notes.txt",
file_text: "hello world"
}))
)
))
)
const response = yield* LanguageModel.generateText({
prompt: "save a note",
toolkit
}).pipe(
Effect.provide(AnthropicLanguageModel.model("claude-sonnet-4-20250514")),
Effect.provide(toolkitLayer),
Effect.provide(layer)
)
assert.deepStrictEqual(receivedParams, {
command: "create",
path: "/memories/notes.txt",
file_text: "hello world"
})
assert.strictEqual(response.toolResults[0]?.name, "AnthropicMemory")
}))
})
// Client-executed (`requiresHandler`) provider tools have their `parameters`
// decoded via `toCodecAnthropic` when the model calls them. Optional
// parameters must use `Schema.optionalKey` (not `Schema.optional`), otherwise
// the codec rejects the schema with "Unsupported AST Undefined" (see #2615).
describe("client provider tool parameters compile with the Anthropic codec", () => {
const displayArgs = { displayWidthPx: 800, displayHeightPx: 600 }
const clientTools: ReadonlyArray<readonly [string, Tool.AnyProviderDefined]> = [
["Bash_20241022", AnthropicTool.Bash_20241022({})],
["Bash_20250124", AnthropicTool.Bash_20250124({})],
["ComputerUse_20241022", AnthropicTool.ComputerUse_20241022(displayArgs)],
["ComputerUse_20250124", AnthropicTool.ComputerUse_20250124(displayArgs)],
["ComputerUse_20251124", AnthropicTool.ComputerUse_20251124(displayArgs)],
["Memory_20250818", AnthropicTool.Memory_20250818({})],
["TextEditor_20241022", AnthropicTool.TextEditor_20241022({})],
["TextEditor_20250124", AnthropicTool.TextEditor_20250124({})],
["TextEditor_20250429", AnthropicTool.TextEditor_20250429({})],
["TextEditor_20250728", AnthropicTool.TextEditor_20250728({})]
]
for (const [name, tool] of clientTools) {
it(name, () => {
const codec = AnthropicStructuredOutput.toCodecAnthropic(tool.parametersSchema)
assert.isDefined(codec)
})
}
})
})
const makeHttpClient = (
handler: (
request: HttpClientRequest.HttpClientRequest
) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError>
) =>
HttpClient.makeWith(
Effect.fnUntraced(function*(requestEffect) {
const request = yield* requestEffect
return yield* handler(request)
}),
Effect.succeed as HttpClient.HttpClient.Preprocess<HttpClientError.HttpClientError, never>
)
const sseResponse = (
request: HttpClientRequest.HttpClientRequest,
events: ReadonlyArray<unknown>
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(toSseBody(events), {
status: 200,
headers: {
"content-type": "text/event-stream"
}
})
)
const jsonResponse = (
request: HttpClientRequest.HttpClientRequest,
body: unknown
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(body), {
status: 200,
headers: {
"content-type": "application/json"
}
})
)
const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
Effect.gen(function*() {
const body = request.body
if (body._tag !== "Uint8Array") {
return yield* Effect.die(new Error("Expected Uint8Array body"))
}
return JSON.parse(new TextDecoder().decode(body.body))
})
const toSseBody = (events: ReadonlyArray<unknown>): string =>
events.map((event) => `event: message_stream\ndata: ${JSON.stringify(event)}\n\n`).join("")

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"references": [
{ "path": "../../effect" }
]
}

View File

@@ -0,0 +1,14 @@
import { AnthropicLanguageModel } from "@effect/ai-anthropic"
import { describe, expect, it } from "tstyche"
declare const acceptsKnownModel: (model: AnthropicLanguageModel.Model) => void
describe("AnthropicLanguageModel", () => {
describe("Model", () => {
it("keeps the known model ids as literals, while the constructors still accept custom ids", () => {
expect(acceptsKnownModel).type.toBeCallableWith("claude-opus-4-8")
expect(acceptsKnownModel).type.not.toBeCallableWith("not-a-real-model")
expect(AnthropicLanguageModel.model).type.toBeCallableWith("not-a-real-model")
})
})
})

View File

@@ -0,0 +1,6 @@
import { mergeConfig, type ViteUserConfig } from "vitest/config"
import shared from "../../../vitest.shared.ts"
const config: ViteUserConfig = {}
export default mergeConfig(shared, config)

View File

@@ -0,0 +1,742 @@
# @effect/ai-openai-compat
## 4.0.0-beta.99
### Patch Changes
- Updated dependencies [[`8ce4795`](https://github.com/Effect-TS/effect/commit/8ce4795ccbaebca4292757db568c005a992546a4), [`80b539f`](https://github.com/Effect-TS/effect/commit/80b539f8aba68f478c75c35c2b4140c4ffc4fada), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`e6e6dba`](https://github.com/Effect-TS/effect/commit/e6e6dba6e9d86e7c2ad27dcedf289db76a19697f), [`bfb203e`](https://github.com/Effect-TS/effect/commit/bfb203e95aa439f731acad37fc3a9a831a190f1c), [`2e9a34a`](https://github.com/Effect-TS/effect/commit/2e9a34ac2bece4f3a206160480c991e3841dc67a), [`55d4eb3`](https://github.com/Effect-TS/effect/commit/55d4eb34f2c64d54f6a25a305b5c5438ebd7934e), [`bddb010`](https://github.com/Effect-TS/effect/commit/bddb010eac3d4436cb094edbbee7460c5440c162), [`a328835`](https://github.com/Effect-TS/effect/commit/a328835e50d76bc96648a1c1550456e8c9f81210), [`5560d05`](https://github.com/Effect-TS/effect/commit/5560d05aa6abdd29466d9c3412cc5e648b0adbde), [`8f6e3ad`](https://github.com/Effect-TS/effect/commit/8f6e3adb185b16e8820b98c509b308086f7ff1af), [`46997fa`](https://github.com/Effect-TS/effect/commit/46997fa60401f5e3c93daa4b61f7df8e31caaab4), [`9e6e12d`](https://github.com/Effect-TS/effect/commit/9e6e12d75c118cd265496f2880490d1f33a5c8bf), [`3394b93`](https://github.com/Effect-TS/effect/commit/3394b93d97d6f24fc38670641d1490289ffca7f1), [`febeabc`](https://github.com/Effect-TS/effect/commit/febeabc3f7c31094da000a23edeaabfe2ab00a38), [`54161c9`](https://github.com/Effect-TS/effect/commit/54161c98f6f3569e0c31842f54e6a257f9421c4c), [`385f7a4`](https://github.com/Effect-TS/effect/commit/385f7a4ee4a7359928597ea56d151dbaf5eb5802), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd), [`44b9cf3`](https://github.com/Effect-TS/effect/commit/44b9cf3d240d726997b4bbcd0ede48e074d3c456), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`0a8aa6a`](https://github.com/Effect-TS/effect/commit/0a8aa6acb90a72b91c24d17133c950e4cacd8abd), [`c8d9fcf`](https://github.com/Effect-TS/effect/commit/c8d9fcf7b030f7c474effbab2764ce7aee1c7209), [`9ca7f9a`](https://github.com/Effect-TS/effect/commit/9ca7f9a69363e4485645966d5a93b8f9597c5206), [`e7aca89`](https://github.com/Effect-TS/effect/commit/e7aca894bb32fbb785b5830837e6061c415a6015), [`55d7560`](https://github.com/Effect-TS/effect/commit/55d75609b8acf8a1b54c1b1c7fbbb65ec741aa3e), [`f809189`](https://github.com/Effect-TS/effect/commit/f809189ddf6b6011ba43a9901baaa734e315da2a), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`0ebdbe7`](https://github.com/Effect-TS/effect/commit/0ebdbe74463dc84385956d0b1e8c2b79ebab5400), [`7517d09`](https://github.com/Effect-TS/effect/commit/7517d09f12a0b183a81bd425962c4e280a68b05d), [`212493b`](https://github.com/Effect-TS/effect/commit/212493b9a1eb98cd1ef6959c707a2e5784a5ae91), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`80ea8cb`](https://github.com/Effect-TS/effect/commit/80ea8cb9222ca73f564c8267ab2f82966fea027a), [`8df19f4`](https://github.com/Effect-TS/effect/commit/8df19f4fe81d90cc33ace88b9a77e5534f82d604)]:
- effect@4.0.0-beta.99
## 4.0.0-beta.98
### Patch Changes
- [#2608](https://github.com/Effect-TS/effect-smol/pull/2608) [`5f142a2`](https://github.com/Effect-TS/effect-smol/commit/5f142a2136e62274ec121273fecb5d248c19f159) Thanks @tim-smart! - Preserve autocomplete for known OpenAI-compatible model config properties while allowing provider-specific properties.
- [#2576](https://github.com/Effect-TS/effect-smol/pull/2576) [`0c2ed5e`](https://github.com/Effect-TS/effect-smol/commit/0c2ed5ee0ef5a437c5e6a2de456a5ddf214c281f) Thanks @tim-smart! - Normalize empty assistant message content to an empty string for OpenAI-compatible providers that reject null content values.
- Updated dependencies [[`989603b`](https://github.com/Effect-TS/effect-smol/commit/989603b60ab1197b64acf214208e0d370cd1f842), [`214c458`](https://github.com/Effect-TS/effect-smol/commit/214c458084bb6995d543cd37d1055f24be3d454e), [`a037273`](https://github.com/Effect-TS/effect-smol/commit/a0372736ac34796969b051bbba4717d7983f1ebe), [`97fdaa9`](https://github.com/Effect-TS/effect-smol/commit/97fdaa9c1f522c65e579365d314a07878e2b904f), [`b24d248`](https://github.com/Effect-TS/effect-smol/commit/b24d248c8df44222ce642087cde2bd859a2dc709), [`19c222c`](https://github.com/Effect-TS/effect-smol/commit/19c222cac2353a3d7b7733caecb00556fffe9a5c), [`eec85dd`](https://github.com/Effect-TS/effect-smol/commit/eec85ddba09ea326fd268ee33eeffd47e50d4671), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`8849052`](https://github.com/Effect-TS/effect-smol/commit/884905232d1e9a365e046d8dde27bf9c5707f57f), [`c15e16a`](https://github.com/Effect-TS/effect-smol/commit/c15e16ad130d1fbde25d912b7ac55995066cb35b), [`01d00a3`](https://github.com/Effect-TS/effect-smol/commit/01d00a3abfbf1f37996cdbe738ea5137c646cdd7), [`8bd4589`](https://github.com/Effect-TS/effect-smol/commit/8bd458975a1b3a8ed042eccf317b93d28ded91e7), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`6e08428`](https://github.com/Effect-TS/effect-smol/commit/6e08428d980501b856f846ad3f3f0e4ea46e7786), [`388dcf9`](https://github.com/Effect-TS/effect-smol/commit/388dcf953f65d317547f34d40e6443c5f264205f), [`2b7ce2b`](https://github.com/Effect-TS/effect-smol/commit/2b7ce2b513e7ec2a77822f1116dc6ffb6ba93f4e), [`87bea7e`](https://github.com/Effect-TS/effect-smol/commit/87bea7e16259246f3bcdf565446394751abca953), [`ce38dc3`](https://github.com/Effect-TS/effect-smol/commit/ce38dc33bda805a684432cca071f4dc3c6b9a1ba), [`a807cd1`](https://github.com/Effect-TS/effect-smol/commit/a807cd170341deca8a1cfb52c4222585f2431bb9), [`fd8a356`](https://github.com/Effect-TS/effect-smol/commit/fd8a356f06a8c9ce4e7e0a13fc4021c178ed31de), [`c2a5edc`](https://github.com/Effect-TS/effect-smol/commit/c2a5edc3abd31ad5bc123362bc1213e03e4095c3), [`5946da3`](https://github.com/Effect-TS/effect-smol/commit/5946da3804a1be5e752b05b96bd058cdba50a1bf), [`4ae0c5f`](https://github.com/Effect-TS/effect-smol/commit/4ae0c5ffcbe6c56ddfcb05c639112a079483539e), [`5b2a0bc`](https://github.com/Effect-TS/effect-smol/commit/5b2a0bceea3a28a33a58555210c90a415dc74a76), [`72ac585`](https://github.com/Effect-TS/effect-smol/commit/72ac585884befde6af9208da738699a93f1bae79), [`5e8c1b8`](https://github.com/Effect-TS/effect-smol/commit/5e8c1b82bfafa121311f987a49ab75395e3647a7), [`0f9c078`](https://github.com/Effect-TS/effect-smol/commit/0f9c07841b04183f485ee6e6458de73b290b09f5)]:
- effect@4.0.0-beta.98
## 4.0.0-beta.97
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.97
## 4.0.0-beta.96
### Patch Changes
- Updated dependencies [[`1503f45`](https://github.com/Effect-TS/effect-smol/commit/1503f45cb5bb2a74f4705252ec505a1f0ade7e62), [`57fe793`](https://github.com/Effect-TS/effect-smol/commit/57fe79316ffbc380b30626a168981fb26ae97459), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`97f29df`](https://github.com/Effect-TS/effect-smol/commit/97f29df457f7ffd07cfb4b379315c12c086af805)]:
- effect@4.0.0-beta.96
## 4.0.0-beta.95
### Patch Changes
- Updated dependencies [[`a482442`](https://github.com/Effect-TS/effect-smol/commit/a482442abdeb490e9652b854ec3495e4aa7273e7), [`fbefa85`](https://github.com/Effect-TS/effect-smol/commit/fbefa850fab2f0a302c20614496aeaaa2a8b5590), [`0b4a32f`](https://github.com/Effect-TS/effect-smol/commit/0b4a32f4260f0d8500942a133001b0d349328102), [`18a49e1`](https://github.com/Effect-TS/effect-smol/commit/18a49e1786679456258002ff9397faf02f678c2d), [`266cb90`](https://github.com/Effect-TS/effect-smol/commit/266cb90bb2c17aabc40563c32db334f09ba3d74b), [`912f095`](https://github.com/Effect-TS/effect-smol/commit/912f095a34572bbd3cedf6edb27878443e3e4a95), [`a6718f9`](https://github.com/Effect-TS/effect-smol/commit/a6718f9e00a15ca903b0732da46116cbf3d6aca7), [`bef5154`](https://github.com/Effect-TS/effect-smol/commit/bef51540a243aa2f872a00c01d0cd58b7a769baa), [`18e0564`](https://github.com/Effect-TS/effect-smol/commit/18e0564bd0f8ebbdfcaf1e2c21529948e9e4a81d), [`fb50f14`](https://github.com/Effect-TS/effect-smol/commit/fb50f14fc3657c1973785aa5b72ecf0b0d28e0b2)]:
- effect@4.0.0-beta.95
## 4.0.0-beta.94
### Patch Changes
- Updated dependencies [[`95a0e9b`](https://github.com/Effect-TS/effect-smol/commit/95a0e9bb62797af0e81c9998773405f248f218c5), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63), [`f11ce73`](https://github.com/Effect-TS/effect-smol/commit/f11ce73af60823754dc24194f4ffc561b9ea1c2d), [`ff30b6e`](https://github.com/Effect-TS/effect-smol/commit/ff30b6e7c2c63ffc56a4c5818d6d86b01b5ad528), [`1caab3c`](https://github.com/Effect-TS/effect-smol/commit/1caab3cc30f626efbf15e59d74f539a487e5c85c), [`aa80c47`](https://github.com/Effect-TS/effect-smol/commit/aa80c4775a04db87553e5568764cab7e32a72814), [`c2ae4fc`](https://github.com/Effect-TS/effect-smol/commit/c2ae4fce2f03a4cd1861c2b1179da7df656e662d), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63)]:
- effect@4.0.0-beta.94
## 4.0.0-beta.93
### Patch Changes
- Updated dependencies [[`00652fe`](https://github.com/Effect-TS/effect-smol/commit/00652fe95c18f87208e91343eb8bf218faa2f677), [`6c58167`](https://github.com/Effect-TS/effect-smol/commit/6c5816746eaf91d2a3c7c899c5720809fa230ae3), [`2bc5415`](https://github.com/Effect-TS/effect-smol/commit/2bc541501a7ef89e542d7cb98e96beb53cd205cc), [`e11cccc`](https://github.com/Effect-TS/effect-smol/commit/e11cccc7d5fe631abccc7d6e3bd296938de0fa2e), [`ba7e77e`](https://github.com/Effect-TS/effect-smol/commit/ba7e77e046b8641a3a4e9750bb88ca4a1d063d3f), [`5713ee7`](https://github.com/Effect-TS/effect-smol/commit/5713ee7edbc3054efde407b2286bbfd45bbc6e1c)]:
- effect@4.0.0-beta.93
## 4.0.0-beta.92
### Patch Changes
- Updated dependencies [[`affdc13`](https://github.com/Effect-TS/effect-smol/commit/affdc139045cc325dce321a84a580fdc1b2da7b9)]:
- effect@4.0.0-beta.92
## 4.0.0-beta.91
### Patch Changes
- Updated dependencies [[`b135b25`](https://github.com/Effect-TS/effect-smol/commit/b135b2517fca9e7839734ace3699a7dfa75b9075), [`aaa21a3`](https://github.com/Effect-TS/effect-smol/commit/aaa21a369a171c600db294f2a4f640583043e150), [`3475ee6`](https://github.com/Effect-TS/effect-smol/commit/3475ee6c2bda6b05c6d7a12ce30c8bb840b5b1a6)]:
- effect@4.0.0-beta.91
## 4.0.0-beta.90
### Patch Changes
- Updated dependencies [[`d237fdf`](https://github.com/Effect-TS/effect-smol/commit/d237fdf726481f76eb52a6196e111b24122bc3d5)]:
- effect@4.0.0-beta.90
## 4.0.0-beta.89
### Patch Changes
- Updated dependencies [[`b7d46ab`](https://github.com/Effect-TS/effect-smol/commit/b7d46ab7e1a29d8711817bab583c9febf48a0dad), [`7777e15`](https://github.com/Effect-TS/effect-smol/commit/7777e1540fd3680dd8346723cffec812b9384669), [`5376197`](https://github.com/Effect-TS/effect-smol/commit/5376197ca8e50358a41b1fd3cec27bd1ec680ec6)]:
- effect@4.0.0-beta.89
## 4.0.0-beta.88
### Patch Changes
- Updated dependencies [[`911f1b8`](https://github.com/Effect-TS/effect-smol/commit/911f1b84790ce42b3a70c95b33e6f6fd9e74de8b), [`8beeeea`](https://github.com/Effect-TS/effect-smol/commit/8beeeea52879d8613a39468848f01c3092bd54d4), [`c306fcf`](https://github.com/Effect-TS/effect-smol/commit/c306fcfeb1ef38455156932a1faf49292b1318da)]:
- effect@4.0.0-beta.88
## 4.0.0-beta.87
### Patch Changes
- Updated dependencies [[`5a0c1a4`](https://github.com/Effect-TS/effect-smol/commit/5a0c1a4faee5707b5cc35e646ff1ffdad70f1956), [`1eea2ea`](https://github.com/Effect-TS/effect-smol/commit/1eea2ea3795ba47316b82b1ac8d4612c0ba389ed)]:
- effect@4.0.0-beta.87
## 4.0.0-beta.86
### Patch Changes
- Updated dependencies [[`0b5795a`](https://github.com/Effect-TS/effect-smol/commit/0b5795a0ab4395e8f15955d8d96f2303084bfc64), [`3e3a859`](https://github.com/Effect-TS/effect-smol/commit/3e3a859ec6351a9e0d31674aabbd48fcefabb12e), [`7dbec24`](https://github.com/Effect-TS/effect-smol/commit/7dbec240dbf3bca599a20c486632abce694ef5ab), [`d8c00a1`](https://github.com/Effect-TS/effect-smol/commit/d8c00a171ac7141e8adc08c332d1162d9a9d56fc), [`85b6317`](https://github.com/Effect-TS/effect-smol/commit/85b631701e935866f2762bd595237aa718370cd9), [`6d0fda0`](https://github.com/Effect-TS/effect-smol/commit/6d0fda0d0cbdfffc523c89c57dfdb1608f84fb12), [`108a933`](https://github.com/Effect-TS/effect-smol/commit/108a9335ff8571928197e5847a09c28ac83d6f46), [`7e1f455`](https://github.com/Effect-TS/effect-smol/commit/7e1f455fab5005d769b939c91e519d450f802cf9), [`46b3e79`](https://github.com/Effect-TS/effect-smol/commit/46b3e79944cfdae7901eb148135c85b7eb39834e)]:
- effect@4.0.0-beta.86
## 4.0.0-beta.85
### Patch Changes
- Updated dependencies [[`328d97c`](https://github.com/Effect-TS/effect-smol/commit/328d97cc53c0dcb89077a5623e35b095eaa59a8c), [`8441836`](https://github.com/Effect-TS/effect-smol/commit/8441836e6dde70e8ae2126be9cefe9b45798b134), [`074e436`](https://github.com/Effect-TS/effect-smol/commit/074e4361091289104cb0ab6959dc3b0ea7794a6a), [`c1dfd60`](https://github.com/Effect-TS/effect-smol/commit/c1dfd60663eb13a58916f3712d877499943b628a), [`2ba316b`](https://github.com/Effect-TS/effect-smol/commit/2ba316bd15fcbf1c50626500d44a2c9b3bec19f5), [`7ce7344`](https://github.com/Effect-TS/effect-smol/commit/7ce7344c41056c79e2ee19ee6a9346c0f1d227c1)]:
- effect@4.0.0-beta.85
## 4.0.0-beta.84
### Patch Changes
- Updated dependencies [[`87f52ba`](https://github.com/Effect-TS/effect-smol/commit/87f52ba16c4370ffa3f84bf8e53038e1419c284e), [`b8ee07f`](https://github.com/Effect-TS/effect-smol/commit/b8ee07ffda8903b5ec2e45a786ddcba59f128fda), [`867c0d7`](https://github.com/Effect-TS/effect-smol/commit/867c0d70a09079b040260d45a1e92ff04dbfbf2f), [`b93bc6c`](https://github.com/Effect-TS/effect-smol/commit/b93bc6c9cb27b909a41d094c97c4f9d25bbc6d6b), [`57d387f`](https://github.com/Effect-TS/effect-smol/commit/57d387f92c30ab63e15e3e641f0a903b65886610), [`bacca41`](https://github.com/Effect-TS/effect-smol/commit/bacca4141c2400effae1eabfdb36c89a459cf246), [`0f8ac79`](https://github.com/Effect-TS/effect-smol/commit/0f8ac7959d29ed68c68ce25aabd6bf0cb7e63ecc), [`25b4482`](https://github.com/Effect-TS/effect-smol/commit/25b448270c01317703f25107e1480d4cd0246d9a), [`9cf3a25`](https://github.com/Effect-TS/effect-smol/commit/9cf3a25c66b0c44a52be9829870c44517ea52db2), [`8def767`](https://github.com/Effect-TS/effect-smol/commit/8def7674b1787f91035298cda4d122937e87ef72)]:
- effect@4.0.0-beta.84
## 4.0.0-beta.83
### Patch Changes
- Updated dependencies [[`1f2e8ce`](https://github.com/Effect-TS/effect-smol/commit/1f2e8ceef09e0a791c850ed2ade01f97089596f9)]:
- effect@4.0.0-beta.83
## 4.0.0-beta.82
### Patch Changes
- Updated dependencies [[`193690b`](https://github.com/Effect-TS/effect-smol/commit/193690b642ea802bbed40d663bd677251bbe9dc3)]:
- effect@4.0.0-beta.82
## 4.0.0-beta.81
### Patch Changes
- Updated dependencies [[`93cb4f8`](https://github.com/Effect-TS/effect-smol/commit/93cb4f8fbfb9e07cb9dc86ce6b155fd1f8167914), [`60341d9`](https://github.com/Effect-TS/effect-smol/commit/60341d9ca744d0473ce3fab621ca9bd225af3a39), [`1105ab5`](https://github.com/Effect-TS/effect-smol/commit/1105ab56cb724212f7ea7b431396ce82e8fd0484), [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36)]:
- effect@4.0.0-beta.81
## 4.0.0-beta.80
### Patch Changes
- [#2380](https://github.com/Effect-TS/effect-smol/pull/2380) [`b5f67a0`](https://github.com/Effect-TS/effect-smol/commit/b5f67a0ce9c110b98731ac485e5894df9da07e3a) Thanks @walln! - Surface reasoning tokens from OpenAI-compatible chat completion responses.
- Updated dependencies [[`d944330`](https://github.com/Effect-TS/effect-smol/commit/d94433090ee03f426d43e13b883abae4494e55e6), [`f48659f`](https://github.com/Effect-TS/effect-smol/commit/f48659fdcc84930ebc1e5b45b540c0f973389182), [`7652aaa`](https://github.com/Effect-TS/effect-smol/commit/7652aaa3bdbc39f241fe58b54b9a43b713e22e12), [`98630b7`](https://github.com/Effect-TS/effect-smol/commit/98630b7c8f679c352ba6796636c85688fa009d8d), [`90ae23c`](https://github.com/Effect-TS/effect-smol/commit/90ae23cf07284da5e1bcd9dffa882e85df7e617b)]:
- effect@4.0.0-beta.80
## 4.0.0-beta.79
### Patch Changes
- [#2338](https://github.com/Effect-TS/effect-smol/pull/2338) [`b12991f`](https://github.com/Effect-TS/effect-smol/commit/b12991f2346eda47fec72df5a89034394ad6c8fb) Thanks @boozedog! - Fix dropped streamed tool-call arguments when a provider sends `function.name: null` on continuation fragments.
OpenAI-compatible providers such as Fireworks send the tool name only on the first streamed `tool_calls` fragment and `function.name: null` on the continuation fragments that carry the argument deltas. `ChatCompletionToolFunctionDelta.name` was `Schema.optionalKey(Schema.String)` (non-nullable), so chunk validation rejected every continuation and silently discarded its argument delta, leaving the assembled tool call with empty or partial params. `name` is now nullable (`Schema.NullOr(Schema.String)`).
- Updated dependencies [[`b9704dc`](https://github.com/Effect-TS/effect-smol/commit/b9704dc9de9f1649ad502371014fe869b69a49a3), [`a207113`](https://github.com/Effect-TS/effect-smol/commit/a207113f66837bb54416926718a9a7d66774d079), [`5e9b9e2`](https://github.com/Effect-TS/effect-smol/commit/5e9b9e217b164ebfd4a002dd4380b3b1563200c3), [`7c128ae`](https://github.com/Effect-TS/effect-smol/commit/7c128aef458a1e2d224712e51c483c9badad1d44), [`0ada457`](https://github.com/Effect-TS/effect-smol/commit/0ada457c0513d8d908254ab77ebb7d29d2b523d6), [`d7cc5a2`](https://github.com/Effect-TS/effect-smol/commit/d7cc5a2bede3de10943aa0c6bdb4f26836a91efd), [`aad63be`](https://github.com/Effect-TS/effect-smol/commit/aad63becf65e0a6b076e94f8973be7bbe7fbd46f), [`09809f6`](https://github.com/Effect-TS/effect-smol/commit/09809f60f19ec98232f98b33e33e02ecb7e4fbd6), [`2fddda5`](https://github.com/Effect-TS/effect-smol/commit/2fddda5311929f46b61e503f0ade4fc749e8c77d), [`5f21768`](https://github.com/Effect-TS/effect-smol/commit/5f2176833399757c4500d8875b7f2fba0393de75), [`f27003e`](https://github.com/Effect-TS/effect-smol/commit/f27003e00524ff83f20dd9909f62b2f8795efe03)]:
- effect@4.0.0-beta.79
## 4.0.0-beta.78
### Patch Changes
- Updated dependencies [[`7836b8e`](https://github.com/Effect-TS/effect-smol/commit/7836b8eb8bb0f3e04cdf554ee070caccf74f00c1), [`35d49a3`](https://github.com/Effect-TS/effect-smol/commit/35d49a3a09bdba6b513de87ddcead9e61a1042ba), [`4093258`](https://github.com/Effect-TS/effect-smol/commit/40932580e65bafab5f23c5f14b520cb411d0b2cd)]:
- effect@4.0.0-beta.78
## 4.0.0-beta.77
### Patch Changes
- Updated dependencies [[`6e9a5ca`](https://github.com/Effect-TS/effect-smol/commit/6e9a5ca62a61156fd67b2518ad3ab14ac0d25f23), [`302f398`](https://github.com/Effect-TS/effect-smol/commit/302f3984ce206e35d86ddd99d3b72be144850a51)]:
- effect@4.0.0-beta.77
## 4.0.0-beta.76
### Patch Changes
- Updated dependencies [[`016108a`](https://github.com/Effect-TS/effect-smol/commit/016108a472af7048ddbbfd05f233e67529fafe12), [`95c03d2`](https://github.com/Effect-TS/effect-smol/commit/95c03d2c55930668c215b5a41c23cf7742fead84), [`07299a3`](https://github.com/Effect-TS/effect-smol/commit/07299a33c09fd52faa9810d30835a2622c752386)]:
- effect@4.0.0-beta.76
## 4.0.0-beta.75
### Patch Changes
- Updated dependencies [[`81b187c`](https://github.com/Effect-TS/effect-smol/commit/81b187c17a0d8817b58232826939154010ae49d7), [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e), [`a29c2e7`](https://github.com/Effect-TS/effect-smol/commit/a29c2e7e3570920156702671d6f3367cd0195f6c), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`ffea4ec`](https://github.com/Effect-TS/effect-smol/commit/ffea4ecf2925f6a4c9fd13079d47584cbf2bed00), [`4255c9b`](https://github.com/Effect-TS/effect-smol/commit/4255c9ba78bb98c7838fbe9dccdd8465e9da5427)]:
- effect@4.0.0-beta.75
## 4.0.0-beta.74
### Patch Changes
- Updated dependencies [[`b1fc6a4`](https://github.com/Effect-TS/effect-smol/commit/b1fc6a4b4d0ca7fa9fd162799ae17c86f2f7ee8e)]:
- effect@4.0.0-beta.74
## 4.0.0-beta.73
### Patch Changes
- Updated dependencies [[`361ca30`](https://github.com/Effect-TS/effect-smol/commit/361ca30eb6e134feece547d6e00f82be4cb23f75), [`b9598c6`](https://github.com/Effect-TS/effect-smol/commit/b9598c6a209e75bfdb87ee3b024ecd1e3923ff6e)]:
- effect@4.0.0-beta.73
## 4.0.0-beta.72
### Patch Changes
- Updated dependencies [[`73e67d1`](https://github.com/Effect-TS/effect-smol/commit/73e67d119a84d697773eaecb4865c6a71eb1a9cb), [`01d71ec`](https://github.com/Effect-TS/effect-smol/commit/01d71ec5a75f3c2747a8d3b1ad9701d1e27b7ce5), [`fcd707e`](https://github.com/Effect-TS/effect-smol/commit/fcd707e091a16e1b35343c901cc4052274e32239)]:
- effect@4.0.0-beta.72
## 4.0.0-beta.71
### Patch Changes
- Updated dependencies [[`d8ac76b`](https://github.com/Effect-TS/effect-smol/commit/d8ac76b5bad458c42cebe8a0c1b3843f955ac293), [`2c3c00a`](https://github.com/Effect-TS/effect-smol/commit/2c3c00af6faba7b7d422af26a7a2bbc35636d230), [`3751e7c`](https://github.com/Effect-TS/effect-smol/commit/3751e7cf353e7a54cd692c37401207d9afba1e63), [`fc5f25b`](https://github.com/Effect-TS/effect-smol/commit/fc5f25b03ada5fc2431987768a74d3d3e75ca485), [`7ccced4`](https://github.com/Effect-TS/effect-smol/commit/7ccced42867c14c013b01160b3d292f14c05bd04), [`a2e1fe5`](https://github.com/Effect-TS/effect-smol/commit/a2e1fe5835c98c8ee4393a091b1d11b75126e349), [`4a4a36b`](https://github.com/Effect-TS/effect-smol/commit/4a4a36b10e6e616cad07584a43908f6a7e07e618), [`d350292`](https://github.com/Effect-TS/effect-smol/commit/d3502922b4740fa9d745797cbc3775cb67839b6d), [`730afb6`](https://github.com/Effect-TS/effect-smol/commit/730afb66696adf9bd5a328cbca29df9c05968771), [`df1b008`](https://github.com/Effect-TS/effect-smol/commit/df1b008f370f414c2a67a7b8139ef747af8e5fba), [`6d469d5`](https://github.com/Effect-TS/effect-smol/commit/6d469d567a7c41d7e5343bdee21d45b07b0e8190)]:
- effect@4.0.0-beta.71
## 4.0.0-beta.70
### Patch Changes
- Updated dependencies [[`af7782d`](https://github.com/Effect-TS/effect-smol/commit/af7782d3008d08b043f3a3f261516001514b2b4e), [`7212d70`](https://github.com/Effect-TS/effect-smol/commit/7212d701a3eee7b3553ff502e2c066126e52e839)]:
- effect@4.0.0-beta.70
## 4.0.0-beta.69
### Patch Changes
- Updated dependencies [[`70ea04a`](https://github.com/Effect-TS/effect-smol/commit/70ea04aa96a2a7859d738d414e1f0e3ed081a27a), [`d0ea8b0`](https://github.com/Effect-TS/effect-smol/commit/d0ea8b03f7d73ae076c1db12666141e480d11178), [`a57674b`](https://github.com/Effect-TS/effect-smol/commit/a57674b64845e9e75a456cf907bfdcb858859118), [`59aa334`](https://github.com/Effect-TS/effect-smol/commit/59aa334fbd0a504dda3c36f6d2ef1be7449b4b8b), [`8f4208e`](https://github.com/Effect-TS/effect-smol/commit/8f4208ee83bc7bdaa6793b5429847b45aab72470)]:
- effect@4.0.0-beta.69
## 4.0.0-beta.68
### Patch Changes
- Updated dependencies [[`af8267f`](https://github.com/Effect-TS/effect-smol/commit/af8267f2f3588c3fb611e9286f6f933f29ce1217), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`f136bb7`](https://github.com/Effect-TS/effect-smol/commit/f136bb763048cbc6b17edd26496dba3e2415b9fa), [`6f38f07`](https://github.com/Effect-TS/effect-smol/commit/6f38f07d5941a211b251383aaab0f4f55e8a6557), [`aec9c40`](https://github.com/Effect-TS/effect-smol/commit/aec9c401a53db227f18bf5e0c84db7130ad862d6)]:
- effect@4.0.0-beta.68
## 4.0.0-beta.67
### Patch Changes
- Updated dependencies [[`a42ef66`](https://github.com/Effect-TS/effect-smol/commit/a42ef6632abbddfa820995ae310ccc84ae8d9b6f), [`35594f8`](https://github.com/Effect-TS/effect-smol/commit/35594f811cafe471acd490114b103a1f8392c8d8), [`8bddd62`](https://github.com/Effect-TS/effect-smol/commit/8bddd628cb623f9533d345082583ff51cead6836), [`4be4c8d`](https://github.com/Effect-TS/effect-smol/commit/4be4c8d60862aa963869ee2ed9ffa048ffac0527), [`0c9d3ab`](https://github.com/Effect-TS/effect-smol/commit/0c9d3ab43eb721a370ed8306260cbac218c27e87), [`b156acc`](https://github.com/Effect-TS/effect-smol/commit/b156accd2691b4a051f823affdece7c39923ce85), [`d16c034`](https://github.com/Effect-TS/effect-smol/commit/d16c03434ee3e6dcd3bfc82b65d99e881d89025b), [`b559d68`](https://github.com/Effect-TS/effect-smol/commit/b559d68845f848a10153395778f035682d399075), [`a3de5d9`](https://github.com/Effect-TS/effect-smol/commit/a3de5d9215e5cc4a62e2666efbd7c1bf595eb84f), [`7e6c12e`](https://github.com/Effect-TS/effect-smol/commit/7e6c12ec9b3a5945f6c26e272cc8f6390541ad3e), [`098167a`](https://github.com/Effect-TS/effect-smol/commit/098167a220fe07da6f14455818733ab1b269c9dd)]:
- effect@4.0.0-beta.67
## 4.0.0-beta.66
### Patch Changes
- Updated dependencies [[`ca2498e`](https://github.com/Effect-TS/effect-smol/commit/ca2498e702ac2d83fb7187707b7eb069bdb261a2), [`cd7d1fb`](https://github.com/Effect-TS/effect-smol/commit/cd7d1fba7e2e2c5ac3ad64e1be433440a5bda436), [`19a7033`](https://github.com/Effect-TS/effect-smol/commit/19a703367ec817cffc41d152da9b594827408e2b), [`33d26b4`](https://github.com/Effect-TS/effect-smol/commit/33d26b4210b2e974f146a71e7eed962f8ce00900), [`856766b`](https://github.com/Effect-TS/effect-smol/commit/856766b2c506aaed6d2df1d63bf3a5b1b062e1d4), [`079c7df`](https://github.com/Effect-TS/effect-smol/commit/079c7df82559bb9ce10a86dffb85d25e6ce07dc3)]:
- effect@4.0.0-beta.66
## 4.0.0-beta.65
### Patch Changes
- Updated dependencies [[`6f11454`](https://github.com/Effect-TS/effect-smol/commit/6f11454a9b6c3bd00f6b35fd7af14a2f2d63a0a2)]:
- effect@4.0.0-beta.65
## 4.0.0-beta.64
### Patch Changes
- Updated dependencies [[`7d4877a`](https://github.com/Effect-TS/effect-smol/commit/7d4877a1929cdb690280ea254326c04f2ec97ea5)]:
- effect@4.0.0-beta.64
## 4.0.0-beta.63
### Patch Changes
- [#2133](https://github.com/Effect-TS/effect-smol/pull/2133) [`5be0aaa`](https://github.com/Effect-TS/effect-smol/commit/5be0aaad694c9eb943a710eb4f896bc4c3fcae99) Thanks @Zelys-DFKH! - Fix `OpenAiLanguageModel` leaking library-only config fields (`fileIdPrefixes`, `strictJsonSchema`) into request body, causing OpenAI 400 errors.
- Updated dependencies [[`7f927ff`](https://github.com/Effect-TS/effect-smol/commit/7f927ffb7a9801dcfc4096c29e369d13d65cd0ac), [`a696b3e`](https://github.com/Effect-TS/effect-smol/commit/a696b3e83a8504cdbe261a18c10a1cc0619ae102)]:
- effect@4.0.0-beta.63
## 4.0.0-beta.62
### Patch Changes
- Updated dependencies [[`4ab4b90`](https://github.com/Effect-TS/effect-smol/commit/4ab4b9007dc27a52ffabc6fcb37c96eeec795bf7)]:
- effect@4.0.0-beta.62
## 4.0.0-beta.61
### Patch Changes
- Updated dependencies [[`50790af`](https://github.com/Effect-TS/effect-smol/commit/50790af9b190c38d10fb0723837d49b66432638f), [`71f7c3d`](https://github.com/Effect-TS/effect-smol/commit/71f7c3df997deda92c84146d569696dab3bd645c), [`aae8797`](https://github.com/Effect-TS/effect-smol/commit/aae8797b9cb383be0c182dd58d03d787c354238b)]:
- effect@4.0.0-beta.61
## 4.0.0-beta.60
### Patch Changes
- Updated dependencies [[`f69d567`](https://github.com/Effect-TS/effect-smol/commit/f69d5675dcff9f4137295752baf066b7153fdc09), [`7909c95`](https://github.com/Effect-TS/effect-smol/commit/7909c954b8f6244a35a4b429f8dd0dff45dad620), [`bbb4dcc`](https://github.com/Effect-TS/effect-smol/commit/bbb4dcc6c406b83a416b4ad3541cc02037c420e4), [`7af2207`](https://github.com/Effect-TS/effect-smol/commit/7af2207901eabf3132c1b7010a69b3899c06fbbe), [`848b40a`](https://github.com/Effect-TS/effect-smol/commit/848b40a4bd4bf54a5098617d50c33c88eee8270a)]:
- effect@4.0.0-beta.60
## 4.0.0-beta.59
### Patch Changes
- Updated dependencies [[`56837ea`](https://github.com/Effect-TS/effect-smol/commit/56837ea2a338395b35550641374e9e589bd8b71d)]:
- effect@4.0.0-beta.59
## 4.0.0-beta.58
### Patch Changes
- Updated dependencies [[`11993d4`](https://github.com/Effect-TS/effect-smol/commit/11993d4934c66f5dc611b8bbf553f01d501ef8f7), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec)]:
- effect@4.0.0-beta.58
## 4.0.0-beta.57
### Patch Changes
- Updated dependencies [[`a971f5c`](https://github.com/Effect-TS/effect-smol/commit/a971f5cbd92dfe4274420bf0966595eb35531060), [`8e110c5`](https://github.com/Effect-TS/effect-smol/commit/8e110c5f02a429ccc43a91df8678e402138c0851)]:
- effect@4.0.0-beta.57
## 4.0.0-beta.56
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.56
## 4.0.0-beta.55
### Patch Changes
- Updated dependencies [[`42cc744`](https://github.com/Effect-TS/effect-smol/commit/42cc744570968deb365fb46d47b53d3277050c93), [`04855ce`](https://github.com/Effect-TS/effect-smol/commit/04855ceeca4d40c55a5750dd9893b691f8ea741a)]:
- effect@4.0.0-beta.55
## 4.0.0-beta.54
### Patch Changes
- Updated dependencies [[`e4b74f9`](https://github.com/Effect-TS/effect-smol/commit/e4b74f9c01a0e9b6cd58416de4af3a26d51da7c8), [`4c72808`](https://github.com/Effect-TS/effect-smol/commit/4c728081851c66dacf889a816535671bc841ae96)]:
- effect@4.0.0-beta.54
## 4.0.0-beta.53
### Patch Changes
- Updated dependencies [[`0768509`](https://github.com/Effect-TS/effect-smol/commit/07685094e931af07d104165195826a535b55fa7e), [`476aede`](https://github.com/Effect-TS/effect-smol/commit/476aede69c6efa06b5781ca5eb3e3b128ca29141), [`4f79c54`](https://github.com/Effect-TS/effect-smol/commit/4f79c542e7b508c235ff485d862cc8b29a8260c5), [`4be6a7c`](https://github.com/Effect-TS/effect-smol/commit/4be6a7cf35dab2a01d652f56dd35f0358c5a7e88), [`88927eb`](https://github.com/Effect-TS/effect-smol/commit/88927ebb896162cdba103b36553280b58e0facac)]:
- effect@4.0.0-beta.53
## 4.0.0-beta.52
### Patch Changes
- Updated dependencies [[`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`cf3a311`](https://github.com/Effect-TS/effect-smol/commit/cf3a311d863a8abb818840c3b80f847e621c43c1), [`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`131fdd5`](https://github.com/Effect-TS/effect-smol/commit/131fdd5b1f26531e265fe1a08f002002f47c276e)]:
- effect@4.0.0-beta.52
## 4.0.0-beta.51
### Patch Changes
- Updated dependencies [[`778d2af`](https://github.com/Effect-TS/effect-smol/commit/778d2afe9b5154bc1f9abae46d93ea7e54c87344), [`4e24dcf`](https://github.com/Effect-TS/effect-smol/commit/4e24dcf75037f65eebc1eb68623bc7cbf9d5512a), [`4b1c015`](https://github.com/Effect-TS/effect-smol/commit/4b1c0150e9bdb5559ed32d250deb66e17b4240c7), [`454f8ad`](https://github.com/Effect-TS/effect-smol/commit/454f8adad822929c3ef60f8280d0987226b049fd), [`6754a0c`](https://github.com/Effect-TS/effect-smol/commit/6754a0cd18626b06805a079cc5265525a5eb7d27), [`90f7fd5`](https://github.com/Effect-TS/effect-smol/commit/90f7fd5243871b30980964135db4512b8119fa82), [`d7e1519`](https://github.com/Effect-TS/effect-smol/commit/d7e151974934201fd93fa4c8a1192ee9a5d965a0), [`72a8122`](https://github.com/Effect-TS/effect-smol/commit/72a81228e09782bae512f7d041bbfbc78bc668d0)]:
- effect@4.0.0-beta.51
## 4.0.0-beta.50
### Patch Changes
- Updated dependencies [[`07be594`](https://github.com/Effect-TS/effect-smol/commit/07be594825de60f8e1b2102d21dbb9b8fc63b414), [`ae02433`](https://github.com/Effect-TS/effect-smol/commit/ae02433103ce28f53a0c9bfb4a44e75773289b7b)]:
- effect@4.0.0-beta.50
## 4.0.0-beta.49
### Patch Changes
- Updated dependencies [[`7d87873`](https://github.com/Effect-TS/effect-smol/commit/7d8787340ff549370f6f2a88b612e9ebbfd6ba45), [`c2f6f90`](https://github.com/Effect-TS/effect-smol/commit/c2f6f901b200a6e515b4f02c93ce8005b7bbf1c5), [`216f13c`](https://github.com/Effect-TS/effect-smol/commit/216f13c1fce454a21b489bb915714a17e791a1ac)]:
- effect@4.0.0-beta.49
## 4.0.0-beta.48
### Patch Changes
- Updated dependencies [[`4da56ec`](https://github.com/Effect-TS/effect-smol/commit/4da56ecff129b2da40137ffede23a73cc4e532d8), [`a5e6f77`](https://github.com/Effect-TS/effect-smol/commit/a5e6f774bab195cf50ecdc818240765f69a3bf4a), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070)]:
- effect@4.0.0-beta.48
## 4.0.0-beta.47
### Patch Changes
- Updated dependencies [[`c584726`](https://github.com/Effect-TS/effect-smol/commit/c58472674e750e6938df955044eab88feda95e45), [`86a91a4`](https://github.com/Effect-TS/effect-smol/commit/86a91a4f0c59286dfa9393232d8020dea70ed4db), [`131caf9`](https://github.com/Effect-TS/effect-smol/commit/131caf9525151a0cb29803a8f1dffa0f4f479d12), [`c3615c8`](https://github.com/Effect-TS/effect-smol/commit/c3615c88379b9daf252df0db72c6ac5a20326406)]:
- effect@4.0.0-beta.47
## 4.0.0-beta.46
### Patch Changes
- Updated dependencies [[`3a30b9e`](https://github.com/Effect-TS/effect-smol/commit/3a30b9e2ec2bd8b8193e1aa139f6878a07e3f5ee)]:
- effect@4.0.0-beta.46
## 4.0.0-beta.45
### Patch Changes
- Updated dependencies [[`5c3af6d`](https://github.com/Effect-TS/effect-smol/commit/5c3af6d554f60be34f8fc21d598d9a298ae11beb)]:
- effect@4.0.0-beta.45
## 4.0.0-beta.44
### Patch Changes
- [#1961](https://github.com/Effect-TS/effect-smol/pull/1961) [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970) Thanks @IMax153! - Rename the `ServiceMap` module to `Context` across exports, docs, and tests.
- Updated dependencies [[`e3f0621`](https://github.com/Effect-TS/effect-smol/commit/e3f0621454c3f5d11070d30619da27c9232cadc1), [`5b476ab`](https://github.com/Effect-TS/effect-smol/commit/5b476abc0bd7e9bb59135ea1bcad2e4936227ced), [`6b40e5a`](https://github.com/Effect-TS/effect-smol/commit/6b40e5a4a6bd2087c15a3d7374d25057fdedfa16), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`3b09fb3`](https://github.com/Effect-TS/effect-smol/commit/3b09fb31c40c2802b01f21c23bcdd1fe7fb0aa82), [`2370410`](https://github.com/Effect-TS/effect-smol/commit/237041062e5af4594d32db91597e34e70a632877), [`dabc272`](https://github.com/Effect-TS/effect-smol/commit/dabc272444a700eb629c07ba3e77671a841ca86e), [`08b63c3`](https://github.com/Effect-TS/effect-smol/commit/08b63c3df11bd35c9fd6090dbd166287fdc40664), [`dfff04c`](https://github.com/Effect-TS/effect-smol/commit/dfff04c4c2b1d352dfad83992a6dce1280c85cf9), [`9baed9e`](https://github.com/Effect-TS/effect-smol/commit/9baed9e17e84702e6e480fcef6f86404f9e24be9), [`7846792`](https://github.com/Effect-TS/effect-smol/commit/7846792adc7e1631d62d26d657bd7ba6139f369b), [`1556a24`](https://github.com/Effect-TS/effect-smol/commit/1556a247623636b7ebe438fb56d77f1a7bf957bb), [`7c11bc2`](https://github.com/Effect-TS/effect-smol/commit/7c11bc292ab8e46252fe8f7576fb685917bfb8b5), [`b5ea591`](https://github.com/Effect-TS/effect-smol/commit/b5ea5913ec1d45d0dd12a327b9dd966bda2f6d02), [`0853afa`](https://github.com/Effect-TS/effect-smol/commit/0853afaeb1633b2d7f8b66893bd01c3aa1ef2c22), [`ac845f3`](https://github.com/Effect-TS/effect-smol/commit/ac845f3ab40e0b8719576e7f9bc16ea2e0e02cd4), [`b80c462`](https://github.com/Effect-TS/effect-smol/commit/b80c46247480f47bb64fc480fab48a3f37bc8888), [`b3f535d`](https://github.com/Effect-TS/effect-smol/commit/b3f535d9a7ac13b5fb984c29f93561c57a081ff0), [`6fe2e93`](https://github.com/Effect-TS/effect-smol/commit/6fe2e93cc2f1b173ef89651d74b6a5d2626b3226), [`cda8004`](https://github.com/Effect-TS/effect-smol/commit/cda800451c1ffbdddfc08415aed7b2d91e0412ee), [`8335477`](https://github.com/Effect-TS/effect-smol/commit/8335477a8a936a24b5f3ee6203c1b268bd1bfc3c), [`8c836f9`](https://github.com/Effect-TS/effect-smol/commit/8c836f99ab1e896b9580a71d67773625baff2eaf), [`718ff6f`](https://github.com/Effect-TS/effect-smol/commit/718ff6fe3e3d3820cefd67d2bff1b2224fe08060), [`7eed84f`](https://github.com/Effect-TS/effect-smol/commit/7eed84fc33c5781a6fb11bf4fd189d424902ebd4), [`5df46fe`](https://github.com/Effect-TS/effect-smol/commit/5df46fe2f654d59ab5fc1578f4fc27fa40368ef9), [`82dd0f2`](https://github.com/Effect-TS/effect-smol/commit/82dd0f26c6442b07143762ef7bc33742d3978dd6), [`03ae41e`](https://github.com/Effect-TS/effect-smol/commit/03ae41e7304cffac9f18feea22b73468feafc43a), [`4677a0a`](https://github.com/Effect-TS/effect-smol/commit/4677a0a58f95eea38a211efcd3f345f237a9e44a), [`87e1fc8`](https://github.com/Effect-TS/effect-smol/commit/87e1fc8b67e4901d75f567b2fecc3841ab762cc4), [`c1af1b7`](https://github.com/Effect-TS/effect-smol/commit/c1af1b756f63291e9c0298cf95c98a6920a0c2a0), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`c8a877b`](https://github.com/Effect-TS/effect-smol/commit/c8a877b53e8f29616335719e5dd1c3992dddf780), [`7da961a`](https://github.com/Effect-TS/effect-smol/commit/7da961ae4916229d2246699a5d3b20e5b2dd2020)]:
- effect@4.0.0-beta.44
## 4.0.0-beta.43
### Patch Changes
- Updated dependencies [[`2ae33d0`](https://github.com/Effect-TS/effect-smol/commit/2ae33d050914915f7cb9c25ab0a020901e08d596), [`979811a`](https://github.com/Effect-TS/effect-smol/commit/979811a4c3f7ed21ed18ef560c49fb7f5569e80e), [`eb7dbef`](https://github.com/Effect-TS/effect-smol/commit/eb7dbeffa883386ad912815e62c0820cac1fdf8e), [`cf50eb4`](https://github.com/Effect-TS/effect-smol/commit/cf50eb49cb04706dae5185f624708117c413dee8), [`1d046fe`](https://github.com/Effect-TS/effect-smol/commit/1d046fe484560e23f3e22cb23eec6433f8f1fa02)]:
- effect@4.0.0-beta.43
## 4.0.0-beta.42
### Patch Changes
- [#1859](https://github.com/Effect-TS/effect-smol/pull/1859) [`a4809db`](https://github.com/Effect-TS/effect-smol/commit/a4809db80f65601d37ccafc13d773e55909c4e98) Thanks @lloydrichards! - add dynamic tooling for openai and openai-compact language models
- Updated dependencies [[`924e216`](https://github.com/Effect-TS/effect-smol/commit/924e216caa7e0bbf22e994a0cd2ce8b1f0f0b3ee), [`80e7f0c`](https://github.com/Effect-TS/effect-smol/commit/80e7f0cd9116e811e97b0ce30a77a8d1ecd072aa), [`f8328bf`](https://github.com/Effect-TS/effect-smol/commit/f8328bf0314da3dc7f31d314f94a5840e8d5217f), [`66d1c06`](https://github.com/Effect-TS/effect-smol/commit/66d1c06039079129707a230f7ad8c676439d7133), [`bee800b`](https://github.com/Effect-TS/effect-smol/commit/bee800bf285192a01bec72a7b7b51bc1159434e6), [`8930441`](https://github.com/Effect-TS/effect-smol/commit/8930441dee6f94c59c583d18d3ebd677cf1f2623)]:
- effect@4.0.0-beta.42
## 4.0.0-beta.41
### Patch Changes
- Updated dependencies [[`36f5c21`](https://github.com/Effect-TS/effect-smol/commit/36f5c2174d31ab42c4598bf81f178f40d0802283), [`d8ce758`](https://github.com/Effect-TS/effect-smol/commit/d8ce758669d6297ae932ac3251d83e7b49b22f30), [`11aab4c`](https://github.com/Effect-TS/effect-smol/commit/11aab4c6d37d5691adafc2d33da1a631b28ce814), [`3bc1efb`](https://github.com/Effect-TS/effect-smol/commit/3bc1efb53dd75b4a40de46f1f80c7f8a7d50af86), [`70e724e`](https://github.com/Effect-TS/effect-smol/commit/70e724e604604d4be1061cd8da0d360494998c84), [`738dee7`](https://github.com/Effect-TS/effect-smol/commit/738dee7edfd70af82dc4d2376db3a8ebe603eb48), [`2111963`](https://github.com/Effect-TS/effect-smol/commit/2111963f19b4c28c800664a8fac9590c1321885f), [`198a553`](https://github.com/Effect-TS/effect-smol/commit/198a553d9ce45f6a00bfc4d65ed0640669602d95)]:
- effect@4.0.0-beta.41
## 4.0.0-beta.40
### Patch Changes
- Updated dependencies [[`f62860f`](https://github.com/Effect-TS/effect-smol/commit/f62860f0e5e45978fabf7256ae620a13152a772a), [`973f281`](https://github.com/Effect-TS/effect-smol/commit/973f2812529aadc1cc54598b2039799fa72b80f8)]:
- effect@4.0.0-beta.40
## 4.0.0-beta.39
### Patch Changes
- Updated dependencies [[`f91fd3d`](https://github.com/Effect-TS/effect-smol/commit/f91fd3db39fe5628439fd175fba201a65a1aa9d0), [`edaae9d`](https://github.com/Effect-TS/effect-smol/commit/edaae9d65f464f941d7eddd723cd33d324f4b071), [`b47db0b`](https://github.com/Effect-TS/effect-smol/commit/b47db0bd5802064b6a24b3ea27c6ff2e0520d513), [`82d3c8e`](https://github.com/Effect-TS/effect-smol/commit/82d3c8e4f3f49b00df611b25aa6f8f74ec21b59b), [`7c22b31`](https://github.com/Effect-TS/effect-smol/commit/7c22b315d198dcbf44ae8cdb8b37879e1c9e3996)]:
- effect@4.0.0-beta.39
## 4.0.0-beta.38
### Patch Changes
- Updated dependencies [[`f4dbe5b`](https://github.com/Effect-TS/effect-smol/commit/f4dbe5b26b9c2d33fae024bf44afbdf8541792cd), [`a71a607`](https://github.com/Effect-TS/effect-smol/commit/a71a607c89fb6669a12a562c2c23be81dfbe1adb), [`66a0494`](https://github.com/Effect-TS/effect-smol/commit/66a0494ed75cd12f2721dcbb1d8a072e3d9e14b6), [`5ef7218`](https://github.com/Effect-TS/effect-smol/commit/5ef7218fc559d57301fe929b8a0cab4033f4f1fd), [`472d260`](https://github.com/Effect-TS/effect-smol/commit/472d260655bc311fba5c2c6e23bb77d8f7e36ba0)]:
- effect@4.0.0-beta.38
## 4.0.0-beta.37
### Patch Changes
- Updated dependencies [[`f7a0b71`](https://github.com/Effect-TS/effect-smol/commit/f7a0b711da8fdd645597dee29cacc5619c6afcf2), [`1e223c3`](https://github.com/Effect-TS/effect-smol/commit/1e223c30ccf835dfbb21284535d78549efaeca80), [`53740f4`](https://github.com/Effect-TS/effect-smol/commit/53740f47aa76d114b7d535649fb50efc54a09608), [`8c7cf89`](https://github.com/Effect-TS/effect-smol/commit/8c7cf89f719e580cbce1bf6c24e6996f1992a0a6), [`b6b81a9`](https://github.com/Effect-TS/effect-smol/commit/b6b81a940eaafcbc792d25413d6c02c707de31b2), [`8f4c1f9`](https://github.com/Effect-TS/effect-smol/commit/8f4c1f97ed60f8810b0b327b50117ffb2d8260d4), [`f2479f9`](https://github.com/Effect-TS/effect-smol/commit/f2479f9d3113b1f012db17a3852b4e28f478cf9c), [`c919921`](https://github.com/Effect-TS/effect-smol/commit/c9199217fad65529421d2cf95ecfff41257090fd), [`7af90c2`](https://github.com/Effect-TS/effect-smol/commit/7af90c2e3c99038eafa39650433839523790e2fe), [`f3be185`](https://github.com/Effect-TS/effect-smol/commit/f3be18569e5ca57c25eabf00df3ca601ebab43c7)]:
- effect@4.0.0-beta.37
## 4.0.0-beta.36
### Patch Changes
- Updated dependencies [[`60fcbcc`](https://github.com/Effect-TS/effect-smol/commit/60fcbcc43d09471e8f7e0969955d99dcefc5be81), [`0a60837`](https://github.com/Effect-TS/effect-smol/commit/0a6083713124440e630030375bab367e8d7df24e), [`49164d2`](https://github.com/Effect-TS/effect-smol/commit/49164d2c20a8d21b66514992c4a15d8521f6b36e), [`334b6e4`](https://github.com/Effect-TS/effect-smol/commit/334b6e4f76fe11941b516d61f57e268bc31f0ca6), [`5700695`](https://github.com/Effect-TS/effect-smol/commit/5700695f76ae6da6b94c9c87d4dd2b8054fb829b), [`f8f4456`](https://github.com/Effect-TS/effect-smol/commit/f8f445644f3aa7ec093cab7445198a62ba18a480), [`969d24f`](https://github.com/Effect-TS/effect-smol/commit/969d24fdfa48c4838e811983848d9cb4e9b3b12c), [`851eda0`](https://github.com/Effect-TS/effect-smol/commit/851eda0533946e39bacaaf581896320d7a4f3e8c), [`8059c1c`](https://github.com/Effect-TS/effect-smol/commit/8059c1c3eba9a90af7cd889ea261bcb8fff0c185), [`6f83295`](https://github.com/Effect-TS/effect-smol/commit/6f8329546a73eaddc7cb5e85ea8e37e73fbfb611), [`65f7f57`](https://github.com/Effect-TS/effect-smol/commit/65f7f5737575fed668987462c96d29a446707c32), [`e7fabd2`](https://github.com/Effect-TS/effect-smol/commit/e7fabd2265db690eae5cfc9b83730c84699aef61), [`89c3e98`](https://github.com/Effect-TS/effect-smol/commit/89c3e985401eb38f33a3ae21a94ad27de3c1d28b), [`53794ab`](https://github.com/Effect-TS/effect-smol/commit/53794ab7af30aa5c5004ecf53659fafbe4b10542)]:
- effect@4.0.0-beta.36
## 4.0.0-beta.35
### Patch Changes
- Updated dependencies [[`9252b43`](https://github.com/Effect-TS/effect-smol/commit/9252b43560f507709c2985abcf52a7837b23ddf8), [`7daf387`](https://github.com/Effect-TS/effect-smol/commit/7daf3870a656882a488a60f67881e6808c8f4d04), [`e1664a3`](https://github.com/Effect-TS/effect-smol/commit/e1664a38bc31ef4ceb4e9324c7226e1e99bf9c07), [`fdaa6e0`](https://github.com/Effect-TS/effect-smol/commit/fdaa6e0a41b6b6605438fa8557441792135380a2), [`19aa47e`](https://github.com/Effect-TS/effect-smol/commit/19aa47ef7b470e427620edca8970dd9cdd551216), [`c667dad`](https://github.com/Effect-TS/effect-smol/commit/c667dad07777b860e4764a3ba9a6cc41c236cd98), [`764d150`](https://github.com/Effect-TS/effect-smol/commit/764d1501bc5026b60fc8aef6cb02a5a87c762801), [`3c27098`](https://github.com/Effect-TS/effect-smol/commit/3c27098b5685a63db2c2eff654a250c94d3fcfa7)]:
- effect@4.0.0-beta.35
## 4.0.0-beta.34
### Patch Changes
- [#1764](https://github.com/Effect-TS/effect-smol/pull/1764) [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f) Thanks @tim-smart! - Add unstable EmbeddingModel support across core and OpenAI providers.
- Add the unstable EmbeddingModel module API surface in `effect`, including service, request, response, and provider types.
- Implement the unstable EmbeddingModel runtime constructor in `effect`, with `RequestResolver` batching, `embed` / `embedMany` spans, provider error propagation, deterministic ordering, and empty-input `embedMany` fast-path behavior.
- Add and align EmbeddingModel behavior tests in `effect` for embedding usage, batching, ordering, and error handling.
- Add `OpenAiEmbeddingModel` in `@effect/ai-openai`, including model / make / layer constructors, config overrides, and provider output index validation with deterministic reordering.
- Add OpenAI-compatible EmbeddingModel provider support in `@effect/ai-openai-compat`, including config overrides, layer constructors, and output index validation.
- [#1771](https://github.com/Effect-TS/effect-smol/pull/1771) [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e) Thanks @tim-smart! - Add `EmbeddingModel.ModelDimensions` and require dimensions in embedding provider `model` constructors.
- Updated dependencies [[`f2f75ee`](https://github.com/Effect-TS/effect-smol/commit/f2f75ee564bce1cd95f5189c7bdeeed4f92dacb1), [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f), [`5d704ee`](https://github.com/Effect-TS/effect-smol/commit/5d704ee10d20e8eb107e34bb8a21feb5aa4a7685), [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e), [`58217d3`](https://github.com/Effect-TS/effect-smol/commit/58217d318a7d716ccd707cce0f41573946939c28), [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2), [`e3b44b6`](https://github.com/Effect-TS/effect-smol/commit/e3b44b6a2af9ee21dc5c1e928f0c20af857fa7a9), [`e1472b7`](https://github.com/Effect-TS/effect-smol/commit/e1472b7525c5d57a48bdec2353c3b742f7f916c0), [`7686320`](https://github.com/Effect-TS/effect-smol/commit/7686320cd123fa352b5c3d076fb18a3cac0a9bba)]:
- effect@4.0.0-beta.34
## 4.0.0-beta.33
### Patch Changes
- Updated dependencies [[`571447d`](https://github.com/Effect-TS/effect-smol/commit/571447da67334449f8ae3d6ecb3d77ea4e0c4295)]:
- effect@4.0.0-beta.33
## 4.0.0-beta.32
### Patch Changes
- Updated dependencies [[`bf8fff8`](https://github.com/Effect-TS/effect-smol/commit/bf8fff8a5f54b6df74cb7bbb42346fe9ba52435a), [`1af3ef3`](https://github.com/Effect-TS/effect-smol/commit/1af3ef3e3ca7fd417d0fc15f8ca8fe207eba4f74), [`27fea0f`](https://github.com/Effect-TS/effect-smol/commit/27fea0f66910de5905f40fd63f8ddbb6f7ac5aba), [`2ad6c1b`](https://github.com/Effect-TS/effect-smol/commit/2ad6c1b2c85a3a0fe351e3d56636a75eb76b4b4e), [`398ac3e`](https://github.com/Effect-TS/effect-smol/commit/398ac3e01cb75efce0e4e2913d1450cf65866732), [`51fe22f`](https://github.com/Effect-TS/effect-smol/commit/51fe22f3266e417b6c541aaed4b75d246fac91e7), [`4605db6`](https://github.com/Effect-TS/effect-smol/commit/4605db69cfacddbdbf1525865ddfde135158090c), [`f4de1b0`](https://github.com/Effect-TS/effect-smol/commit/f4de1b087c998d0bad1d9468f70b7d16c13b9f6f), [`60214f2`](https://github.com/Effect-TS/effect-smol/commit/60214f2080b2aeb091f691140eb20acb741691c3), [`c4b8b0f`](https://github.com/Effect-TS/effect-smol/commit/c4b8b0ffa8efb47c4cd7578a8943d6868509373f), [`6d9393a`](https://github.com/Effect-TS/effect-smol/commit/6d9393a0770a18722d23340e77f15455de341245), [`6de4efe`](https://github.com/Effect-TS/effect-smol/commit/6de4efe463c783614ceb0c094d77a336a899cbe0), [`4f969d1`](https://github.com/Effect-TS/effect-smol/commit/4f969d1563ba755ffa116c8ae409bb3436bd881d), [`6cc67c8`](https://github.com/Effect-TS/effect-smol/commit/6cc67c855e054ee3f3ac3485dca5f7805e79e8fb), [`8531a22`](https://github.com/Effect-TS/effect-smol/commit/8531a22ffbb52e11a030b09f358cafbfdf5edff7), [`b226760`](https://github.com/Effect-TS/effect-smol/commit/b22676067617f15c00722a3a63fd7c2c172c3d45), [`47a51ab`](https://github.com/Effect-TS/effect-smol/commit/47a51aba0ecdf3ef478bfa28a498bca188399bd4), [`1521d02`](https://github.com/Effect-TS/effect-smol/commit/1521d02e1f19f1d795edaaf862c1a1031d9c755e)]:
- effect@4.0.0-beta.32
## 4.0.0-beta.31
### Patch Changes
- Updated dependencies [[`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6f23f0e`](https://github.com/Effect-TS/effect-smol/commit/6f23f0ed4cba573cd9395c2e582f582fe7271544), [`654aaec`](https://github.com/Effect-TS/effect-smol/commit/654aaec593305521b65dd042c204d761cc6e8c28), [`2958a42`](https://github.com/Effect-TS/effect-smol/commit/2958a42078966a8713a98f00485ab36484d5eccf), [`95d27a2`](https://github.com/Effect-TS/effect-smol/commit/95d27a239ed5147302605ab0b3147a056541b0c7), [`0fbaea8`](https://github.com/Effect-TS/effect-smol/commit/0fbaea8f9555a8044cec31a770394db613fc78e2), [`21d5d5e`](https://github.com/Effect-TS/effect-smol/commit/21d5d5e0439fd4d9bb6e508377215b1087555d45), [`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6e49959`](https://github.com/Effect-TS/effect-smol/commit/6e499590357a104c81779b3176cd3f84e4f91064), [`8f5805d`](https://github.com/Effect-TS/effect-smol/commit/8f5805dbdd0d1bc0ff0727cc398c8d80e544edee), [`990df2c`](https://github.com/Effect-TS/effect-smol/commit/990df2c3ceeb32e659acc10cc9485617f7b3c423)]:
- effect@4.0.0-beta.31
## 4.0.0-beta.30
### Patch Changes
- [#1692](https://github.com/Effect-TS/effect-smol/pull/1692) [`00caf4f`](https://github.com/Effect-TS/effect-smol/commit/00caf4fbe209ff56426e9bcd51a205bdb84b978e) Thanks @tim-smart! - Preserve streamed OpenAI compat tool call ids and names across fragmented chat completion chunks.
- Updated dependencies [[`c88e5b7`](https://github.com/Effect-TS/effect-smol/commit/c88e5b723ff09da4edaef6ce14d927ca01104a32), [`947d0e4`](https://github.com/Effect-TS/effect-smol/commit/947d0e4268ba5c4020ead380aa80812c7342408f), [`7517908`](https://github.com/Effect-TS/effect-smol/commit/75179085d159b88a1ab0bce70669d76dcf0d79a4), [`a49ecd5`](https://github.com/Effect-TS/effect-smol/commit/a49ecd5a183d7e7d33f47ff95e9d2dea5a12ead5), [`6993e33`](https://github.com/Effect-TS/effect-smol/commit/6993e3329122c834c20bacea72d8678232f4f103), [`514f2a2`](https://github.com/Effect-TS/effect-smol/commit/514f2a2ae54580fcacdbe2ea2196a83a852d0748), [`3214b47`](https://github.com/Effect-TS/effect-smol/commit/3214b47676de2d33fddc5fecfc2d226e6e83cc7b), [`95ec5ed`](https://github.com/Effect-TS/effect-smol/commit/95ec5ed345de77c893049e182d37a37cf164a268)]:
- effect@4.0.0-beta.30
## 4.0.0-beta.29
### Patch Changes
- Updated dependencies [[`9d93adb`](https://github.com/Effect-TS/effect-smol/commit/9d93adb1c1795d1978391b30d7d2972c88052662), [`b52721c`](https://github.com/Effect-TS/effect-smol/commit/b52721cf0d11a567722b060c8536e3bdd4161f07), [`a891c7b`](https://github.com/Effect-TS/effect-smol/commit/a891c7b12f415b2287613dd4b91a09dfd38ef30d), [`ef26cdf`](https://github.com/Effect-TS/effect-smol/commit/ef26cdfb65d9955fc7e161629191930c2cc2c63f), [`82fd3ed`](https://github.com/Effect-TS/effect-smol/commit/82fd3ed922063ee5a34f96f3993c15c7515e4f67)]:
- effect@4.0.0-beta.29
## 4.0.0-beta.28
### Patch Changes
- [#1634](https://github.com/Effect-TS/effect-smol/pull/1634) [`621d3a1`](https://github.com/Effect-TS/effect-smol/commit/621d3a1248ef50e83c35d71333c69bf01bc33a11) Thanks @tim-smart! - Allow custom request properties in openai-compat model config and chat request types, and forward model-level custom properties to chat-completions payloads.
- Updated dependencies [[`ff533f2`](https://github.com/Effect-TS/effect-smol/commit/ff533f203cd06302ad08032a27e01269b4a2d4c6), [`dc803ee`](https://github.com/Effect-TS/effect-smol/commit/dc803ee52ebd3e9f931118f0dfcb804542847556), [`d660b1c`](https://github.com/Effect-TS/effect-smol/commit/d660b1c99cb93d4f79715e91c7a4486801c0eefa), [`93a05e3`](https://github.com/Effect-TS/effect-smol/commit/93a05e3eaa624058b162aedd66aad70102837270), [`2a65cf6`](https://github.com/Effect-TS/effect-smol/commit/2a65cf6fd81ef63d944e6fb51f058d439bf4a834), [`a561a40`](https://github.com/Effect-TS/effect-smol/commit/a561a40cc41c548c2cf3153aca065ee92ee8aa57), [`29cd24d`](https://github.com/Effect-TS/effect-smol/commit/29cd24d1fe78480a72eeb38a90281ffddc0530bc), [`662a8e6`](https://github.com/Effect-TS/effect-smol/commit/662a8e6857dac64a7cd13bd8df4b0674654622f8), [`d2b52ba`](https://github.com/Effect-TS/effect-smol/commit/d2b52bae5b9336cf59729fbdcc4d7f09512b0cbf), [`407c3b4`](https://github.com/Effect-TS/effect-smol/commit/407c3b43a5d1414558e0e33b6f1fc0e6a6d489cc), [`42bc7ce`](https://github.com/Effect-TS/effect-smol/commit/42bc7ce5480f6f2953c39f8cb5c850d61df6f5a2), [`e741322`](https://github.com/Effect-TS/effect-smol/commit/e74132226cbfee24234311c7c1c13e6b7391384e), [`5c75fa8`](https://github.com/Effect-TS/effect-smol/commit/5c75fa8fb71163bc4c035ba1a215574dfd4badfc), [`747177b`](https://github.com/Effect-TS/effect-smol/commit/747177b0602f12d4461a843e953dfdffbeb0a429), [`326cd48`](https://github.com/Effect-TS/effect-smol/commit/326cd4828bce573fe985f35152155464bf4c5a70), [`627e922`](https://github.com/Effect-TS/effect-smol/commit/627e922b8d1e9521eae5e1caa5d667ad00b1619a), [`662287e`](https://github.com/Effect-TS/effect-smol/commit/662287e9abc76c941ccc2ee330aa07904d571341)]:
- effect@4.0.0-beta.28
## 4.0.0-beta.27
### Patch Changes
- [#1623](https://github.com/Effect-TS/effect-smol/pull/1623) [`2cf00c1`](https://github.com/Effect-TS/effect-smol/commit/2cf00c184ccc994b7977f33996321923fa3534c4) Thanks @tim-smart! - Forward `OpenAiLanguageModel` `reasoning` config into chat-completions requests.
- Updated dependencies [[`903a839`](https://github.com/Effect-TS/effect-smol/commit/903a839e94239e6ec4568315af28e405bcad95f4), [`91a0168`](https://github.com/Effect-TS/effect-smol/commit/91a016836680a6669308ecf464d3584bcc4ae1b7), [`c890f9a`](https://github.com/Effect-TS/effect-smol/commit/c890f9a1b3a989ed22528bd5a43326342e05b142), [`1e985f2`](https://github.com/Effect-TS/effect-smol/commit/1e985f237d250b51b91de22dde77160c1e778ce7)]:
- effect@4.0.0-beta.27
## 4.0.0-beta.26
### Patch Changes
- Updated dependencies [[`fb21462`](https://github.com/Effect-TS/effect-smol/commit/fb21462642cdd5b1bada92f3eba18ae20445be42), [`2ed26b1`](https://github.com/Effect-TS/effect-smol/commit/2ed26b139805700e3df39efaa768ff01565e5c86), [`e832a57`](https://github.com/Effect-TS/effect-smol/commit/e832a57b570fe38f010c1fd99bceac5a325a9e07), [`7f01be7`](https://github.com/Effect-TS/effect-smol/commit/7f01be7f8db363d4b2e88e6b5571e96bb815786f), [`e965143`](https://github.com/Effect-TS/effect-smol/commit/e9651431e114479e6becf8ca7b1ed99ac7e91ccc), [`b9b80f1`](https://github.com/Effect-TS/effect-smol/commit/b9b80f1f15e152ceef0a727d150b7dc230abae99), [`98252aa`](https://github.com/Effect-TS/effect-smol/commit/98252aa0c0b17fc73fbdad65d0a1104965f9fc0f), [`56fbd94`](https://github.com/Effect-TS/effect-smol/commit/56fbd94311ad19a05001ad649d9e34ab00c74541), [`3faa109`](https://github.com/Effect-TS/effect-smol/commit/3faa109b7d093fbf14ad410d3e11d663f16e28f1), [`692ecfe`](https://github.com/Effect-TS/effect-smol/commit/692ecfed99fe58056b7a5afe001f4fcd1a61c446), [`1e70b72`](https://github.com/Effect-TS/effect-smol/commit/1e70b72d0b210474d0e96a15a5cfc279eae37e0c), [`ecf0782`](https://github.com/Effect-TS/effect-smol/commit/ecf07829ef2dfc01d8943c96c4fe9c1b44b97926)]:
- effect@4.0.0-beta.26
## 4.0.0-beta.25
### Patch Changes
- Updated dependencies [[`fa17bb5`](https://github.com/Effect-TS/effect-smol/commit/fa17bb5be9f2533d01e11322b14804c7dec43714), [`f46e5b5`](https://github.com/Effect-TS/effect-smol/commit/f46e5b5ca2a918ee4d9270167e79db223077c96f), [`ce4767c`](https://github.com/Effect-TS/effect-smol/commit/ce4767cadcacc6ce8ff4c3a0d0fbc82ede655f63), [`c830a8b`](https://github.com/Effect-TS/effect-smol/commit/c830a8b6c292a6528d7f9318759d34800b00372d)]:
- effect@4.0.0-beta.25
## 4.0.0-beta.24
### Patch Changes
- Updated dependencies [[`a909e1c`](https://github.com/Effect-TS/effect-smol/commit/a909e1c1ac2bc707527f5073776e3e7d239688d9), [`8814a4e`](https://github.com/Effect-TS/effect-smol/commit/8814a4ef78d67144d27689370af10099ea210399), [`3f942c5`](https://github.com/Effect-TS/effect-smol/commit/3f942c51cefa7b2ffa7c49e8c8a2c887570ba4c0), [`774ed59`](https://github.com/Effect-TS/effect-smol/commit/774ed59c52b2ab578bbb897c4f551f812231e1d2), [`f54b8d3`](https://github.com/Effect-TS/effect-smol/commit/f54b8d398fedad1815fd1f4c49814ab938cfc385)]:
- effect@4.0.0-beta.24
## 4.0.0-beta.23
### Patch Changes
- Updated dependencies [[`5c73c41`](https://github.com/Effect-TS/effect-smol/commit/5c73c41b69eaeab80fcd62c9bfda490b446d1966)]:
- effect@4.0.0-beta.23
## 4.0.0-beta.22
### Patch Changes
- Updated dependencies [[`0874332`](https://github.com/Effect-TS/effect-smol/commit/0874332f7c81118b06ac2eb105e0710211631479), [`c592dcd`](https://github.com/Effect-TS/effect-smol/commit/c592dcde0697e322065c8f418c0480ef910cb183), [`1dbe28d`](https://github.com/Effect-TS/effect-smol/commit/1dbe28dac8299cd3e218c9768450cfd173b5e294), [`564d730`](https://github.com/Effect-TS/effect-smol/commit/564d730b6bbf38dd8548a3b046e7a693b28699a4), [`3cfadc4`](https://github.com/Effect-TS/effect-smol/commit/3cfadc458b070c6cba6c5674b72a059f1e49118b), [`6634fd0`](https://github.com/Effect-TS/effect-smol/commit/6634fd07da067d80b8261fb2959d1a952b9e412e), [`d10dabe`](https://github.com/Effect-TS/effect-smol/commit/d10dabeb7af9a368f995829cd36ad08167cd8f95), [`f82f549`](https://github.com/Effect-TS/effect-smol/commit/f82f549a09e950e9d4987f279a800f4d953f0939), [`78a3382`](https://github.com/Effect-TS/effect-smol/commit/78a3382ddfbe034408f7480fa794733d9e82147b)]:
- effect@4.0.0-beta.22
## 4.0.0-beta.21
### Patch Changes
- Updated dependencies [[`e691909`](https://github.com/Effect-TS/effect-smol/commit/e691909495ccb162ea7bfa351dd74632b99997cb), [`d5f413f`](https://github.com/Effect-TS/effect-smol/commit/d5f413f3c8fc57f2413cc5649c2003d6d4e5a6d7), [`139d152`](https://github.com/Effect-TS/effect-smol/commit/139d152941e562a073b5be12e8d66c8a4d4a8a57), [`947e3d4`](https://github.com/Effect-TS/effect-smol/commit/947e3d436ab8a017efda9b29be523efd1ca8df28), [`84b2cce`](https://github.com/Effect-TS/effect-smol/commit/84b2ccefe2aa3a7413b86738a4dc33cdb311ca55), [`7f5305e`](https://github.com/Effect-TS/effect-smol/commit/7f5305e69f5a33309e77b08a576edb25d7daaee2), [`9e6fd84`](https://github.com/Effect-TS/effect-smol/commit/9e6fd8471c93a3c643929151a3bdb62cb9c0ca0e), [`fdb8a4b`](https://github.com/Effect-TS/effect-smol/commit/fdb8a4b172721fbefe98bd5aa6fe4f0efd1da3eb), [`0f986ef`](https://github.com/Effect-TS/effect-smol/commit/0f986ef22f196fe091a7afdbd179485a7d888882), [`9355fc0`](https://github.com/Effect-TS/effect-smol/commit/9355fc0ffb5b7382146a5aed9eea83974b10d007)]:
- effect@4.0.0-beta.21
## 4.0.0-beta.20
### Patch Changes
- [#1529](https://github.com/Effect-TS/effect-smol/pull/1529) [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8) Thanks @tim-smart! - Add dedicated AiError metadata interfaces per reason so provider packages can safely augment metadata without conflicting module declarations.
- [#1528](https://github.com/Effect-TS/effect-smol/pull/1528) [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34) Thanks @tim-smart! - Add `Model.ModelName` and provide it from AI model constructors.
- Updated dependencies [[`842a624`](https://github.com/Effect-TS/effect-smol/commit/842a624f79d5e1407460b0ef3ab27d14d48ccf74), [`4785eef`](https://github.com/Effect-TS/effect-smol/commit/4785eef5d7cf1edb96ef2509aed2ba4d1edf3862), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`12ee8e2`](https://github.com/Effect-TS/effect-smol/commit/12ee8e27df7eb393d83a5e403390d0cfc82ca732), [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34), [`989d1cc`](https://github.com/Effect-TS/effect-smol/commit/989d1cca936fce0cc459057825ba40e3f5ef3827)]:
- effect@4.0.0-beta.20
## 4.0.0-beta.19
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.19
## 4.0.0-beta.18
### Patch Changes
- Updated dependencies [[`01e31fd`](https://github.com/Effect-TS/effect-smol/commit/01e31fdf8e5206849d23cbafd23a346f2f177ab8), [`0890aab`](https://github.com/Effect-TS/effect-smol/commit/0890aab15ed9c5ba52c383a72fdc6a444d7504d5), [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d)]:
- effect@4.0.0-beta.18
## 4.0.0-beta.17
### Patch Changes
- Updated dependencies [[`8f59c32`](https://github.com/Effect-TS/effect-smol/commit/8f59c32922597a48392744f7203e284866747781)]:
- effect@4.0.0-beta.17
## 4.0.0-beta.16
### Patch Changes
- Updated dependencies [[`bf9096c`](https://github.com/Effect-TS/effect-smol/commit/bf9096c52a7d8791d93d232739e523eb84f6625a), [`29f81ca`](https://github.com/Effect-TS/effect-smol/commit/29f81ca07c67dba265804b140a7487fb15a5fc6b), [`68eb28c`](https://github.com/Effect-TS/effect-smol/commit/68eb28c2b0fc67a9f6204ade9bd16c5b37803bfb)]:
- effect@4.0.0-beta.16
## 4.0.0-beta.15
### Patch Changes
- [#1502](https://github.com/Effect-TS/effect-smol/pull/1502) [`285b7e6`](https://github.com/Effect-TS/effect-smol/commit/285b7e667167566d5788367d5155b19c79f1bf22) Thanks @tim-smart! - allow undefined for ai config
- Updated dependencies [[`24ae609`](https://github.com/Effect-TS/effect-smol/commit/24ae60995d2fd7d621be356cdfdfd328c79639ba), [`0e3c059`](https://github.com/Effect-TS/effect-smol/commit/0e3c059987caa55ebd0c134f7c7b147c639c328e), [`e843b0a`](https://github.com/Effect-TS/effect-smol/commit/e843b0a7d7e7b600a0b3bd477f24e2e4cd26bc8b), [`f4389a2`](https://github.com/Effect-TS/effect-smol/commit/f4389a2cca3c5bbf00d69779f52ce41255f15a28), [`5b73de0`](https://github.com/Effect-TS/effect-smol/commit/5b73de095b3402d0c5c74092ace6ce18ebfad566), [`595d2d6`](https://github.com/Effect-TS/effect-smol/commit/595d2d6e7d50419f3532bd39266191532ace38f2)]:
- effect@4.0.0-beta.15
## 4.0.0-beta.14
### Patch Changes
- Updated dependencies [[`c414700`](https://github.com/Effect-TS/effect-smol/commit/c414700ef1932e4b67d0102856de417336912350), [`a30c969`](https://github.com/Effect-TS/effect-smol/commit/a30c9699c0d736cf3952041e45d508b7d58907a9)]:
- effect@4.0.0-beta.14
## 4.0.0-beta.13
### Patch Changes
- Updated dependencies [[`368f4c3`](https://github.com/Effect-TS/effect-smol/commit/368f4c363dd117e6f5a19ad77b161176cfd29fdd), [`db8a579`](https://github.com/Effect-TS/effect-smol/commit/db8a579e93e93ff73b1e60712732e03b597b916b), [`668b703`](https://github.com/Effect-TS/effect-smol/commit/668b70337e9ddbb0d1ae2282a95c282ce404e562), [`d40e76b`](https://github.com/Effect-TS/effect-smol/commit/d40e76b973543979e60e04a6baca04a8c65bdfc2), [`6e18cf8`](https://github.com/Effect-TS/effect-smol/commit/6e18cf883e9905ca718a6697b6a2a4bbd42739aa), [`86062e8`](https://github.com/Effect-TS/effect-smol/commit/86062e8a0c61bca5412fc40d2cf151d676901f08), [`c27ce75`](https://github.com/Effect-TS/effect-smol/commit/c27ce75d34c74dcfc6dba1bf77f1ce88f410a0de), [`e2d4fbf`](https://github.com/Effect-TS/effect-smol/commit/e2d4fbfeeda6a5d2a4c5aeb0501d8240c248b9eb), [`114ab42`](https://github.com/Effect-TS/effect-smol/commit/114ab42ad0edc590d29169675a493e0e915aa58f), [`484caec`](https://github.com/Effect-TS/effect-smol/commit/484caec47cccac8b86db2910742e406dfc7173ab)]:
- effect@4.0.0-beta.13
## 4.0.0-beta.12
### Patch Changes
- Updated dependencies [[`70a74e8`](https://github.com/Effect-TS/effect-smol/commit/70a74e88a8767c9d4acdb9e5f25aec9a33588d07), [`b5b6e10`](https://github.com/Effect-TS/effect-smol/commit/b5b6e10621d54bf8c9857fec0d647ced78ecd857), [`f5ce5a9`](https://github.com/Effect-TS/effect-smol/commit/f5ce5a915359c6ebf254079e1da23cab6cde34fb), [`a29eb70`](https://github.com/Effect-TS/effect-smol/commit/a29eb702ffe3fc58bd28c4d7857298cd65d73668), [`c7b36e5`](https://github.com/Effect-TS/effect-smol/commit/c7b36e541a23e9a00f64e25b23851e51a37dfce5), [`9381d6d`](https://github.com/Effect-TS/effect-smol/commit/9381d6d4d9d819a81a46e56d0364c76e92a4fbca), [`88439f1`](https://github.com/Effect-TS/effect-smol/commit/88439f13ca13549f3e4822c48c4f019c14fc2bcc), [`e35307d`](https://github.com/Effect-TS/effect-smol/commit/e35307dbeb8eb26a9923f958b894a8eaaf259bf2), [`c7df4bc`](https://github.com/Effect-TS/effect-smol/commit/c7df4bce34009474c63d62a807abfdafb76971eb), [`accaf3b`](https://github.com/Effect-TS/effect-smol/commit/accaf3be7ac8da36e2334c509c23b8c9e88ea160), [`3e1c270`](https://github.com/Effect-TS/effect-smol/commit/3e1c2707bbdf67720af1509642b8ced195790882), [`6cd81f7`](https://github.com/Effect-TS/effect-smol/commit/6cd81f73baad86f5bbfa455a55d75cde71e9611a), [`f222da3`](https://github.com/Effect-TS/effect-smol/commit/f222da3cdb44554f3324c2c52d0d005ee575053e), [`61f901d`](https://github.com/Effect-TS/effect-smol/commit/61f901d830005b66e22d1de889fda132aeea97cd)]:
- effect@4.0.0-beta.12
## 4.0.0-beta.11
### Patch Changes
- Updated dependencies [[`88659ed`](https://github.com/Effect-TS/effect-smol/commit/88659edb26e3623d557dccfe914c2c949672da16), [`f2915e8`](https://github.com/Effect-TS/effect-smol/commit/f2915e8e2efe80d50c281e53f297b9701d6dc199), [`eb71ace`](https://github.com/Effect-TS/effect-smol/commit/eb71acebbe0f228e4920278013beee3b67d62310), [`2a16999`](https://github.com/Effect-TS/effect-smol/commit/2a169996c7513d377ac47adbfd68e1490457135c), [`d42dd52`](https://github.com/Effect-TS/effect-smol/commit/d42dd52f11203f8e749fb5d3ecf7153e4a5a6814), [`339adaf`](https://github.com/Effect-TS/effect-smol/commit/339adaf850a62a892adebcb208c2d9dddf3b97b3), [`de19645`](https://github.com/Effect-TS/effect-smol/commit/de1964526d01102dd1cb99c8cfdd3e8df1f49ef1), [`9b1dc3b`](https://github.com/Effect-TS/effect-smol/commit/9b1dc3bcf2a1b68d0a67e3465db5ad01a1a56997), [`e4cb2f5`](https://github.com/Effect-TS/effect-smol/commit/e4cb2f55b30f4771ec1bf613ced36d6d96464dd5), [`8bced95`](https://github.com/Effect-TS/effect-smol/commit/8bced954ecb35d4489197a57b0efe927e7d75f49), [`9431420`](https://github.com/Effect-TS/effect-smol/commit/94314207c8019918200fbcb97aec992219f801f0), [`948dca2`](https://github.com/Effect-TS/effect-smol/commit/948dca22e4f672ba7a6db57f9899272bec7c08b8), [`d18e327`](https://github.com/Effect-TS/effect-smol/commit/d18e32765a2665e31ffb31e746bf983fcfac34c5), [`ab512f7`](https://github.com/Effect-TS/effect-smol/commit/ab512f7be1c0e6b359da921e22cd4944e4c57d3e)]:
- effect@4.0.0-beta.11
## 4.0.0-beta.10
### Patch Changes
- Updated dependencies [[`371acab`](https://github.com/Effect-TS/effect-smol/commit/371acabb58d56f3a7a5e3e33d3d5fdc9f5573c74), [`856d774`](https://github.com/Effect-TS/effect-smol/commit/856d7741f1e296dd5048c6ff2b44b95d023e6ae4), [`b9e9202`](https://github.com/Effect-TS/effect-smol/commit/b9e92023c38caa322975d77cfe83e2d34ac9305a), [`1d1a974`](https://github.com/Effect-TS/effect-smol/commit/1d1a974bd280c81bff5d4505491cda03ba7a3f36), [`6bfe2a6`](https://github.com/Effect-TS/effect-smol/commit/6bfe2a659bc6335db75709931f405da45301cba2), [`b12c811`](https://github.com/Effect-TS/effect-smol/commit/b12c81157be287b1649c210616a244b50ec094d2), [`d17d98a`](https://github.com/Effect-TS/effect-smol/commit/d17d98ad78e2b44d95ef434adab79ac3c35e75ab), [`68c3c7c`](https://github.com/Effect-TS/effect-smol/commit/68c3c7cb1e06ed94fa5c4c123a234b4ccbfdecd8)]:
- effect@4.0.0-beta.10
## 4.0.0-beta.9
### Patch Changes
- Updated dependencies [[`3386557`](https://github.com/Effect-TS/effect-smol/commit/338655731564a7be9f8859dedbf4d5bcac6eb350), [`b6666e3`](https://github.com/Effect-TS/effect-smol/commit/b6666e3cf6bd44ba1a8704e65c256c30359cb422)]:
- effect@4.0.0-beta.9
## 4.0.0-beta.8
### Patch Changes
- Updated dependencies [[`246e672`](https://github.com/Effect-TS/effect-smol/commit/246e672dbbd7848d60e0c78fd66671b2f10b3752), [`807dec0`](https://github.com/Effect-TS/effect-smol/commit/807dec03801b4c58a6d00c237b6d98d6386911df)]:
- effect@4.0.0-beta.8
## 4.0.0-beta.7
### Patch Changes
- Updated dependencies [[`a2bda6d`](https://github.com/Effect-TS/effect-smol/commit/a2bda6d4ef6de9d9b0c53ae2df5434f778d6161a), [`1f95a2b`](https://github.com/Effect-TS/effect-smol/commit/1f95a2b5aa9524bb38f4437f4691a664bf463ca1), [`a8d5e79`](https://github.com/Effect-TS/effect-smol/commit/a8d5e792fec201a83af0eb92fc79928d055125fd), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`06d8a03`](https://github.com/Effect-TS/effect-smol/commit/06d8a0391631e6130e3ab25227e59817852e227f), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`f9e883e`](https://github.com/Effect-TS/effect-smol/commit/f9e883e266fbda870336ee62f46b7ac85ba3de6e), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430)]:
- effect@4.0.0-beta.7
## 4.0.0-beta.6
### Patch Changes
- Updated dependencies [[`3247da2`](https://github.com/Effect-TS/effect-smol/commit/3247da28331f345f68be5dbd2974a7e03d300fe1), [`f205705`](https://github.com/Effect-TS/effect-smol/commit/f2057050dbd034b8c186be2d40c3d03ee63a5a3b), [`f35022c`](https://github.com/Effect-TS/effect-smol/commit/f35022c212e4111527e1bb43f360a67b2b49fa85), [`8622721`](https://github.com/Effect-TS/effect-smol/commit/86227217b02d43680a3c6f3c21731b1d852c91f5), [`fc660ab`](https://github.com/Effect-TS/effect-smol/commit/fc660ab8b5ebae38b8d6b96cbf2f9b880cc09253), [`f37dc33`](https://github.com/Effect-TS/effect-smol/commit/f37dc335f64622fa9ce8d6d1d5dd8fc3f260257b), [`3662f32`](https://github.com/Effect-TS/effect-smol/commit/3662f328fcfa3b2fa01ffa79da40e12e93fcede8), [`a7d436f`](https://github.com/Effect-TS/effect-smol/commit/a7d436f438dcd7f49b9485e4e95a4511f31fad7d), [`6856a41`](https://github.com/Effect-TS/effect-smol/commit/6856a415d7eddd9d73d60919e976f1d071421be4), [`8c417d0`](https://github.com/Effect-TS/effect-smol/commit/8c417d03475e5e12d00dca0c4781d0af7e66b86c), [`5419570`](https://github.com/Effect-TS/effect-smol/commit/5419570ba47ce882a3a10882707b46f66e464906), [`449c5ed`](https://github.com/Effect-TS/effect-smol/commit/449c5ed5318e8a874e730420bcf52918fa2ec80f), [`4b5ec12`](https://github.com/Effect-TS/effect-smol/commit/4b5ec12f87f95f2a3cd8fe4d5b26c6eb0529381a), [`df87937`](https://github.com/Effect-TS/effect-smol/commit/df879375fc3b169c43f9c434b3775e12b80dffe4), [`5dbfca8`](https://github.com/Effect-TS/effect-smol/commit/5dbfca8d1dbb6d18d1605d4f8562e99c86e2ff11), [`e629497`](https://github.com/Effect-TS/effect-smol/commit/e6294973d55597ab6b6deca6babbe1e946b2c91d), [`981c991`](https://github.com/Effect-TS/effect-smol/commit/981c991cd78db34def815d5754379d737157f005), [`1ca2ed6`](https://github.com/Effect-TS/effect-smol/commit/1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe), [`45722bd`](https://github.com/Effect-TS/effect-smol/commit/45722bde974458311f11ad237711363a10ec6894), [`eb2a85e`](https://github.com/Effect-TS/effect-smol/commit/eb2a85ed4dc162b2535d304799333a5a20477fd0)]:
- effect@4.0.0-beta.6
## 4.0.0-beta.5
### Patch Changes
- Updated dependencies [[`f6e133e`](https://github.com/Effect-TS/effect-smol/commit/f6e133e9a16b32317bd09ff08c12b97a0ae44600), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8), [`a88e206`](https://github.com/Effect-TS/effect-smol/commit/a88e206e44dc66ca5a2b45bedc797877c5dbb083), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8)]:
- effect@4.0.0-beta.5
## 4.0.0-beta.4
### Patch Changes
- Updated dependencies [[`c5a18ef`](https://github.com/Effect-TS/effect-smol/commit/c5a18ef44171e3880bf983faee74529908974b32), [`bc6b885`](https://github.com/Effect-TS/effect-smol/commit/bc6b885b94d887a200657c0775dfa874dc15bc0c)]:
- effect@4.0.0-beta.4
## 4.0.0-beta.3
### Patch Changes
- Updated dependencies [[`3a0cf36`](https://github.com/Effect-TS/effect-smol/commit/3a0cf36eff106ba48d74e133c1598cd40613e530), [`c4da328`](https://github.com/Effect-TS/effect-smol/commit/c4da328d32fad1d61e0e538f5d371edf61521d7e)]:
- effect@4.0.0-beta.3
## 4.0.0-beta.2
### Patch Changes
- Updated dependencies [[`a22ce73`](https://github.com/Effect-TS/effect-smol/commit/a22ce73b2bd9305b7ba665694d2255c0e6d5a8d0), [`ebdabf7`](https://github.com/Effect-TS/effect-smol/commit/ebdabf79ff4e62c8384aa8cf9a8d2787d536ee78), [`8f663bb`](https://github.com/Effect-TS/effect-smol/commit/8f663bb121021bf12bd264e8ae385187cb7a5dae)]:
- effect@4.0.0-beta.2
## 4.0.0-beta.1
### Patch Changes
- Updated dependencies [[`0fecf70`](https://github.com/Effect-TS/effect-smol/commit/0fecf70048057623eed7c584a06671773a2b1743), [`709569e`](https://github.com/Effect-TS/effect-smol/commit/709569ed76bead9ebb0670599e4d890a07ca5a43)]:
- effect@4.0.0-beta.1
## 4.0.0-beta.0
### Major Changes
- [#1183](https://github.com/Effect-TS/effect-smol/pull/1183) [`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66) Thanks @tim-smart! - v4 beta
### Patch Changes
- Updated dependencies [[`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66)]:
- effect@4.0.0-beta.0

View File

@@ -0,0 +1,23 @@
{
"$schema": "../../node_modules/@effect/docgen/schema.json",
"srcLink": "https://github.com/Effect-TS/effect/tree/main/packages/ai/openai-compat/src/",
"exclude": ["src/internal/**"],
"examplesCompilerOptions": {
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "Bundler",
"module": "ES2022",
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"rewriteRelativeImportExtensions": true,
"allowImportingTsExtensions": true,
"paths": {
"effect": ["../../../effect/src/index.js"],
"effect/*": ["../../../effect/src/*.js"]
},
"plugins": [
{ "name": "@effect/language-service", "includeSuggestionsInTsc": false }
]
}
}

View File

@@ -0,0 +1,68 @@
{
"name": "@effect/ai-openai-compat",
"version": "4.0.0-beta.99",
"type": "module",
"license": "MIT",
"description": "An OpenAI compat integration for Effect",
"homepage": "https://effect.website",
"repository": {
"type": "git",
"url": "https://github.com/Effect-TS/effect.git",
"directory": "packages/ai/openai-compat"
},
"bugs": {
"url": "https://github.com/Effect-TS/effect/issues"
},
"tags": [
"typescript",
"ai",
"openai",
"compat"
],
"keywords": [
"typescript",
"ai",
"openai",
"compat"
],
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts",
"./*": "./src/*.ts",
"./internal/*": null,
"./*/index": null
},
"files": [
"src/**/*.ts",
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map"
],
"publishConfig": {
"access": "public",
"provenance": true,
"exports": {
"./package.json": "./package.json",
".": "./dist/index.js",
"./*": "./dist/*.js",
"./internal/*": null,
"./*/index": null
}
},
"scripts": {
"codegen": "effect-utils codegen",
"build": "tsc -b tsconfig.json && pnpm babel",
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
"check": "tsc -b tsconfig.json",
"test": "vitest",
"coverage": "vitest --coverage"
},
"devDependencies": {
"effect": "workspace:^"
},
"peerDependencies": {
"effect": "workspace:^"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/**
* The `OpenAiConfig` module lets a workflow temporarily customize the HTTP
* client used by OpenAI-compatible request helpers. Model, embedding, and
* tool-calling code can use this scoped configuration to add middleware,
* instrumentation, or routing without rebuilding the client layer.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import type { HttpClient } from "effect/unstable/http/HttpClient"
/**
* Context service for OpenAI-compatible client configuration in the current
* Effect scope.
*
* **When to use**
*
* Use as the context service for scoped OpenAI-compatible client configuration
* and HTTP client transforms.
*
* @see {@link withClientTransform} for scoping an HTTP client transformation
*
* @category services
* @since 4.0.0
*/
export class OpenAiConfig extends Context.Service<
OpenAiConfig,
OpenAiConfig.Service
>()("@effect/ai-openai-compat/OpenAiConfig") {
/**
* Gets the configured OpenAI-compatible service from the current context when present.
*
* @since 4.0.0
*/
static readonly getOrUndefined: Effect.Effect<typeof OpenAiConfig.Service | undefined> = Effect.map(
Effect.context<never>(),
(context) => context.mapUnsafe.get(OpenAiConfig.key)
)
}
/**
* Types associated with the `OpenAiConfig` context service.
*
* @since 4.0.0
*/
export declare namespace OpenAiConfig {
/**
* Configuration consumed by OpenAI-compatible clients when they build or
* resolve the underlying HTTP client.
*
* @category models
* @since 4.0.0
*/
export interface Service {
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined
}
}
/**
* Provides an HTTP client transform for the supplied effect.
*
* **When to use**
*
* Use to add provider-specific OpenAI-compatible HTTP behavior, such as
* headers, retries, instrumentation, or proxy routing.
*
* **Details**
*
* OpenAI-compatible provider services read the transform from the
* `OpenAiConfig` context.
*
* @category configuration
* @since 4.0.0
*/
export const withClientTransform: {
(transform: (client: HttpClient) => HttpClient): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<A, E, R>(self: Effect.Effect<A, E, R>, transform: (client: HttpClient) => HttpClient): Effect.Effect<A, E, R>
} = dual(2, <A, E, R>(
self: Effect.Effect<A, E, R>,
transformClient: (client: HttpClient) => HttpClient
) =>
Effect.flatMap(
OpenAiConfig.getOrUndefined,
(config) => Effect.provideService(self, OpenAiConfig, { ...config, transformClient })
))

View File

@@ -0,0 +1,244 @@
/**
* The `OpenAiEmbeddingModel` module adapts OpenAI-compatible embeddings
* endpoints to Effect's embedding model service. It sends embedding requests
* through {@link OpenAiClient}, exposes constructors for layers and `AiModel`
* values, supports scoped request configuration overrides, and checks that the
* provider returns one numeric vector for each requested input.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Layer from "effect/Layer"
import type { Simplify } from "effect/Types"
import * as AiError from "effect/unstable/ai/AiError"
import * as EmbeddingModel from "effect/unstable/ai/EmbeddingModel"
import * as AiModel from "effect/unstable/ai/Model"
import type { CreateEmbedding200, CreateEmbeddingRequestJson } from "./OpenAiClient.ts"
import { OpenAiClient } from "./OpenAiClient.ts"
/**
* A model identifier accepted by an OpenAI-compatible embeddings endpoint.
*
* @category models
* @since 4.0.0
*/
export type Model = string
type ConfigOptions = Simplify<Partial<Omit<CreateEmbeddingRequestJson, "input">>>
type ModelConfig = Omit<ConfigOptions, "model"> & { readonly [x: string]: unknown }
/**
* Context service for OpenAI embedding model configuration.
*
* **When to use**
*
* Use when you need to provide shared default request options for
* OpenAI-compatible embedding operations through the Effect context, such as
* `dimensions`, `encoding_format`, or `user`.
*
* **Details**
*
* The service stores the embedding request payload without `input`. Requests
* combine the selected model, layer or constructor config, and scoped context
* config, with scoped context config taking precedence.
*
* @see {@link withConfigOverride} for scoping embedding request overrides
*
* @category context
* @since 4.0.0
*/
export class Config extends Context.Service<
Config,
ConfigOptions & { readonly [x: string]: unknown }
>()("@effect/ai-openai-compat/OpenAiEmbeddingModel/Config") {}
/**
* Creates an `AiModel` for an OpenAI-compatible embedding model with its configured vector dimensions.
*
* **When to use**
*
* Use to provide an OpenAI-compatible `EmbeddingModel` and its `Dimensions`
* service to an Effect program.
*
* @see {@link layer} for providing only the embedding model service
* @see {@link withConfigOverride} for scoped request configuration overrides
*
* @category constructors
* @since 4.0.0
*/
export const model = (
model: string,
options: Omit<ConfigOptions, "model" | "dimensions"> & {
readonly dimensions: number
readonly [x: string]: unknown
}
): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel | EmbeddingModel.Dimensions, OpenAiClient> =>
AiModel.make(
"openai",
model,
Layer.merge(
layer({
model,
config: options
}),
Layer.succeed(EmbeddingModel.Dimensions, options.dimensions)
)
)
/**
* Creates an OpenAI-compatible embedding model service backed by `OpenAiClient`.
*
* **When to use**
*
* Use when you need to build or provide an `EmbeddingModel` service directly
* from an existing `OpenAiClient`.
*
* **Details**
*
* The service sends embedding requests through `OpenAiClient.createEmbedding`.
* Request config is merged as the selected model, constructor config, then
* scoped `Config`, so scoped overrides take precedence. Provider usage
* `prompt_tokens` is exposed as `usage.inputTokens`.
*
* **Gotchas**
*
* Provider responses must contain one numeric vector for every requested input
* with unique, in-range `index` values; otherwise embedding operations fail with
* `AiError.InvalidOutputError`.
*
* @see {@link model} for the higher-level `AiModel` descriptor that also provides `EmbeddingModel.Dimensions`
* @see {@link layer} for providing the service as a `Layer`
* @see {@link withConfigOverride} for scoping embedding request overrides
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
readonly model: string
readonly config?: ModelConfig | undefined
}): Effect.fn.Return<EmbeddingModel.Service, never, OpenAiClient> {
const client = yield* OpenAiClient
const makeConfig = Effect.gen(function*() {
const services = yield* Effect.context<never>()
return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
})
return yield* EmbeddingModel.make({
embedMany: Effect.fnUntraced(function*({ inputs }) {
const config = yield* makeConfig
const response = yield* client.createEmbedding({ ...config, input: inputs })
return yield* mapProviderResponse(inputs.length, response)
})
})
})
/**
* Creates a layer for an OpenAI-compatible embedding model service.
*
* **When to use**
*
* Use when composing application layers and you want an OpenAI-compatible
* embeddings endpoint to satisfy `EmbeddingModel.EmbeddingModel` while
* supplying `OpenAiClient` from another layer.
*
* @see {@link make} for constructing the embedding model service effectfully
* @see {@link model} for creating an `AiModel` with configured dimensions
*
* @category layers
* @since 4.0.0
*/
export const layer = (options: {
readonly model: string
readonly config?: ModelConfig | undefined
}): Layer.Layer<EmbeddingModel.EmbeddingModel, never, OpenAiClient> =>
Layer.effect(EmbeddingModel.EmbeddingModel, make(options))
/**
* Provides scoped request config overrides for OpenAI-compatible embedding model operations.
*
* **When to use**
*
* Use to apply embedding request options to one effect without changing the
* model's default configuration.
*
* **Details**
*
* The overrides are merged with any existing `Config` service for the duration
* of the supplied effect. Fields in `overrides` take precedence over existing
* config, and the helper supports both `effect.pipe(withConfigOverride(overrides))`
* and `withConfigOverride(effect, overrides)`.
*
* @see {@link Config} for available OpenAI-compatible embedding request configuration fields
*
* @category configuration
* @since 4.0.0
*/
export const withConfigOverride: {
(overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
} = dual<
(
overrides: typeof Config.Service
) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
>(2, (self, overrides) =>
Effect.flatMap(
Effect.serviceOption(Config),
(config) =>
Effect.provideService(self, Config, {
...(config._tag === "Some" ? config.value : {}),
...overrides
})
))
const mapProviderResponse = (
inputLength: number,
response: CreateEmbedding200
): Effect.Effect<EmbeddingModel.ProviderResponse, AiError.AiError> => {
if (response.data.length !== inputLength) {
return Effect.fail(
invalidOutput(`Provider returned ${response.data.length} embeddings but expected ${inputLength}`)
)
}
const results = new Array<Array<number>>(inputLength)
const seen = new Set<number>()
for (const entry of response.data) {
if (!Number.isInteger(entry.index) || entry.index < 0 || entry.index >= inputLength) {
return Effect.fail(invalidOutput(`Provider returned invalid embedding index: ${entry.index}`))
}
if (seen.has(entry.index)) {
return Effect.fail(invalidOutput(`Provider returned duplicate embedding index: ${entry.index}`))
}
if (!Array.isArray(entry.embedding)) {
return Effect.fail(invalidOutput(`Provider returned non-vector embedding at index ${entry.index}`))
}
seen.add(entry.index)
results[entry.index] = [...entry.embedding]
}
if (seen.size !== inputLength) {
return Effect.fail(
invalidOutput(`Provider returned embeddings for ${seen.size} inputs but expected ${inputLength}`)
)
}
return Effect.succeed({
results,
usage: {
inputTokens: response.usage?.prompt_tokens
}
})
}
const invalidOutput = (description: string): AiError.AiError =>
AiError.make({
module: "OpenAiEmbeddingModel",
method: "embedMany",
reason: new AiError.InvalidOutputError({ description })
})

View File

@@ -0,0 +1,178 @@
/**
* The `OpenAiError` module defines OpenAI-specific metadata that can be
* attached to the shared `AiError` error types used by the AI packages. It is
* primarily used by OpenAI-compatible clients to preserve provider details
* such as error codes, error types, request IDs, and rate limit headers while
* still exposing errors through the provider-neutral Effect AI error model.
*
* Use this module when mapping OpenAI API failures into `AiError` values and
* when consumers need enough structured metadata to debug failed requests,
* inspect quota or rate limit responses, or correlate an error with OpenAI
* support. The exported types are metadata shapes only; the module augmentation
* makes those shapes available on the corresponding shared AI error metadata
* interfaces without defining new runtime error classes.
*
* @since 4.0.0
*/
/**
* OpenAI-specific error metadata fields.
*
* @category models
* @since 4.0.0
*/
export type OpenAiErrorMetadata = {
/**
* The OpenAI error code returned by the API.
*/
readonly errorCode: string | null
/**
* The OpenAI error type returned by the API.
*/
readonly errorType: string | null
/**
* The unique request ID for debugging with OpenAI support.
*/
readonly requestId: string | null
}
/**
* OpenAI-specific rate limit metadata fields.
*
* **Details**
*
* Extends base error metadata with rate limit specific information from
* OpenAI's rate limit headers.
*
* @category models
* @since 4.0.0
*/
export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
/**
* The rate limit type (e.g. "requests", "tokens").
*/
readonly limit: string | null
/**
* Number of remaining requests in the current window.
*/
readonly remaining: number | null
/**
* Time until the request rate limit resets.
*/
readonly resetRequests: string | null
/**
* Time until the token rate limit resets.
*/
readonly resetTokens: string | null
}
declare module "effect/unstable/ai/AiError" {
/**
* Metadata attached to rate limit errors returned by OpenAI-compatible APIs.
*
* @category models
* @since 4.0.0
*/
export interface RateLimitErrorMetadata {
readonly openai?: OpenAiRateLimitMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible provider reports that quota or
* billing limits have been exhausted.
*
* @category models
* @since 4.0.0
*/
export interface QuotaExhaustedErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached to authentication failures from OpenAI-compatible APIs,
* such as invalid, missing, or unauthorized API credentials.
*
* @category models
* @since 4.0.0
*/
export interface AuthenticationErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible provider rejects content because
* it violates a safety or usage policy.
*
* @category models
* @since 4.0.0
*/
export interface ContentPolicyErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached to malformed or unsupported requests rejected by an
* OpenAI-compatible API before model execution.
*
* @category models
* @since 4.0.0
*/
export interface InvalidRequestErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached to unexpected server-side failures reported by an
* OpenAI-compatible provider.
*
* @category models
* @since 4.0.0
*/
export interface InternalProviderErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible response cannot be converted
* into the expected AI package output shape.
*
* @category models
* @since 4.0.0
*/
export interface InvalidOutputErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible structured output response does
* not satisfy the requested schema or parsing constraints.
*
* @category models
* @since 4.0.0
*/
export interface StructuredOutputErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible provider cannot support the
* schema supplied for structured output or tool definitions.
*
* @category models
* @since 4.0.0
*/
export interface UnsupportedSchemaErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
/**
* Metadata attached when an OpenAI-compatible error response cannot be mapped
* to a more specific shared AI error category.
*
* @category models
* @since 4.0.0
*/
export interface UnknownErrorMetadata {
readonly openai?: OpenAiErrorMetadata | null
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,164 @@
/**
* The `OpenAiTelemetry` module defines OpenAI-compatible telemetry attributes
* and a helper for adding them to a tracing span. It keeps the standard GenAI
* telemetry attributes and adds request and response metadata under the
* `gen_ai.openai.*` OpenTelemetry namespaces.
*
* @since 4.0.0
*/
import { dual } from "effect/Function"
import * as String from "effect/String"
import type { Span } from "effect/Tracer"
import type { Simplify } from "effect/Types"
import * as Telemetry from "effect/unstable/ai/Telemetry"
/**
* The attributes used to describe telemetry in the context of Generative
* Artificial Intelligence (GenAI) Models requests and responses.
*
* **Details**
*
* These attributes follow the OpenTelemetry generative AI semantic
* conventions:
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/
*
* @category models
* @since 4.0.0
*/
export type OpenAiTelemetryAttributes = Simplify<
& Telemetry.GenAITelemetryAttributes
& Telemetry.AttributesWithPrefix<RequestAttributes, "gen_ai.openai.request">
& Telemetry.AttributesWithPrefix<ResponseAttributes, "gen_ai.openai.request">
>
/**
* All telemetry attributes which are part of the GenAI specification,
* including the OpenAI-specific attributes.
*
* @category models
* @since 4.0.0
*/
export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.openai.request`.
*
* @category models
* @since 4.0.0
*/
export interface RequestAttributes {
/**
* The response format that is requested.
*/
readonly responseFormat?: (string & {}) | WellKnownResponseFormat | null | undefined
/**
* The service tier requested. May be a specific tier, `default`, or `auto`.
*/
readonly serviceTier?: (string & {}) | WellKnownServiceTier | null | undefined
}
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.openai.response`.
*
* @category models
* @since 4.0.0
*/
export interface ResponseAttributes {
/**
* The service tier used for the response.
*/
readonly serviceTier?: string | null | undefined
/**
* A fingerprint to track any eventual change in the Generative AI
* environment.
*/
readonly systemFingerprint?: string | null | undefined
}
/**
* The `gen_ai.openai.request.response_format` attribute has a list of
* well-known values.
*
* **Details**
*
* If one of them applies, then the respective value **MUST** be used;
* otherwise, a custom value **MAY** be used.
*
* @category models
* @since 4.0.0
*/
export type WellKnownResponseFormat = "json_object" | "json_schema" | "text"
/**
* The `gen_ai.openai.request.service_tier` attribute has a list of
* well-known values.
*
* **Details**
*
* If one of them applies, then the respective value **MUST** be used;
* otherwise, a custom value **MAY** be used.
*
* @category models
* @since 4.0.0
*/
export type WellKnownServiceTier = "auto" | "default"
/**
* Options accepted by `addGenAIAnnotations`, combining standard GenAI telemetry
* attributes with optional OpenAI-compatible request and response attributes.
*
* @category options
* @since 4.0.0
*/
export type OpenAiTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeOptions & {
openai?: {
request?: RequestAttributes | undefined
response?: ResponseAttributes | undefined
} | undefined
}
const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("gen_ai.openai.request", String.camelToSnake)<
RequestAttributes
>
const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.response", String.camelToSnake)<
ResponseAttributes
>
/**
* Applies the specified OpenAI GenAI telemetry attributes to the provided
* `Span`.
*
* **When to use**
*
* Use to annotate an OpenAI-compatible model span with standard GenAI telemetry
* attributes and OpenAI-specific request or response metadata.
*
* **Details**
*
* Standard GenAI attributes are applied first. When OpenAI request or response
* metadata is present, it is written under `gen_ai.openai.request.*` and
* `gen_ai.openai.response.*` attributes.
*
* **Gotchas**
*
* Mutates the supplied `Span` in place.
*
* @category tracing
* @since 4.0.0
*/
export const addGenAIAnnotations: {
(options: OpenAiTelemetryAttributeOptions): (span: Span) => void
(span: Span, options: OpenAiTelemetryAttributeOptions): void
} = dual(2, (span: Span, options: OpenAiTelemetryAttributeOptions) => {
Telemetry.addGenAIAnnotations(span, options)
if (options.openai != null) {
if (options.openai.request != null) {
addOpenAiRequestAttributes(span, options.openai.request)
}
if (options.openai.response != null) {
addOpenAiResponseAttributes(span, options.openai.response)
}
}
})

View File

@@ -0,0 +1,35 @@
/**
* @since 4.0.0
*/
// @barrel: Auto-generated exports. Do not edit manually.
/**
* @since 4.0.0
*/
export * as OpenAiClient from "./OpenAiClient.ts"
/**
* @since 4.0.0
*/
export * as OpenAiConfig from "./OpenAiConfig.ts"
/**
* @since 4.0.0
*/
export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts"
/**
* @since 4.0.0
*/
export * as OpenAiError from "./OpenAiError.ts"
/**
* @since 4.0.0
*/
export * as OpenAiLanguageModel from "./OpenAiLanguageModel.ts"
/**
* @since 4.0.0
*/
export * as OpenAiTelemetry from "./OpenAiTelemetry.ts"

View File

@@ -0,0 +1,327 @@
import * as Arr from "effect/Array"
import * as Duration from "effect/Duration"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Number from "effect/Number"
import * as Option from "effect/Option"
import * as Redactable from "effect/Redactable"
import * as Schema from "effect/Schema"
import * as SchemaTransformation from "effect/SchemaTransformation"
import * as String from "effect/String"
import * as AiError from "effect/unstable/ai/AiError"
import type * as Response from "effect/unstable/ai/Response"
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import type { OpenAiErrorMetadata } from "../OpenAiError.ts"
/** @internal */
export const OpenAiErrorBody = Schema.Struct({
error: Schema.Struct({
message: Schema.String,
type: Schema.optional(Schema.NullOr(Schema.String)),
status: Schema.optional(Schema.NullOr(Schema.String)),
param: Schema.optional(Schema.NullOr(Schema.String)),
code: Schema.optional(Schema.NullOr(Schema.Union([Schema.String, Schema.Number])))
})
})
const OpenAiErrorBodyJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Union([
OpenAiErrorBody,
Schema.NonEmptyArray(OpenAiErrorBody).pipe(
Schema.decodeTo(
Schema.toType(OpenAiErrorBody),
SchemaTransformation.transform({
decode: Arr.headNonEmpty,
encode: (item) => [item]
})
)
)
])))
/** @internal */
export const mapSchemaError = dual<
(method: string) => (error: Schema.SchemaError) => AiError.AiError,
(error: Schema.SchemaError, method: string) => AiError.AiError
>(2, (error, method) =>
AiError.make({
module: "OpenAiClient",
method,
reason: AiError.InvalidOutputError.fromSchemaError(error)
}))
/** @internal */
export const mapHttpClientError = dual<
(method: string) => (error: HttpClientError.HttpClientError) => Effect.Effect<never, AiError.AiError>,
(error: HttpClientError.HttpClientError, method: string) => Effect.Effect<never, AiError.AiError>
>(2, (error, method) => {
const reason = error.reason
switch (reason._tag) {
case "TransportError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "TransportError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "EncodeError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "EncodeError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "InvalidUrlError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "InvalidUrlError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "StatusCodeError": {
return mapStatusCodeError(reason, method)
}
case "DecodeError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Failed to decode response"
})
}))
}
case "EmptyBodyError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Response body was empty"
})
}))
}
}
})
/** @internal */
const mapStatusCodeError = Effect.fnUntraced(function*(
error: HttpClientError.StatusCodeError,
method: string
) {
const { request, response, description } = error
const status = response.status
const headers = response.headers as Record<string, string>
const requestId = headers["x-request-id"]
let body = yield* response.text.pipe(
Effect.catchCause(() => Effect.succeed(description?.startsWith("{") ? description : undefined))
)
const decoded = OpenAiErrorBodyJson(body)
const reason = mapStatusCodeToReason({
status,
headers,
message: Option.isSome(decoded) ? decoded.value.error.message : undefined,
http: buildHttpContext({ request, response, body }),
metadata: {
errorCode: Option.isSome(decoded) ? decoded.value.error.code?.toString() ?? null : null,
errorType: decoded.pipe(
Option.flatMapNullishOr((d) => d.error.type ?? d.error.status),
Option.map(String.toLowerCase),
Option.getOrNull
),
requestId: requestId ?? null
}
})
return yield* AiError.make({ module: "OpenAiClient", method, reason })
})
/** @internal */
export const parseRateLimitHeaders = (headers: Record<string, string>) => {
const retryAfterRaw = headers["retry-after"]
let retryAfter: Duration.Duration | undefined
if (retryAfterRaw !== undefined) {
const parsed = Number.parse(retryAfterRaw)
if (Option.isSome(parsed)) {
retryAfter = Duration.seconds(parsed.value)
}
}
const remainingRaw = headers["x-ratelimit-remaining-requests"]
const remaining = remainingRaw !== undefined ? Option.getOrNull(Number.parse(remainingRaw)) : null
return {
retryAfter,
limit: headers["x-ratelimit-limit-requests"] ?? null,
remaining,
resetRequests: headers["x-ratelimit-reset-requests"] ?? null,
resetTokens: headers["x-ratelimit-reset-tokens"] ?? null
}
}
/** @internal */
export const buildHttpRequestDetails = (
request: HttpClientRequest.HttpClientRequest
): typeof Response.HttpRequestDetails.Type => ({
method: request.method,
url: request.url,
urlParams: Array.from(request.urlParams),
hash: Option.getOrUndefined(request.hash),
headers: Redactable.redact(request.headers) as Record<string, string>
})
/** @internal */
export const buildHttpContext = (params: {
readonly request: HttpClientRequest.HttpClientRequest
readonly response?: HttpClientResponse.HttpClientResponse
readonly body?: string | undefined
}): typeof AiError.HttpContext.Type => ({
request: buildHttpRequestDetails(params.request),
response: params.response !== undefined
? {
status: params.response.status,
headers: Redactable.redact(params.response.headers) as Record<string, string>
}
: undefined,
body: params.body
})
const buildInvalidRequestDescription = (params: {
readonly status: number
readonly message: string | undefined
readonly method: string
readonly url: string
readonly errorCode: string | null
readonly errorType: string | null
readonly requestId: string | null
readonly body: string | undefined
}): string => {
const parts: Array<string> = []
if (params.message) {
parts.push(params.message)
} else {
parts.push(`HTTP ${params.status}`)
}
parts.push(`(${params.method} ${params.url})`)
if (params.errorCode) {
parts.push(`[code: ${params.errorCode}]`)
} else if (params.errorType) {
parts.push(`[type: ${params.errorType}]`)
}
if (params.requestId) {
parts.push(`[requestId: ${params.requestId}]`)
}
if (!params.message && params.body) {
const truncated = params.body.length > 200
? params.body.slice(0, 200) + "..."
: params.body
parts.push(`Response: ${truncated}`)
}
return parts.join(" ")
}
/** @internal */
export const mapStatusCodeToReason = ({ status, headers, message, metadata, http }: {
readonly status: number
readonly headers: Record<string, string>
readonly message: string | undefined
readonly metadata: OpenAiErrorMetadata
readonly http: typeof AiError.HttpContext.Type
}): AiError.AiErrorReason => {
const invalidRequestDescription = buildInvalidRequestDescription({
status,
message,
method: http.request.method,
url: http.request.url,
errorCode: metadata.errorCode,
errorType: metadata.errorType,
requestId: metadata.requestId,
body: http.body
})
switch (status) {
case 400:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 401:
return new AiError.AuthenticationError({
kind: "InvalidKey",
metadata,
http
})
case 403:
return new AiError.AuthenticationError({
kind: "InsufficientPermissions",
metadata,
http
})
case 404:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 409:
case 422:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 429: {
if (
metadata.errorCode === "insufficient_quota" ||
metadata.errorType === "insufficient_quota" ||
metadata.errorType?.includes("quota") ||
metadata.errorType?.includes("exhausted")
) {
return new AiError.QuotaExhaustedError({
metadata: { openai: metadata },
http
})
}
const { retryAfter, ...rateLimitMetadata } = parseRateLimitHeaders(headers)
return new AiError.RateLimitError({
retryAfter,
metadata: {
openai: {
...metadata,
...rateLimitMetadata
}
},
http
})
}
default:
if (status >= 500) {
return new AiError.InternalProviderError({
description: message ?? "Server error",
metadata,
http
})
}
return new AiError.UnknownError({
description: message,
metadata,
http
})
}
}

View File

@@ -0,0 +1,24 @@
import type * as Response from "effect/unstable/ai/Response"
const finishReasonMap: Record<string, Response.FinishReason> = {
content_filter: "content-filter",
function_call: "tool-calls",
length: "length",
stop: "stop",
tool_calls: "tool-calls"
}
/** @internal */
export const resolveFinishReason = (
finishReason: string | null | undefined,
hasToolCalls: boolean
): Response.FinishReason => {
if (finishReason == null) {
return hasToolCalls ? "tool-calls" : "stop"
}
const reason = finishReasonMap[finishReason]
if (reason == null) {
return hasToolCalls ? "tool-calls" : "unknown"
}
return reason
}

View File

@@ -0,0 +1,474 @@
import * as OpenAiClient from "@effect/ai-openai-compat/OpenAiClient"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Layer, Redacted, Stream } from "effect"
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
describe("OpenAiClient", () => {
describe("request behavior", () => {
it.effect("sets auth and OpenAI headers on /chat/completions requests", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key"),
apiUrl: "https://compat.example.test/v1",
organizationId: Redacted.make("org_123"),
projectId: Redacted.make("proj_456")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, 200, makeChatCompletion()))
})
))
)
yield* client.createResponse({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }]
})
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
assert.isTrue(capturedRequest.url.endsWith("/chat/completions"))
assert.isTrue(capturedRequest.url.startsWith("https://compat.example.test/v1"))
assert.strictEqual(capturedRequest.headers["authorization"], "Bearer sk-test-key")
assert.strictEqual(capturedRequest.headers["openai-organization"], "org_123")
assert.strictEqual(capturedRequest.headers["openai-project"], "proj_456")
const body = yield* getRequestBody(capturedRequest)
assert.strictEqual(body.messages[0]?.role, "user")
assert.strictEqual(body.messages[0]?.content, "hello")
}))
it.effect("passes custom chat-completions request properties through", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, 200, makeChatCompletion()))
})
))
)
yield* client.createResponse({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
provider_feature: {
enabled: true
}
})
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
assert.deepStrictEqual(body.provider_feature, {
enabled: true
})
}))
it.effect("uses /embeddings path and decodes permissive embedding payloads", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key"),
apiUrl: "https://compat.example.test/v1"
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, 200, {
data: [{
embedding: "YmFzZTY0LWRhdGE=",
index: 0,
object: "embedding",
vendor_payload: { future_field: true }
}],
model: "my-custom-embedding-model",
object: "list",
usage: {
prompt_tokens: 5,
total_tokens: 5
},
unknown_top_level: true
}))
})
))
)
const embedding = yield* client.createEmbedding({
model: "my-custom-embedding-model",
input: "embed this"
})
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
assert.isTrue(capturedRequest.url.endsWith("/embeddings"))
assert.strictEqual(embedding.model, "my-custom-embedding-model")
assert.strictEqual(embedding.data[0]?.index, 0)
assert.strictEqual(typeof embedding.data[0]?.embedding, "string")
}))
it.effect("sets stream=true for createResponseStream and returns chat chunks", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(sseResponse(request, [
{
id: "chatcmpl_test_1",
object: "chat.completion.chunk",
model: "gpt-4o-mini",
created: 1,
future_provider_field: { accepted: true },
choices: [{
index: 0,
delta: { content: "Hello" },
finish_reason: null
}]
},
{
id: "chatcmpl_test_1",
object: "chat.completion.chunk",
model: "gpt-4o-mini",
created: 1,
usage: {
prompt_tokens: 4,
completion_tokens: 2,
total_tokens: 6,
prompt_tokens_details: { cached_tokens: 1 },
completion_tokens_details: { reasoning_tokens: 1 }
},
choices: [{
index: 0,
delta: {},
finish_reason: "stop"
}]
},
"[DONE]"
]))
})
))
)
const eventsChunk = yield* client.createResponseStream({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }]
}).pipe(
Effect.flatMap(([_, stream]) => Stream.runCollect(stream))
)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
assert.strictEqual(body.stream, true)
assert.strictEqual(body.stream_options.include_usage, true)
assert.isTrue(capturedRequest.url.endsWith("/chat/completions"))
const events = globalThis.Array.from(eventsChunk)
const firstEvent = events[0]
const secondEvent = events[1]
assert.isTrue(typeof firstEvent === "object")
assert.isTrue(typeof secondEvent === "object")
if (
typeof firstEvent !== "object" || firstEvent === null || typeof secondEvent !== "object" ||
secondEvent === null
) {
return
}
assert.strictEqual(firstEvent.id, "chatcmpl_test_1")
assert.strictEqual(secondEvent.id, "chatcmpl_test_1")
assert.strictEqual(events[2], "[DONE]")
}))
it.effect("passes chat-completions tool_choice payload through unchanged", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, 200, makeChatCompletion()))
})
))
)
yield* client.createResponse({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }],
tool_choice: {
type: "function",
function: {
name: "TestTool"
}
},
tools: [{
type: "function",
function: {
name: "TestTool",
parameters: {
type: "object",
additionalProperties: false,
properties: {
input: { type: "string" }
},
required: ["input"]
}
}
}]
})
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
assert.deepStrictEqual(body.tool_choice, { type: "function", function: { name: "TestTool" } })
}))
it.effect("accepts assistant tool-call and tool result chat history", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, 200, makeChatCompletion()))
})
))
)
yield* client.createResponse({
model: "gpt-4o-mini",
messages: [
{
role: "assistant",
content: null,
tool_calls: [{
id: "patch_call_1",
type: "function",
function: {
name: "apply_patch",
arguments: JSON.stringify({
call_id: "patch_call_1",
operation: {
type: "delete_file",
path: "src/obsolete.ts"
}
})
}
}]
},
{
role: "tool",
tool_call_id: "patch_call_1",
content: "deleted"
}
]
})
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const body = yield* getRequestBody(capturedRequest)
const assistantMessages = body.messages.filter((message: any) => message.role === "assistant")
const patchMessage = assistantMessages.find((message: any) =>
message.tool_calls?.[0]?.function?.name === "apply_patch"
)
assert.isDefined(patchMessage)
assert.strictEqual(patchMessage.tool_calls[0].id, "patch_call_1")
assert.deepStrictEqual(JSON.parse(patchMessage.tool_calls[0].function.arguments), {
call_id: "patch_call_1",
operation: {
type: "delete_file",
path: "src/obsolete.ts"
}
})
const toolMessages = body.messages.filter((message: any) => message.role === "tool")
const patchOutput = toolMessages.find((message: any) => message.tool_call_id === "patch_call_1")
assert.isDefined(patchOutput)
assert.strictEqual(patchOutput.content, "deleted")
}))
})
describe("error mapping", () => {
it.effect("maps 400 responses to InvalidRequestError", () =>
Effect.gen(function*() {
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, 400, {
error: {
message: "Bad request",
type: "invalid_request_error",
code: null
}
}))
)
))
)
const error = yield* client.createResponse({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }]
}).pipe(Effect.flip)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.method, "createResponse")
assert.strictEqual(error.reason._tag, "InvalidRequestError")
}))
it.effect("maps insufficient quota errors to QuotaExhaustedError", () =>
Effect.gen(function*() {
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-key")
}).pipe(
Effect.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, 429, {
error: {
message: "You exceeded your current quota",
type: "insufficient_quota",
code: "insufficient_quota"
}
}))
)
))
)
const error = yield* client.createResponse({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hello" }]
}).pipe(Effect.flip)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.method, "createResponse")
assert.strictEqual(error.reason._tag, "QuotaExhaustedError")
}))
})
})
const makeHttpClient = (
handler: (
request: HttpClientRequest.HttpClientRequest
) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError>
) =>
HttpClient.makeWith(
Effect.fnUntraced(function*(requestEffect) {
const request = yield* requestEffect
return yield* handler(request)
}),
Effect.succeed as HttpClient.HttpClient.Preprocess<HttpClientError.HttpClientError, never>
)
const makeChatCompletion = () => ({
id: "chatcmpl_test_1",
object: "chat.completion",
model: "gpt-4o-mini",
created: 1,
choices: [{
index: 0,
finish_reason: "stop",
message: {
role: "assistant",
content: "Hello"
}
}],
usage: {
prompt_tokens: 1,
completion_tokens: 1,
total_tokens: 2
}
})
const jsonResponse = (
request: HttpClientRequest.HttpClientRequest,
status: number,
body: unknown
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(body), {
status,
headers: {
"content-type": "application/json"
}
})
)
const sseResponse = (
request: HttpClientRequest.HttpClientRequest,
events: ReadonlyArray<unknown>
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(toSseBody(events), {
status: 200,
headers: {
"content-type": "text/event-stream"
}
})
)
const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
Effect.gen(function*() {
const body = request.body
if (body._tag === "Uint8Array") {
const text = new TextDecoder().decode(body.body)
return JSON.parse(text)
}
return yield* Effect.die(new Error("Expected Uint8Array body"))
})
const toSseBody = (events: ReadonlyArray<unknown>): string =>
events.map((event) => {
if (typeof event === "string") {
return `data: ${event}\n\n`
}
return `data: ${JSON.stringify(event)}\n\n`
}).join("")

View File

@@ -0,0 +1,216 @@
import { OpenAiClient, OpenAiEmbeddingModel } from "@effect/ai-openai-compat"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Layer, Redacted } from "effect"
import { EmbeddingModel } from "effect/unstable/ai"
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
describe("OpenAiEmbeddingModel", () => {
it.effect("model provides dimensions service", () =>
Effect.gen(function*() {
const dimensions = yield* EmbeddingModel.Dimensions
assert.strictEqual(dimensions, 1536)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.model("text-embedding-3-small", { dimensions: 1536 })),
Effect.provideService(OpenAiClient.OpenAiClient, noopOpenAiClient)
))
it.effect("reorders embeddings by provider index", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
data: [
{ index: 1, embedding: [20, 21] },
{ index: 0, embedding: [10, 11] }
],
model: "text-embedding-3-small",
usage: {
prompt_tokens: 7,
total_tokens: 7
}
}))
})
))
)
const response = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["first", "second"])
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.deepStrictEqual(response.embeddings.map((embedding) => embedding.vector), [[10, 11], [20, 21]])
assert.strictEqual(response.usage.inputTokens, 7)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const requestBody = yield* getRequestBody(capturedRequest)
assert.strictEqual(requestBody.model, "text-embedding-3-small")
assert.deepStrictEqual(requestBody.input, ["first", "second"])
}))
it.effect("merges config and applies withConfigOverride precedence", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
data: [{ index: 0, embedding: [1, 2, 3] }],
model: "override-model"
}))
})
))
)
yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
yield* model.embed("hello")
}).pipe(
OpenAiEmbeddingModel.withConfigOverride({
model: "override-model",
dimensions: 1024,
user: "request-user"
}),
Effect.provide(OpenAiEmbeddingModel.layer({
model: "base-model",
config: {
dimensions: 256,
user: "provider-user"
}
})),
Effect.provide(clientLayer)
)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const requestBody = yield* getRequestBody(capturedRequest)
assert.strictEqual(requestBody.model, "override-model")
assert.strictEqual(requestBody.dimensions, 1024)
assert.strictEqual(requestBody.user, "request-user")
assert.deepStrictEqual(requestBody.input, ["hello"])
}))
it.effect("fails with InvalidOutputError when provider returns duplicate indices", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [
{ index: 0, embedding: [1] },
{ index: 0, embedding: [2] }
],
model: "text-embedding-3-small",
usage: {
prompt_tokens: 2,
total_tokens: 2
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["a", "b"]).pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
it.effect("fails with InvalidOutputError when provider returns non-vector embedding payload", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [{ index: 0, embedding: "AQID" }],
model: "text-embedding-3-small",
usage: {
prompt_tokens: 1,
total_tokens: 1
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embed("a").pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
})
const makeHttpClient = (
handler: (
request: HttpClientRequest.HttpClientRequest
) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError>
) =>
HttpClient.makeWith(
Effect.fnUntraced(function*(requestEffect) {
const request = yield* requestEffect
return yield* handler(request)
}),
Effect.succeed as HttpClient.HttpClient.Preprocess<HttpClientError.HttpClientError, never>
)
const jsonResponse = (
request: HttpClientRequest.HttpClientRequest,
body: unknown
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(body), {
status: 200,
headers: {
"content-type": "application/json"
}
})
)
const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
Effect.gen(function*() {
const body = request.body
if (body._tag === "Uint8Array") {
const text = new TextDecoder().decode(body.body)
return JSON.parse(text)
}
return yield* Effect.die(new Error("Expected Uint8Array body"))
})
const noopOpenAiClient: OpenAiClient.Service = {
client: undefined as unknown as OpenAiClient.Service["client"],
createResponse: () => Effect.die(new Error("noop")),
createResponseStream: () => Effect.die(new Error("noop")),
createEmbedding: () => Effect.die(new Error("noop"))
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"references": [
{ "path": "../../effect" }
]
}

View File

@@ -0,0 +1,26 @@
import { OpenAiEmbeddingModel, OpenAiLanguageModel } from "@effect/ai-openai-compat"
import { describe, expect, it } from "tstyche"
describe("OpenAI-compatible config", () => {
it("accepts custom language model properties while preserving known property types", () => {
expect(OpenAiLanguageModel.model).type.toBeCallableWith("model", {
temperature: 0.5,
vendor_setting: true
})
expect(OpenAiLanguageModel.model).type.not.toBeCallableWith("model", {
temperature: "high"
})
})
it("accepts custom embedding properties while preserving known property types", () => {
expect(OpenAiEmbeddingModel.model).type.toBeCallableWith("model", {
dimensions: 3,
user: "test-user",
vendor_setting: true
})
expect(OpenAiEmbeddingModel.model).type.not.toBeCallableWith("model", {
dimensions: 3,
user: 1
})
})
})

View File

@@ -0,0 +1,6 @@
import { mergeConfig, type ViteUserConfig } from "vitest/config"
import shared from "../../../vitest.shared.ts"
const config: ViteUserConfig = {}
export default mergeConfig(shared, config)

View File

@@ -0,0 +1,780 @@
# @effect/ai-openai
## 4.0.0-beta.99
### Patch Changes
- Updated dependencies [[`8ce4795`](https://github.com/Effect-TS/effect/commit/8ce4795ccbaebca4292757db568c005a992546a4), [`80b539f`](https://github.com/Effect-TS/effect/commit/80b539f8aba68f478c75c35c2b4140c4ffc4fada), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`e6e6dba`](https://github.com/Effect-TS/effect/commit/e6e6dba6e9d86e7c2ad27dcedf289db76a19697f), [`bfb203e`](https://github.com/Effect-TS/effect/commit/bfb203e95aa439f731acad37fc3a9a831a190f1c), [`2e9a34a`](https://github.com/Effect-TS/effect/commit/2e9a34ac2bece4f3a206160480c991e3841dc67a), [`55d4eb3`](https://github.com/Effect-TS/effect/commit/55d4eb34f2c64d54f6a25a305b5c5438ebd7934e), [`bddb010`](https://github.com/Effect-TS/effect/commit/bddb010eac3d4436cb094edbbee7460c5440c162), [`a328835`](https://github.com/Effect-TS/effect/commit/a328835e50d76bc96648a1c1550456e8c9f81210), [`5560d05`](https://github.com/Effect-TS/effect/commit/5560d05aa6abdd29466d9c3412cc5e648b0adbde), [`8f6e3ad`](https://github.com/Effect-TS/effect/commit/8f6e3adb185b16e8820b98c509b308086f7ff1af), [`46997fa`](https://github.com/Effect-TS/effect/commit/46997fa60401f5e3c93daa4b61f7df8e31caaab4), [`9e6e12d`](https://github.com/Effect-TS/effect/commit/9e6e12d75c118cd265496f2880490d1f33a5c8bf), [`3394b93`](https://github.com/Effect-TS/effect/commit/3394b93d97d6f24fc38670641d1490289ffca7f1), [`febeabc`](https://github.com/Effect-TS/effect/commit/febeabc3f7c31094da000a23edeaabfe2ab00a38), [`54161c9`](https://github.com/Effect-TS/effect/commit/54161c98f6f3569e0c31842f54e6a257f9421c4c), [`385f7a4`](https://github.com/Effect-TS/effect/commit/385f7a4ee4a7359928597ea56d151dbaf5eb5802), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd), [`44b9cf3`](https://github.com/Effect-TS/effect/commit/44b9cf3d240d726997b4bbcd0ede48e074d3c456), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`0a8aa6a`](https://github.com/Effect-TS/effect/commit/0a8aa6acb90a72b91c24d17133c950e4cacd8abd), [`c8d9fcf`](https://github.com/Effect-TS/effect/commit/c8d9fcf7b030f7c474effbab2764ce7aee1c7209), [`9ca7f9a`](https://github.com/Effect-TS/effect/commit/9ca7f9a69363e4485645966d5a93b8f9597c5206), [`e7aca89`](https://github.com/Effect-TS/effect/commit/e7aca894bb32fbb785b5830837e6061c415a6015), [`55d7560`](https://github.com/Effect-TS/effect/commit/55d75609b8acf8a1b54c1b1c7fbbb65ec741aa3e), [`f809189`](https://github.com/Effect-TS/effect/commit/f809189ddf6b6011ba43a9901baaa734e315da2a), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`0ebdbe7`](https://github.com/Effect-TS/effect/commit/0ebdbe74463dc84385956d0b1e8c2b79ebab5400), [`7517d09`](https://github.com/Effect-TS/effect/commit/7517d09f12a0b183a81bd425962c4e280a68b05d), [`212493b`](https://github.com/Effect-TS/effect/commit/212493b9a1eb98cd1ef6959c707a2e5784a5ae91), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`80ea8cb`](https://github.com/Effect-TS/effect/commit/80ea8cb9222ca73f564c8267ab2f82966fea027a), [`8df19f4`](https://github.com/Effect-TS/effect/commit/8df19f4fe81d90cc33ace88b9a77e5534f82d604)]:
- effect@4.0.0-beta.99
## 4.0.0-beta.98
### Patch Changes
- Updated dependencies [[`989603b`](https://github.com/Effect-TS/effect-smol/commit/989603b60ab1197b64acf214208e0d370cd1f842), [`214c458`](https://github.com/Effect-TS/effect-smol/commit/214c458084bb6995d543cd37d1055f24be3d454e), [`a037273`](https://github.com/Effect-TS/effect-smol/commit/a0372736ac34796969b051bbba4717d7983f1ebe), [`97fdaa9`](https://github.com/Effect-TS/effect-smol/commit/97fdaa9c1f522c65e579365d314a07878e2b904f), [`b24d248`](https://github.com/Effect-TS/effect-smol/commit/b24d248c8df44222ce642087cde2bd859a2dc709), [`19c222c`](https://github.com/Effect-TS/effect-smol/commit/19c222cac2353a3d7b7733caecb00556fffe9a5c), [`eec85dd`](https://github.com/Effect-TS/effect-smol/commit/eec85ddba09ea326fd268ee33eeffd47e50d4671), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`8849052`](https://github.com/Effect-TS/effect-smol/commit/884905232d1e9a365e046d8dde27bf9c5707f57f), [`c15e16a`](https://github.com/Effect-TS/effect-smol/commit/c15e16ad130d1fbde25d912b7ac55995066cb35b), [`01d00a3`](https://github.com/Effect-TS/effect-smol/commit/01d00a3abfbf1f37996cdbe738ea5137c646cdd7), [`8bd4589`](https://github.com/Effect-TS/effect-smol/commit/8bd458975a1b3a8ed042eccf317b93d28ded91e7), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`6e08428`](https://github.com/Effect-TS/effect-smol/commit/6e08428d980501b856f846ad3f3f0e4ea46e7786), [`388dcf9`](https://github.com/Effect-TS/effect-smol/commit/388dcf953f65d317547f34d40e6443c5f264205f), [`2b7ce2b`](https://github.com/Effect-TS/effect-smol/commit/2b7ce2b513e7ec2a77822f1116dc6ffb6ba93f4e), [`87bea7e`](https://github.com/Effect-TS/effect-smol/commit/87bea7e16259246f3bcdf565446394751abca953), [`ce38dc3`](https://github.com/Effect-TS/effect-smol/commit/ce38dc33bda805a684432cca071f4dc3c6b9a1ba), [`a807cd1`](https://github.com/Effect-TS/effect-smol/commit/a807cd170341deca8a1cfb52c4222585f2431bb9), [`fd8a356`](https://github.com/Effect-TS/effect-smol/commit/fd8a356f06a8c9ce4e7e0a13fc4021c178ed31de), [`c2a5edc`](https://github.com/Effect-TS/effect-smol/commit/c2a5edc3abd31ad5bc123362bc1213e03e4095c3), [`5946da3`](https://github.com/Effect-TS/effect-smol/commit/5946da3804a1be5e752b05b96bd058cdba50a1bf), [`4ae0c5f`](https://github.com/Effect-TS/effect-smol/commit/4ae0c5ffcbe6c56ddfcb05c639112a079483539e), [`5b2a0bc`](https://github.com/Effect-TS/effect-smol/commit/5b2a0bceea3a28a33a58555210c90a415dc74a76), [`72ac585`](https://github.com/Effect-TS/effect-smol/commit/72ac585884befde6af9208da738699a93f1bae79), [`5e8c1b8`](https://github.com/Effect-TS/effect-smol/commit/5e8c1b82bfafa121311f987a49ab75395e3647a7), [`0f9c078`](https://github.com/Effect-TS/effect-smol/commit/0f9c07841b04183f485ee6e6458de73b290b09f5)]:
- effect@4.0.0-beta.98
## 4.0.0-beta.97
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.97
## 4.0.0-beta.96
### Patch Changes
- Updated dependencies [[`1503f45`](https://github.com/Effect-TS/effect-smol/commit/1503f45cb5bb2a74f4705252ec505a1f0ade7e62), [`57fe793`](https://github.com/Effect-TS/effect-smol/commit/57fe79316ffbc380b30626a168981fb26ae97459), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`97f29df`](https://github.com/Effect-TS/effect-smol/commit/97f29df457f7ffd07cfb4b379315c12c086af805)]:
- effect@4.0.0-beta.96
## 4.0.0-beta.95
### Patch Changes
- Updated dependencies [[`a482442`](https://github.com/Effect-TS/effect-smol/commit/a482442abdeb490e9652b854ec3495e4aa7273e7), [`fbefa85`](https://github.com/Effect-TS/effect-smol/commit/fbefa850fab2f0a302c20614496aeaaa2a8b5590), [`0b4a32f`](https://github.com/Effect-TS/effect-smol/commit/0b4a32f4260f0d8500942a133001b0d349328102), [`18a49e1`](https://github.com/Effect-TS/effect-smol/commit/18a49e1786679456258002ff9397faf02f678c2d), [`266cb90`](https://github.com/Effect-TS/effect-smol/commit/266cb90bb2c17aabc40563c32db334f09ba3d74b), [`912f095`](https://github.com/Effect-TS/effect-smol/commit/912f095a34572bbd3cedf6edb27878443e3e4a95), [`a6718f9`](https://github.com/Effect-TS/effect-smol/commit/a6718f9e00a15ca903b0732da46116cbf3d6aca7), [`bef5154`](https://github.com/Effect-TS/effect-smol/commit/bef51540a243aa2f872a00c01d0cd58b7a769baa), [`18e0564`](https://github.com/Effect-TS/effect-smol/commit/18e0564bd0f8ebbdfcaf1e2c21529948e9e4a81d), [`fb50f14`](https://github.com/Effect-TS/effect-smol/commit/fb50f14fc3657c1973785aa5b72ecf0b0d28e0b2)]:
- effect@4.0.0-beta.95
## 4.0.0-beta.94
### Patch Changes
- Updated dependencies [[`95a0e9b`](https://github.com/Effect-TS/effect-smol/commit/95a0e9bb62797af0e81c9998773405f248f218c5), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63), [`f11ce73`](https://github.com/Effect-TS/effect-smol/commit/f11ce73af60823754dc24194f4ffc561b9ea1c2d), [`ff30b6e`](https://github.com/Effect-TS/effect-smol/commit/ff30b6e7c2c63ffc56a4c5818d6d86b01b5ad528), [`1caab3c`](https://github.com/Effect-TS/effect-smol/commit/1caab3cc30f626efbf15e59d74f539a487e5c85c), [`aa80c47`](https://github.com/Effect-TS/effect-smol/commit/aa80c4775a04db87553e5568764cab7e32a72814), [`c2ae4fc`](https://github.com/Effect-TS/effect-smol/commit/c2ae4fce2f03a4cd1861c2b1179da7df656e662d), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63)]:
- effect@4.0.0-beta.94
## 4.0.0-beta.93
### Patch Changes
- Updated dependencies [[`00652fe`](https://github.com/Effect-TS/effect-smol/commit/00652fe95c18f87208e91343eb8bf218faa2f677), [`6c58167`](https://github.com/Effect-TS/effect-smol/commit/6c5816746eaf91d2a3c7c899c5720809fa230ae3), [`2bc5415`](https://github.com/Effect-TS/effect-smol/commit/2bc541501a7ef89e542d7cb98e96beb53cd205cc), [`e11cccc`](https://github.com/Effect-TS/effect-smol/commit/e11cccc7d5fe631abccc7d6e3bd296938de0fa2e), [`ba7e77e`](https://github.com/Effect-TS/effect-smol/commit/ba7e77e046b8641a3a4e9750bb88ca4a1d063d3f), [`5713ee7`](https://github.com/Effect-TS/effect-smol/commit/5713ee7edbc3054efde407b2286bbfd45bbc6e1c)]:
- effect@4.0.0-beta.93
## 4.0.0-beta.92
### Patch Changes
- Updated dependencies [[`affdc13`](https://github.com/Effect-TS/effect-smol/commit/affdc139045cc325dce321a84a580fdc1b2da7b9)]:
- effect@4.0.0-beta.92
## 4.0.0-beta.91
### Patch Changes
- Updated dependencies [[`b135b25`](https://github.com/Effect-TS/effect-smol/commit/b135b2517fca9e7839734ace3699a7dfa75b9075), [`aaa21a3`](https://github.com/Effect-TS/effect-smol/commit/aaa21a369a171c600db294f2a4f640583043e150), [`3475ee6`](https://github.com/Effect-TS/effect-smol/commit/3475ee6c2bda6b05c6d7a12ce30c8bb840b5b1a6)]:
- effect@4.0.0-beta.91
## 4.0.0-beta.90
### Patch Changes
- Updated dependencies [[`d237fdf`](https://github.com/Effect-TS/effect-smol/commit/d237fdf726481f76eb52a6196e111b24122bc3d5)]:
- effect@4.0.0-beta.90
## 4.0.0-beta.89
### Patch Changes
- Updated dependencies [[`b7d46ab`](https://github.com/Effect-TS/effect-smol/commit/b7d46ab7e1a29d8711817bab583c9febf48a0dad), [`7777e15`](https://github.com/Effect-TS/effect-smol/commit/7777e1540fd3680dd8346723cffec812b9384669), [`5376197`](https://github.com/Effect-TS/effect-smol/commit/5376197ca8e50358a41b1fd3cec27bd1ec680ec6)]:
- effect@4.0.0-beta.89
## 4.0.0-beta.88
### Patch Changes
- Updated dependencies [[`911f1b8`](https://github.com/Effect-TS/effect-smol/commit/911f1b84790ce42b3a70c95b33e6f6fd9e74de8b), [`8beeeea`](https://github.com/Effect-TS/effect-smol/commit/8beeeea52879d8613a39468848f01c3092bd54d4), [`c306fcf`](https://github.com/Effect-TS/effect-smol/commit/c306fcfeb1ef38455156932a1faf49292b1318da)]:
- effect@4.0.0-beta.88
## 4.0.0-beta.87
### Patch Changes
- Updated dependencies [[`5a0c1a4`](https://github.com/Effect-TS/effect-smol/commit/5a0c1a4faee5707b5cc35e646ff1ffdad70f1956), [`1eea2ea`](https://github.com/Effect-TS/effect-smol/commit/1eea2ea3795ba47316b82b1ac8d4612c0ba389ed)]:
- effect@4.0.0-beta.87
## 4.0.0-beta.86
### Patch Changes
- Updated dependencies [[`0b5795a`](https://github.com/Effect-TS/effect-smol/commit/0b5795a0ab4395e8f15955d8d96f2303084bfc64), [`3e3a859`](https://github.com/Effect-TS/effect-smol/commit/3e3a859ec6351a9e0d31674aabbd48fcefabb12e), [`7dbec24`](https://github.com/Effect-TS/effect-smol/commit/7dbec240dbf3bca599a20c486632abce694ef5ab), [`d8c00a1`](https://github.com/Effect-TS/effect-smol/commit/d8c00a171ac7141e8adc08c332d1162d9a9d56fc), [`85b6317`](https://github.com/Effect-TS/effect-smol/commit/85b631701e935866f2762bd595237aa718370cd9), [`6d0fda0`](https://github.com/Effect-TS/effect-smol/commit/6d0fda0d0cbdfffc523c89c57dfdb1608f84fb12), [`108a933`](https://github.com/Effect-TS/effect-smol/commit/108a9335ff8571928197e5847a09c28ac83d6f46), [`7e1f455`](https://github.com/Effect-TS/effect-smol/commit/7e1f455fab5005d769b939c91e519d450f802cf9), [`46b3e79`](https://github.com/Effect-TS/effect-smol/commit/46b3e79944cfdae7901eb148135c85b7eb39834e)]:
- effect@4.0.0-beta.86
## 4.0.0-beta.85
### Patch Changes
- Updated dependencies [[`328d97c`](https://github.com/Effect-TS/effect-smol/commit/328d97cc53c0dcb89077a5623e35b095eaa59a8c), [`8441836`](https://github.com/Effect-TS/effect-smol/commit/8441836e6dde70e8ae2126be9cefe9b45798b134), [`074e436`](https://github.com/Effect-TS/effect-smol/commit/074e4361091289104cb0ab6959dc3b0ea7794a6a), [`c1dfd60`](https://github.com/Effect-TS/effect-smol/commit/c1dfd60663eb13a58916f3712d877499943b628a), [`2ba316b`](https://github.com/Effect-TS/effect-smol/commit/2ba316bd15fcbf1c50626500d44a2c9b3bec19f5), [`7ce7344`](https://github.com/Effect-TS/effect-smol/commit/7ce7344c41056c79e2ee19ee6a9346c0f1d227c1)]:
- effect@4.0.0-beta.85
## 4.0.0-beta.84
### Patch Changes
- Updated dependencies [[`87f52ba`](https://github.com/Effect-TS/effect-smol/commit/87f52ba16c4370ffa3f84bf8e53038e1419c284e), [`b8ee07f`](https://github.com/Effect-TS/effect-smol/commit/b8ee07ffda8903b5ec2e45a786ddcba59f128fda), [`867c0d7`](https://github.com/Effect-TS/effect-smol/commit/867c0d70a09079b040260d45a1e92ff04dbfbf2f), [`b93bc6c`](https://github.com/Effect-TS/effect-smol/commit/b93bc6c9cb27b909a41d094c97c4f9d25bbc6d6b), [`57d387f`](https://github.com/Effect-TS/effect-smol/commit/57d387f92c30ab63e15e3e641f0a903b65886610), [`bacca41`](https://github.com/Effect-TS/effect-smol/commit/bacca4141c2400effae1eabfdb36c89a459cf246), [`0f8ac79`](https://github.com/Effect-TS/effect-smol/commit/0f8ac7959d29ed68c68ce25aabd6bf0cb7e63ecc), [`25b4482`](https://github.com/Effect-TS/effect-smol/commit/25b448270c01317703f25107e1480d4cd0246d9a), [`9cf3a25`](https://github.com/Effect-TS/effect-smol/commit/9cf3a25c66b0c44a52be9829870c44517ea52db2), [`8def767`](https://github.com/Effect-TS/effect-smol/commit/8def7674b1787f91035298cda4d122937e87ef72)]:
- effect@4.0.0-beta.84
## 4.0.0-beta.83
### Patch Changes
- Updated dependencies [[`1f2e8ce`](https://github.com/Effect-TS/effect-smol/commit/1f2e8ceef09e0a791c850ed2ade01f97089596f9)]:
- effect@4.0.0-beta.83
## 4.0.0-beta.82
### Patch Changes
- Updated dependencies [[`193690b`](https://github.com/Effect-TS/effect-smol/commit/193690b642ea802bbed40d663bd677251bbe9dc3)]:
- effect@4.0.0-beta.82
## 4.0.0-beta.81
### Patch Changes
- Updated dependencies [[`93cb4f8`](https://github.com/Effect-TS/effect-smol/commit/93cb4f8fbfb9e07cb9dc86ce6b155fd1f8167914), [`60341d9`](https://github.com/Effect-TS/effect-smol/commit/60341d9ca744d0473ce3fab621ca9bd225af3a39), [`1105ab5`](https://github.com/Effect-TS/effect-smol/commit/1105ab56cb724212f7ea7b431396ce82e8fd0484), [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36)]:
- effect@4.0.0-beta.81
## 4.0.0-beta.80
### Patch Changes
- Updated dependencies [[`d944330`](https://github.com/Effect-TS/effect-smol/commit/d94433090ee03f426d43e13b883abae4494e55e6), [`f48659f`](https://github.com/Effect-TS/effect-smol/commit/f48659fdcc84930ebc1e5b45b540c0f973389182), [`7652aaa`](https://github.com/Effect-TS/effect-smol/commit/7652aaa3bdbc39f241fe58b54b9a43b713e22e12), [`98630b7`](https://github.com/Effect-TS/effect-smol/commit/98630b7c8f679c352ba6796636c85688fa009d8d), [`90ae23c`](https://github.com/Effect-TS/effect-smol/commit/90ae23cf07284da5e1bcd9dffa882e85df7e617b)]:
- effect@4.0.0-beta.80
## 4.0.0-beta.79
### Patch Changes
- Updated dependencies [[`b9704dc`](https://github.com/Effect-TS/effect-smol/commit/b9704dc9de9f1649ad502371014fe869b69a49a3), [`a207113`](https://github.com/Effect-TS/effect-smol/commit/a207113f66837bb54416926718a9a7d66774d079), [`5e9b9e2`](https://github.com/Effect-TS/effect-smol/commit/5e9b9e217b164ebfd4a002dd4380b3b1563200c3), [`7c128ae`](https://github.com/Effect-TS/effect-smol/commit/7c128aef458a1e2d224712e51c483c9badad1d44), [`0ada457`](https://github.com/Effect-TS/effect-smol/commit/0ada457c0513d8d908254ab77ebb7d29d2b523d6), [`d7cc5a2`](https://github.com/Effect-TS/effect-smol/commit/d7cc5a2bede3de10943aa0c6bdb4f26836a91efd), [`aad63be`](https://github.com/Effect-TS/effect-smol/commit/aad63becf65e0a6b076e94f8973be7bbe7fbd46f), [`09809f6`](https://github.com/Effect-TS/effect-smol/commit/09809f60f19ec98232f98b33e33e02ecb7e4fbd6), [`2fddda5`](https://github.com/Effect-TS/effect-smol/commit/2fddda5311929f46b61e503f0ade4fc749e8c77d), [`5f21768`](https://github.com/Effect-TS/effect-smol/commit/5f2176833399757c4500d8875b7f2fba0393de75), [`f27003e`](https://github.com/Effect-TS/effect-smol/commit/f27003e00524ff83f20dd9909f62b2f8795efe03)]:
- effect@4.0.0-beta.79
## 4.0.0-beta.78
### Patch Changes
- Updated dependencies [[`7836b8e`](https://github.com/Effect-TS/effect-smol/commit/7836b8eb8bb0f3e04cdf554ee070caccf74f00c1), [`35d49a3`](https://github.com/Effect-TS/effect-smol/commit/35d49a3a09bdba6b513de87ddcead9e61a1042ba), [`4093258`](https://github.com/Effect-TS/effect-smol/commit/40932580e65bafab5f23c5f14b520cb411d0b2cd)]:
- effect@4.0.0-beta.78
## 4.0.0-beta.77
### Patch Changes
- Updated dependencies [[`6e9a5ca`](https://github.com/Effect-TS/effect-smol/commit/6e9a5ca62a61156fd67b2518ad3ab14ac0d25f23), [`302f398`](https://github.com/Effect-TS/effect-smol/commit/302f3984ce206e35d86ddd99d3b72be144850a51)]:
- effect@4.0.0-beta.77
## 4.0.0-beta.76
### Patch Changes
- Updated dependencies [[`016108a`](https://github.com/Effect-TS/effect-smol/commit/016108a472af7048ddbbfd05f233e67529fafe12), [`95c03d2`](https://github.com/Effect-TS/effect-smol/commit/95c03d2c55930668c215b5a41c23cf7742fead84), [`07299a3`](https://github.com/Effect-TS/effect-smol/commit/07299a33c09fd52faa9810d30835a2622c752386)]:
- effect@4.0.0-beta.76
## 4.0.0-beta.75
### Patch Changes
- [#2312](https://github.com/Effect-TS/effect-smol/pull/2312) [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e) Thanks @gcanti! - Validate `Schema.StructWithRest` fixed fields against rest index signatures at the type level so schemas cannot be constructed with incompatible decoded, encoded, or make shapes. This keeps `StructWithRest` types sound and updates the generated OpenAI conversation-items request schema to keep accepting arbitrary additional fields under the stricter validation.
- Updated dependencies [[`81b187c`](https://github.com/Effect-TS/effect-smol/commit/81b187c17a0d8817b58232826939154010ae49d7), [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e), [`a29c2e7`](https://github.com/Effect-TS/effect-smol/commit/a29c2e7e3570920156702671d6f3367cd0195f6c), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`ffea4ec`](https://github.com/Effect-TS/effect-smol/commit/ffea4ecf2925f6a4c9fd13079d47584cbf2bed00), [`4255c9b`](https://github.com/Effect-TS/effect-smol/commit/4255c9ba78bb98c7838fbe9dccdd8465e9da5427)]:
- effect@4.0.0-beta.75
## 4.0.0-beta.74
### Patch Changes
- Updated dependencies [[`b1fc6a4`](https://github.com/Effect-TS/effect-smol/commit/b1fc6a4b4d0ca7fa9fd162799ae17c86f2f7ee8e)]:
- effect@4.0.0-beta.74
## 4.0.0-beta.73
### Patch Changes
- Updated dependencies [[`361ca30`](https://github.com/Effect-TS/effect-smol/commit/361ca30eb6e134feece547d6e00f82be4cb23f75), [`b9598c6`](https://github.com/Effect-TS/effect-smol/commit/b9598c6a209e75bfdb87ee3b024ecd1e3923ff6e)]:
- effect@4.0.0-beta.73
## 4.0.0-beta.72
### Patch Changes
- [#2280](https://github.com/Effect-TS/effect-smol/pull/2280) [`fa07f9e`](https://github.com/Effect-TS/effect-smol/commit/fa07f9e7138bde3e9404aa91c22b41e273aba091) Thanks @tim-smart! - improve openai websocket error status
- Updated dependencies [[`73e67d1`](https://github.com/Effect-TS/effect-smol/commit/73e67d119a84d697773eaecb4865c6a71eb1a9cb), [`01d71ec`](https://github.com/Effect-TS/effect-smol/commit/01d71ec5a75f3c2747a8d3b1ad9701d1e27b7ce5), [`fcd707e`](https://github.com/Effect-TS/effect-smol/commit/fcd707e091a16e1b35343c901cc4052274e32239)]:
- effect@4.0.0-beta.72
## 4.0.0-beta.71
### Patch Changes
- [#2279](https://github.com/Effect-TS/effect-smol/pull/2279) [`1fab4ac`](https://github.com/Effect-TS/effect-smol/commit/1fab4acc63818dece3a0e732b1a71843f0759f21) Thanks @tim-smart! - handle missing output array in openai responses
- Updated dependencies [[`d8ac76b`](https://github.com/Effect-TS/effect-smol/commit/d8ac76b5bad458c42cebe8a0c1b3843f955ac293), [`2c3c00a`](https://github.com/Effect-TS/effect-smol/commit/2c3c00af6faba7b7d422af26a7a2bbc35636d230), [`3751e7c`](https://github.com/Effect-TS/effect-smol/commit/3751e7cf353e7a54cd692c37401207d9afba1e63), [`fc5f25b`](https://github.com/Effect-TS/effect-smol/commit/fc5f25b03ada5fc2431987768a74d3d3e75ca485), [`7ccced4`](https://github.com/Effect-TS/effect-smol/commit/7ccced42867c14c013b01160b3d292f14c05bd04), [`a2e1fe5`](https://github.com/Effect-TS/effect-smol/commit/a2e1fe5835c98c8ee4393a091b1d11b75126e349), [`4a4a36b`](https://github.com/Effect-TS/effect-smol/commit/4a4a36b10e6e616cad07584a43908f6a7e07e618), [`d350292`](https://github.com/Effect-TS/effect-smol/commit/d3502922b4740fa9d745797cbc3775cb67839b6d), [`730afb6`](https://github.com/Effect-TS/effect-smol/commit/730afb66696adf9bd5a328cbca29df9c05968771), [`df1b008`](https://github.com/Effect-TS/effect-smol/commit/df1b008f370f414c2a67a7b8139ef747af8e5fba), [`6d469d5`](https://github.com/Effect-TS/effect-smol/commit/6d469d567a7c41d7e5343bdee21d45b07b0e8190)]:
- effect@4.0.0-beta.71
## 4.0.0-beta.70
### Patch Changes
- [#2224](https://github.com/Effect-TS/effect-smol/pull/2224) [`74d8f37`](https://github.com/Effect-TS/effect-smol/commit/74d8f37a05ce755ff09cff48f992cb80ce9e4769) Thanks @aniravi24! - Fix `OpenAIFile` schema decode failure on responses where `expires_at` and `status_details` are returned as literal `null`. The OpenAI files endpoint returns `null` (not omitted) for these fields when no expiration / status detail applies (e.g. uploads with `purpose: "user_data"`), but the upstream OpenAPI spec marks them only as optional. Codegen patches widen both fields to allow `null`, which now decodes cleanly via `OpenAiClient.createFile`, `retrieveFile`, `listFiles`, and any other endpoint returning the `OpenAIFile` shape.
- Updated dependencies [[`af7782d`](https://github.com/Effect-TS/effect-smol/commit/af7782d3008d08b043f3a3f261516001514b2b4e), [`7212d70`](https://github.com/Effect-TS/effect-smol/commit/7212d701a3eee7b3553ff502e2c066126e52e839)]:
- effect@4.0.0-beta.70
## 4.0.0-beta.69
### Patch Changes
- Updated dependencies [[`70ea04a`](https://github.com/Effect-TS/effect-smol/commit/70ea04aa96a2a7859d738d414e1f0e3ed081a27a), [`d0ea8b0`](https://github.com/Effect-TS/effect-smol/commit/d0ea8b03f7d73ae076c1db12666141e480d11178), [`a57674b`](https://github.com/Effect-TS/effect-smol/commit/a57674b64845e9e75a456cf907bfdcb858859118), [`59aa334`](https://github.com/Effect-TS/effect-smol/commit/59aa334fbd0a504dda3c36f6d2ef1be7449b4b8b), [`8f4208e`](https://github.com/Effect-TS/effect-smol/commit/8f4208ee83bc7bdaa6793b5429847b45aab72470)]:
- effect@4.0.0-beta.69
## 4.0.0-beta.68
### Patch Changes
- Updated dependencies [[`af8267f`](https://github.com/Effect-TS/effect-smol/commit/af8267f2f3588c3fb611e9286f6f933f29ce1217), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`f136bb7`](https://github.com/Effect-TS/effect-smol/commit/f136bb763048cbc6b17edd26496dba3e2415b9fa), [`6f38f07`](https://github.com/Effect-TS/effect-smol/commit/6f38f07d5941a211b251383aaab0f4f55e8a6557), [`aec9c40`](https://github.com/Effect-TS/effect-smol/commit/aec9c401a53db227f18bf5e0c84db7130ad862d6)]:
- effect@4.0.0-beta.68
## 4.0.0-beta.67
### Patch Changes
- Updated dependencies [[`a42ef66`](https://github.com/Effect-TS/effect-smol/commit/a42ef6632abbddfa820995ae310ccc84ae8d9b6f), [`35594f8`](https://github.com/Effect-TS/effect-smol/commit/35594f811cafe471acd490114b103a1f8392c8d8), [`8bddd62`](https://github.com/Effect-TS/effect-smol/commit/8bddd628cb623f9533d345082583ff51cead6836), [`4be4c8d`](https://github.com/Effect-TS/effect-smol/commit/4be4c8d60862aa963869ee2ed9ffa048ffac0527), [`0c9d3ab`](https://github.com/Effect-TS/effect-smol/commit/0c9d3ab43eb721a370ed8306260cbac218c27e87), [`b156acc`](https://github.com/Effect-TS/effect-smol/commit/b156accd2691b4a051f823affdece7c39923ce85), [`d16c034`](https://github.com/Effect-TS/effect-smol/commit/d16c03434ee3e6dcd3bfc82b65d99e881d89025b), [`b559d68`](https://github.com/Effect-TS/effect-smol/commit/b559d68845f848a10153395778f035682d399075), [`a3de5d9`](https://github.com/Effect-TS/effect-smol/commit/a3de5d9215e5cc4a62e2666efbd7c1bf595eb84f), [`7e6c12e`](https://github.com/Effect-TS/effect-smol/commit/7e6c12ec9b3a5945f6c26e272cc8f6390541ad3e), [`098167a`](https://github.com/Effect-TS/effect-smol/commit/098167a220fe07da6f14455818733ab1b269c9dd)]:
- effect@4.0.0-beta.67
## 4.0.0-beta.66
### Patch Changes
- Updated dependencies [[`ca2498e`](https://github.com/Effect-TS/effect-smol/commit/ca2498e702ac2d83fb7187707b7eb069bdb261a2), [`cd7d1fb`](https://github.com/Effect-TS/effect-smol/commit/cd7d1fba7e2e2c5ac3ad64e1be433440a5bda436), [`19a7033`](https://github.com/Effect-TS/effect-smol/commit/19a703367ec817cffc41d152da9b594827408e2b), [`33d26b4`](https://github.com/Effect-TS/effect-smol/commit/33d26b4210b2e974f146a71e7eed962f8ce00900), [`856766b`](https://github.com/Effect-TS/effect-smol/commit/856766b2c506aaed6d2df1d63bf3a5b1b062e1d4), [`079c7df`](https://github.com/Effect-TS/effect-smol/commit/079c7df82559bb9ce10a86dffb85d25e6ce07dc3)]:
- effect@4.0.0-beta.66
## 4.0.0-beta.65
### Patch Changes
- Updated dependencies [[`6f11454`](https://github.com/Effect-TS/effect-smol/commit/6f11454a9b6c3bd00f6b35fd7af14a2f2d63a0a2)]:
- effect@4.0.0-beta.65
## 4.0.0-beta.64
### Patch Changes
- Updated dependencies [[`7d4877a`](https://github.com/Effect-TS/effect-smol/commit/7d4877a1929cdb690280ea254326c04f2ec97ea5)]:
- effect@4.0.0-beta.64
## 4.0.0-beta.63
### Patch Changes
- [#2133](https://github.com/Effect-TS/effect-smol/pull/2133) [`5be0aaa`](https://github.com/Effect-TS/effect-smol/commit/5be0aaad694c9eb943a710eb4f896bc4c3fcae99) Thanks @Zelys-DFKH! - Fix `OpenAiLanguageModel` leaking library-only config fields (`fileIdPrefixes`, `strictJsonSchema`) into request body, causing OpenAI 400 errors.
- Updated dependencies [[`7f927ff`](https://github.com/Effect-TS/effect-smol/commit/7f927ffb7a9801dcfc4096c29e369d13d65cd0ac), [`a696b3e`](https://github.com/Effect-TS/effect-smol/commit/a696b3e83a8504cdbe261a18c10a1cc0619ae102)]:
- effect@4.0.0-beta.63
## 4.0.0-beta.62
### Patch Changes
- Updated dependencies [[`4ab4b90`](https://github.com/Effect-TS/effect-smol/commit/4ab4b9007dc27a52ffabc6fcb37c96eeec795bf7)]:
- effect@4.0.0-beta.62
## 4.0.0-beta.61
### Patch Changes
- Updated dependencies [[`50790af`](https://github.com/Effect-TS/effect-smol/commit/50790af9b190c38d10fb0723837d49b66432638f), [`71f7c3d`](https://github.com/Effect-TS/effect-smol/commit/71f7c3df997deda92c84146d569696dab3bd645c), [`aae8797`](https://github.com/Effect-TS/effect-smol/commit/aae8797b9cb383be0c182dd58d03d787c354238b)]:
- effect@4.0.0-beta.61
## 4.0.0-beta.60
### Patch Changes
- Updated dependencies [[`f69d567`](https://github.com/Effect-TS/effect-smol/commit/f69d5675dcff9f4137295752baf066b7153fdc09), [`7909c95`](https://github.com/Effect-TS/effect-smol/commit/7909c954b8f6244a35a4b429f8dd0dff45dad620), [`bbb4dcc`](https://github.com/Effect-TS/effect-smol/commit/bbb4dcc6c406b83a416b4ad3541cc02037c420e4), [`7af2207`](https://github.com/Effect-TS/effect-smol/commit/7af2207901eabf3132c1b7010a69b3899c06fbbe), [`848b40a`](https://github.com/Effect-TS/effect-smol/commit/848b40a4bd4bf54a5098617d50c33c88eee8270a)]:
- effect@4.0.0-beta.60
## 4.0.0-beta.59
### Patch Changes
- [#2101](https://github.com/Effect-TS/effect-smol/pull/2101) [`953edef`](https://github.com/Effect-TS/effect-smol/commit/953edef2e1ade369e530017d64391281ef22f28f) Thanks @tim-smart! - don't omit reasoning from openai config
- Updated dependencies [[`56837ea`](https://github.com/Effect-TS/effect-smol/commit/56837ea2a338395b35550641374e9e589bd8b71d)]:
- effect@4.0.0-beta.59
## 4.0.0-beta.58
### Patch Changes
- Updated dependencies [[`11993d4`](https://github.com/Effect-TS/effect-smol/commit/11993d4934c66f5dc611b8bbf553f01d501ef8f7), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec)]:
- effect@4.0.0-beta.58
## 4.0.0-beta.57
### Patch Changes
- Updated dependencies [[`a971f5c`](https://github.com/Effect-TS/effect-smol/commit/a971f5cbd92dfe4274420bf0966595eb35531060), [`8e110c5`](https://github.com/Effect-TS/effect-smol/commit/8e110c5f02a429ccc43a91df8678e402138c0851)]:
- effect@4.0.0-beta.57
## 4.0.0-beta.56
### Patch Changes
- [#1933](https://github.com/Effect-TS/effect-smol/pull/1933) [`c5a7327`](https://github.com/Effect-TS/effect-smol/commit/c5a732746690185b8bb7935d0cf26a5ef21658e4) Thanks @IMax153! - Accept both `in-memory` and `in_memory` for OpenAI `prompt_cache_retention` schema fields.
- [#1933](https://github.com/Effect-TS/effect-smol/pull/1933) [`c5a7327`](https://github.com/Effect-TS/effect-smol/commit/c5a732746690185b8bb7935d0cf26a5ef21658e4) Thanks @IMax153! - Fix OpenAI MCP tool call handling to keep the canonical `OpenAiMcp` tool name across response and stream paths, including approval flows.
- Updated dependencies []:
- effect@4.0.0-beta.56
## 4.0.0-beta.55
### Patch Changes
- Updated dependencies [[`42cc744`](https://github.com/Effect-TS/effect-smol/commit/42cc744570968deb365fb46d47b53d3277050c93), [`04855ce`](https://github.com/Effect-TS/effect-smol/commit/04855ceeca4d40c55a5750dd9893b691f8ea741a)]:
- effect@4.0.0-beta.55
## 4.0.0-beta.54
### Patch Changes
- Updated dependencies [[`e4b74f9`](https://github.com/Effect-TS/effect-smol/commit/e4b74f9c01a0e9b6cd58416de4af3a26d51da7c8), [`4c72808`](https://github.com/Effect-TS/effect-smol/commit/4c728081851c66dacf889a816535671bc841ae96)]:
- effect@4.0.0-beta.54
## 4.0.0-beta.53
### Patch Changes
- [#2060](https://github.com/Effect-TS/effect-smol/pull/2060) [`aef2b1c`](https://github.com/Effect-TS/effect-smol/commit/aef2b1c0130a61a430f116465e4c200c51bcd9e2) Thanks @tim-smart! - add back openai reasoning types
- Updated dependencies [[`0768509`](https://github.com/Effect-TS/effect-smol/commit/07685094e931af07d104165195826a535b55fa7e), [`476aede`](https://github.com/Effect-TS/effect-smol/commit/476aede69c6efa06b5781ca5eb3e3b128ca29141), [`4f79c54`](https://github.com/Effect-TS/effect-smol/commit/4f79c542e7b508c235ff485d862cc8b29a8260c5), [`4be6a7c`](https://github.com/Effect-TS/effect-smol/commit/4be6a7cf35dab2a01d652f56dd35f0358c5a7e88), [`88927eb`](https://github.com/Effect-TS/effect-smol/commit/88927ebb896162cdba103b36553280b58e0facac)]:
- effect@4.0.0-beta.53
## 4.0.0-beta.52
### Minor Changes
- [#2059](https://github.com/Effect-TS/effect-smol/pull/2059) [`6f73f92`](https://github.com/Effect-TS/effect-smol/commit/6f73f92291733dc1e970e222e63aba865183a072) Thanks @tim-smart! - Add a new public `OpenAiSchema` module with minimal local schemas for responses, streaming SSE events (including unknown-event fallback), and embeddings.
### Patch Changes
- [#2059](https://github.com/Effect-TS/effect-smol/pull/2059) [`6f73f92`](https://github.com/Effect-TS/effect-smol/commit/6f73f92291733dc1e970e222e63aba865183a072) Thanks @tim-smart! - Add a new `OpenAiClientGenerated` module that exposes the generated OpenAI client as a dedicated context service with `make`, `layer`, and `layerConfig` constructors. This provides a compatibility path for direct generated-client access while preserving existing auth, base URL, header, and `OpenAiConfig.transformClient` wiring.
- [#2059](https://github.com/Effect-TS/effect-smol/pull/2059) [`6f73f92`](https://github.com/Effect-TS/effect-smol/commit/6f73f92291733dc1e970e222e63aba865183a072) Thanks @tim-smart! - Refactor `OpenAiClient` to the handwritten minimal-schema path so `client` now exposes the configured `HttpClient`, `createResponse` / `createResponseStream` / `createEmbedding` use `OpenAiSchema` request-response types, and websocket mode no longer depends on generated-client internals.
Also migrate OpenAI language and embedding model request-response typing to `OpenAiSchema` and make embedding decoding explicitly reject non-vector (string/base64) payloads with `InvalidOutputError`.
- Updated dependencies [[`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`cf3a311`](https://github.com/Effect-TS/effect-smol/commit/cf3a311d863a8abb818840c3b80f847e621c43c1), [`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`131fdd5`](https://github.com/Effect-TS/effect-smol/commit/131fdd5b1f26531e265fe1a08f002002f47c276e)]:
- effect@4.0.0-beta.52
## 4.0.0-beta.51
### Patch Changes
- Updated dependencies [[`778d2af`](https://github.com/Effect-TS/effect-smol/commit/778d2afe9b5154bc1f9abae46d93ea7e54c87344), [`4e24dcf`](https://github.com/Effect-TS/effect-smol/commit/4e24dcf75037f65eebc1eb68623bc7cbf9d5512a), [`4b1c015`](https://github.com/Effect-TS/effect-smol/commit/4b1c0150e9bdb5559ed32d250deb66e17b4240c7), [`454f8ad`](https://github.com/Effect-TS/effect-smol/commit/454f8adad822929c3ef60f8280d0987226b049fd), [`6754a0c`](https://github.com/Effect-TS/effect-smol/commit/6754a0cd18626b06805a079cc5265525a5eb7d27), [`90f7fd5`](https://github.com/Effect-TS/effect-smol/commit/90f7fd5243871b30980964135db4512b8119fa82), [`d7e1519`](https://github.com/Effect-TS/effect-smol/commit/d7e151974934201fd93fa4c8a1192ee9a5d965a0), [`72a8122`](https://github.com/Effect-TS/effect-smol/commit/72a81228e09782bae512f7d041bbfbc78bc668d0)]:
- effect@4.0.0-beta.51
## 4.0.0-beta.50
### Patch Changes
- Updated dependencies [[`07be594`](https://github.com/Effect-TS/effect-smol/commit/07be594825de60f8e1b2102d21dbb9b8fc63b414), [`ae02433`](https://github.com/Effect-TS/effect-smol/commit/ae02433103ce28f53a0c9bfb4a44e75773289b7b)]:
- effect@4.0.0-beta.50
## 4.0.0-beta.49
### Patch Changes
- Updated dependencies [[`7d87873`](https://github.com/Effect-TS/effect-smol/commit/7d8787340ff549370f6f2a88b612e9ebbfd6ba45), [`c2f6f90`](https://github.com/Effect-TS/effect-smol/commit/c2f6f901b200a6e515b4f02c93ce8005b7bbf1c5), [`216f13c`](https://github.com/Effect-TS/effect-smol/commit/216f13c1fce454a21b489bb915714a17e791a1ac)]:
- effect@4.0.0-beta.49
## 4.0.0-beta.48
### Patch Changes
- Updated dependencies [[`4da56ec`](https://github.com/Effect-TS/effect-smol/commit/4da56ecff129b2da40137ffede23a73cc4e532d8), [`a5e6f77`](https://github.com/Effect-TS/effect-smol/commit/a5e6f774bab195cf50ecdc818240765f69a3bf4a), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070)]:
- effect@4.0.0-beta.48
## 4.0.0-beta.47
### Patch Changes
- Updated dependencies [[`c584726`](https://github.com/Effect-TS/effect-smol/commit/c58472674e750e6938df955044eab88feda95e45), [`86a91a4`](https://github.com/Effect-TS/effect-smol/commit/86a91a4f0c59286dfa9393232d8020dea70ed4db), [`131caf9`](https://github.com/Effect-TS/effect-smol/commit/131caf9525151a0cb29803a8f1dffa0f4f479d12), [`c3615c8`](https://github.com/Effect-TS/effect-smol/commit/c3615c88379b9daf252df0db72c6ac5a20326406)]:
- effect@4.0.0-beta.47
## 4.0.0-beta.46
### Patch Changes
- Updated dependencies [[`3a30b9e`](https://github.com/Effect-TS/effect-smol/commit/3a30b9e2ec2bd8b8193e1aa139f6878a07e3f5ee)]:
- effect@4.0.0-beta.46
## 4.0.0-beta.45
### Patch Changes
- Updated dependencies [[`5c3af6d`](https://github.com/Effect-TS/effect-smol/commit/5c3af6d554f60be34f8fc21d598d9a298ae11beb)]:
- effect@4.0.0-beta.45
## 4.0.0-beta.44
### Patch Changes
- [#1961](https://github.com/Effect-TS/effect-smol/pull/1961) [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970) Thanks @IMax153! - Rename the `ServiceMap` module to `Context` across exports, docs, and tests.
- Updated dependencies [[`e3f0621`](https://github.com/Effect-TS/effect-smol/commit/e3f0621454c3f5d11070d30619da27c9232cadc1), [`5b476ab`](https://github.com/Effect-TS/effect-smol/commit/5b476abc0bd7e9bb59135ea1bcad2e4936227ced), [`6b40e5a`](https://github.com/Effect-TS/effect-smol/commit/6b40e5a4a6bd2087c15a3d7374d25057fdedfa16), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`3b09fb3`](https://github.com/Effect-TS/effect-smol/commit/3b09fb31c40c2802b01f21c23bcdd1fe7fb0aa82), [`2370410`](https://github.com/Effect-TS/effect-smol/commit/237041062e5af4594d32db91597e34e70a632877), [`dabc272`](https://github.com/Effect-TS/effect-smol/commit/dabc272444a700eb629c07ba3e77671a841ca86e), [`08b63c3`](https://github.com/Effect-TS/effect-smol/commit/08b63c3df11bd35c9fd6090dbd166287fdc40664), [`dfff04c`](https://github.com/Effect-TS/effect-smol/commit/dfff04c4c2b1d352dfad83992a6dce1280c85cf9), [`9baed9e`](https://github.com/Effect-TS/effect-smol/commit/9baed9e17e84702e6e480fcef6f86404f9e24be9), [`7846792`](https://github.com/Effect-TS/effect-smol/commit/7846792adc7e1631d62d26d657bd7ba6139f369b), [`1556a24`](https://github.com/Effect-TS/effect-smol/commit/1556a247623636b7ebe438fb56d77f1a7bf957bb), [`7c11bc2`](https://github.com/Effect-TS/effect-smol/commit/7c11bc292ab8e46252fe8f7576fb685917bfb8b5), [`b5ea591`](https://github.com/Effect-TS/effect-smol/commit/b5ea5913ec1d45d0dd12a327b9dd966bda2f6d02), [`0853afa`](https://github.com/Effect-TS/effect-smol/commit/0853afaeb1633b2d7f8b66893bd01c3aa1ef2c22), [`ac845f3`](https://github.com/Effect-TS/effect-smol/commit/ac845f3ab40e0b8719576e7f9bc16ea2e0e02cd4), [`b80c462`](https://github.com/Effect-TS/effect-smol/commit/b80c46247480f47bb64fc480fab48a3f37bc8888), [`b3f535d`](https://github.com/Effect-TS/effect-smol/commit/b3f535d9a7ac13b5fb984c29f93561c57a081ff0), [`6fe2e93`](https://github.com/Effect-TS/effect-smol/commit/6fe2e93cc2f1b173ef89651d74b6a5d2626b3226), [`cda8004`](https://github.com/Effect-TS/effect-smol/commit/cda800451c1ffbdddfc08415aed7b2d91e0412ee), [`8335477`](https://github.com/Effect-TS/effect-smol/commit/8335477a8a936a24b5f3ee6203c1b268bd1bfc3c), [`8c836f9`](https://github.com/Effect-TS/effect-smol/commit/8c836f99ab1e896b9580a71d67773625baff2eaf), [`718ff6f`](https://github.com/Effect-TS/effect-smol/commit/718ff6fe3e3d3820cefd67d2bff1b2224fe08060), [`7eed84f`](https://github.com/Effect-TS/effect-smol/commit/7eed84fc33c5781a6fb11bf4fd189d424902ebd4), [`5df46fe`](https://github.com/Effect-TS/effect-smol/commit/5df46fe2f654d59ab5fc1578f4fc27fa40368ef9), [`82dd0f2`](https://github.com/Effect-TS/effect-smol/commit/82dd0f26c6442b07143762ef7bc33742d3978dd6), [`03ae41e`](https://github.com/Effect-TS/effect-smol/commit/03ae41e7304cffac9f18feea22b73468feafc43a), [`4677a0a`](https://github.com/Effect-TS/effect-smol/commit/4677a0a58f95eea38a211efcd3f345f237a9e44a), [`87e1fc8`](https://github.com/Effect-TS/effect-smol/commit/87e1fc8b67e4901d75f567b2fecc3841ab762cc4), [`c1af1b7`](https://github.com/Effect-TS/effect-smol/commit/c1af1b756f63291e9c0298cf95c98a6920a0c2a0), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`c8a877b`](https://github.com/Effect-TS/effect-smol/commit/c8a877b53e8f29616335719e5dd1c3992dddf780), [`7da961a`](https://github.com/Effect-TS/effect-smol/commit/7da961ae4916229d2246699a5d3b20e5b2dd2020)]:
- effect@4.0.0-beta.44
## 4.0.0-beta.43
### Patch Changes
- [#1905](https://github.com/Effect-TS/effect-smol/pull/1905) [`fc1444d`](https://github.com/Effect-TS/effect-smol/commit/fc1444df6fb7223ed228650c5c927b1df54a8757) Thanks @tim-smart! - openai ws tweaks
- Updated dependencies [[`2ae33d0`](https://github.com/Effect-TS/effect-smol/commit/2ae33d050914915f7cb9c25ab0a020901e08d596), [`979811a`](https://github.com/Effect-TS/effect-smol/commit/979811a4c3f7ed21ed18ef560c49fb7f5569e80e), [`eb7dbef`](https://github.com/Effect-TS/effect-smol/commit/eb7dbeffa883386ad912815e62c0820cac1fdf8e), [`cf50eb4`](https://github.com/Effect-TS/effect-smol/commit/cf50eb49cb04706dae5185f624708117c413dee8), [`1d046fe`](https://github.com/Effect-TS/effect-smol/commit/1d046fe484560e23f3e22cb23eec6433f8f1fa02)]:
- effect@4.0.0-beta.43
## 4.0.0-beta.42
### Patch Changes
- [#1859](https://github.com/Effect-TS/effect-smol/pull/1859) [`a4809db`](https://github.com/Effect-TS/effect-smol/commit/a4809db80f65601d37ccafc13d773e55909c4e98) Thanks @lloydrichards! - add dynamic tooling for openai and openai-compact language models
- [#1890](https://github.com/Effect-TS/effect-smol/pull/1890) [`4549175`](https://github.com/Effect-TS/effect-smol/commit/454917547e8c305707f0777f4efac7adaa924f00) Thanks @tim-smart! - reset openai websocket on error
- Updated dependencies [[`924e216`](https://github.com/Effect-TS/effect-smol/commit/924e216caa7e0bbf22e994a0cd2ce8b1f0f0b3ee), [`80e7f0c`](https://github.com/Effect-TS/effect-smol/commit/80e7f0cd9116e811e97b0ce30a77a8d1ecd072aa), [`f8328bf`](https://github.com/Effect-TS/effect-smol/commit/f8328bf0314da3dc7f31d314f94a5840e8d5217f), [`66d1c06`](https://github.com/Effect-TS/effect-smol/commit/66d1c06039079129707a230f7ad8c676439d7133), [`bee800b`](https://github.com/Effect-TS/effect-smol/commit/bee800bf285192a01bec72a7b7b51bc1159434e6), [`8930441`](https://github.com/Effect-TS/effect-smol/commit/8930441dee6f94c59c583d18d3ebd677cf1f2623)]:
- effect@4.0.0-beta.42
## 4.0.0-beta.41
### Patch Changes
- [#1887](https://github.com/Effect-TS/effect-smol/pull/1887) [`f291344`](https://github.com/Effect-TS/effect-smol/commit/f2913443d99146a6b36d8437e50b94f7c03c1284) Thanks @tim-smart! - make defects a retryable network error in websocket mode
- Updated dependencies [[`36f5c21`](https://github.com/Effect-TS/effect-smol/commit/36f5c2174d31ab42c4598bf81f178f40d0802283), [`d8ce758`](https://github.com/Effect-TS/effect-smol/commit/d8ce758669d6297ae932ac3251d83e7b49b22f30), [`11aab4c`](https://github.com/Effect-TS/effect-smol/commit/11aab4c6d37d5691adafc2d33da1a631b28ce814), [`3bc1efb`](https://github.com/Effect-TS/effect-smol/commit/3bc1efb53dd75b4a40de46f1f80c7f8a7d50af86), [`70e724e`](https://github.com/Effect-TS/effect-smol/commit/70e724e604604d4be1061cd8da0d360494998c84), [`738dee7`](https://github.com/Effect-TS/effect-smol/commit/738dee7edfd70af82dc4d2376db3a8ebe603eb48), [`2111963`](https://github.com/Effect-TS/effect-smol/commit/2111963f19b4c28c800664a8fac9590c1321885f), [`198a553`](https://github.com/Effect-TS/effect-smol/commit/198a553d9ce45f6a00bfc4d65ed0640669602d95)]:
- effect@4.0.0-beta.41
## 4.0.0-beta.40
### Patch Changes
- Updated dependencies [[`f62860f`](https://github.com/Effect-TS/effect-smol/commit/f62860f0e5e45978fabf7256ae620a13152a772a), [`973f281`](https://github.com/Effect-TS/effect-smol/commit/973f2812529aadc1cc54598b2039799fa72b80f8)]:
- effect@4.0.0-beta.40
## 4.0.0-beta.39
### Patch Changes
- Updated dependencies [[`f91fd3d`](https://github.com/Effect-TS/effect-smol/commit/f91fd3db39fe5628439fd175fba201a65a1aa9d0), [`edaae9d`](https://github.com/Effect-TS/effect-smol/commit/edaae9d65f464f941d7eddd723cd33d324f4b071), [`b47db0b`](https://github.com/Effect-TS/effect-smol/commit/b47db0bd5802064b6a24b3ea27c6ff2e0520d513), [`82d3c8e`](https://github.com/Effect-TS/effect-smol/commit/82d3c8e4f3f49b00df611b25aa6f8f74ec21b59b), [`7c22b31`](https://github.com/Effect-TS/effect-smol/commit/7c22b315d198dcbf44ae8cdb8b37879e1c9e3996)]:
- effect@4.0.0-beta.39
## 4.0.0-beta.38
### Patch Changes
- Updated dependencies [[`f4dbe5b`](https://github.com/Effect-TS/effect-smol/commit/f4dbe5b26b9c2d33fae024bf44afbdf8541792cd), [`a71a607`](https://github.com/Effect-TS/effect-smol/commit/a71a607c89fb6669a12a562c2c23be81dfbe1adb), [`66a0494`](https://github.com/Effect-TS/effect-smol/commit/66a0494ed75cd12f2721dcbb1d8a072e3d9e14b6), [`5ef7218`](https://github.com/Effect-TS/effect-smol/commit/5ef7218fc559d57301fe929b8a0cab4033f4f1fd), [`472d260`](https://github.com/Effect-TS/effect-smol/commit/472d260655bc311fba5c2c6e23bb77d8f7e36ba0)]:
- effect@4.0.0-beta.38
## 4.0.0-beta.37
### Patch Changes
- Updated dependencies [[`f7a0b71`](https://github.com/Effect-TS/effect-smol/commit/f7a0b711da8fdd645597dee29cacc5619c6afcf2), [`1e223c3`](https://github.com/Effect-TS/effect-smol/commit/1e223c30ccf835dfbb21284535d78549efaeca80), [`53740f4`](https://github.com/Effect-TS/effect-smol/commit/53740f47aa76d114b7d535649fb50efc54a09608), [`8c7cf89`](https://github.com/Effect-TS/effect-smol/commit/8c7cf89f719e580cbce1bf6c24e6996f1992a0a6), [`b6b81a9`](https://github.com/Effect-TS/effect-smol/commit/b6b81a940eaafcbc792d25413d6c02c707de31b2), [`8f4c1f9`](https://github.com/Effect-TS/effect-smol/commit/8f4c1f97ed60f8810b0b327b50117ffb2d8260d4), [`f2479f9`](https://github.com/Effect-TS/effect-smol/commit/f2479f9d3113b1f012db17a3852b4e28f478cf9c), [`c919921`](https://github.com/Effect-TS/effect-smol/commit/c9199217fad65529421d2cf95ecfff41257090fd), [`7af90c2`](https://github.com/Effect-TS/effect-smol/commit/7af90c2e3c99038eafa39650433839523790e2fe), [`f3be185`](https://github.com/Effect-TS/effect-smol/commit/f3be18569e5ca57c25eabf00df3ca601ebab43c7)]:
- effect@4.0.0-beta.37
## 4.0.0-beta.36
### Patch Changes
- [#1807](https://github.com/Effect-TS/effect-smol/pull/1807) [`1bbd4d9`](https://github.com/Effect-TS/effect-smol/commit/1bbd4d9d617e683a7f338ddd40d11f5838c722bd) Thanks @tim-smart! - Handle streamed OpenAI function calls from `response.function_call_arguments.done` so tool calls are emitted even when `response.output_item.done` is missing.
- [#1809](https://github.com/Effect-TS/effect-smol/pull/1809) [`126db2e`](https://github.com/Effect-TS/effect-smol/commit/126db2e38774c8ef4934321a70d39428ed4491ed) Thanks @tim-smart! - Fix OpenAI reasoning stream state handling so out-of-order reasoning summary events do not crash when prior reasoning item state is missing.
- Updated dependencies [[`60fcbcc`](https://github.com/Effect-TS/effect-smol/commit/60fcbcc43d09471e8f7e0969955d99dcefc5be81), [`0a60837`](https://github.com/Effect-TS/effect-smol/commit/0a6083713124440e630030375bab367e8d7df24e), [`49164d2`](https://github.com/Effect-TS/effect-smol/commit/49164d2c20a8d21b66514992c4a15d8521f6b36e), [`334b6e4`](https://github.com/Effect-TS/effect-smol/commit/334b6e4f76fe11941b516d61f57e268bc31f0ca6), [`5700695`](https://github.com/Effect-TS/effect-smol/commit/5700695f76ae6da6b94c9c87d4dd2b8054fb829b), [`f8f4456`](https://github.com/Effect-TS/effect-smol/commit/f8f445644f3aa7ec093cab7445198a62ba18a480), [`969d24f`](https://github.com/Effect-TS/effect-smol/commit/969d24fdfa48c4838e811983848d9cb4e9b3b12c), [`851eda0`](https://github.com/Effect-TS/effect-smol/commit/851eda0533946e39bacaaf581896320d7a4f3e8c), [`8059c1c`](https://github.com/Effect-TS/effect-smol/commit/8059c1c3eba9a90af7cd889ea261bcb8fff0c185), [`6f83295`](https://github.com/Effect-TS/effect-smol/commit/6f8329546a73eaddc7cb5e85ea8e37e73fbfb611), [`65f7f57`](https://github.com/Effect-TS/effect-smol/commit/65f7f5737575fed668987462c96d29a446707c32), [`e7fabd2`](https://github.com/Effect-TS/effect-smol/commit/e7fabd2265db690eae5cfc9b83730c84699aef61), [`89c3e98`](https://github.com/Effect-TS/effect-smol/commit/89c3e985401eb38f33a3ae21a94ad27de3c1d28b), [`53794ab`](https://github.com/Effect-TS/effect-smol/commit/53794ab7af30aa5c5004ecf53659fafbe4b10542)]:
- effect@4.0.0-beta.36
## 4.0.0-beta.35
### Patch Changes
- [#1776](https://github.com/Effect-TS/effect-smol/pull/1776) [`f5e553d`](https://github.com/Effect-TS/effect-smol/commit/f5e553d133b8e0a303f6c82624d2fec4ab8043da) Thanks @tim-smart! - improve openai socket errors
- Updated dependencies [[`9252b43`](https://github.com/Effect-TS/effect-smol/commit/9252b43560f507709c2985abcf52a7837b23ddf8), [`7daf387`](https://github.com/Effect-TS/effect-smol/commit/7daf3870a656882a488a60f67881e6808c8f4d04), [`e1664a3`](https://github.com/Effect-TS/effect-smol/commit/e1664a38bc31ef4ceb4e9324c7226e1e99bf9c07), [`fdaa6e0`](https://github.com/Effect-TS/effect-smol/commit/fdaa6e0a41b6b6605438fa8557441792135380a2), [`19aa47e`](https://github.com/Effect-TS/effect-smol/commit/19aa47ef7b470e427620edca8970dd9cdd551216), [`c667dad`](https://github.com/Effect-TS/effect-smol/commit/c667dad07777b860e4764a3ba9a6cc41c236cd98), [`764d150`](https://github.com/Effect-TS/effect-smol/commit/764d1501bc5026b60fc8aef6cb02a5a87c762801), [`3c27098`](https://github.com/Effect-TS/effect-smol/commit/3c27098b5685a63db2c2eff654a250c94d3fcfa7)]:
- effect@4.0.0-beta.35
## 4.0.0-beta.34
### Patch Changes
- [#1759](https://github.com/Effect-TS/effect-smol/pull/1759) [`8feecd2`](https://github.com/Effect-TS/effect-smol/commit/8feecd24158f254ca0571a1ddb554b560ed3177d) Thanks @tim-smart! - Ensure OpenAiSocket sends a `{"type":"response.cancel"}` websocket event when a response stream is interrupted.
- [#1764](https://github.com/Effect-TS/effect-smol/pull/1764) [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f) Thanks @tim-smart! - Add unstable EmbeddingModel support across core and OpenAI providers.
- Add the unstable EmbeddingModel module API surface in `effect`, including service, request, response, and provider types.
- Implement the unstable EmbeddingModel runtime constructor in `effect`, with `RequestResolver` batching, `embed` / `embedMany` spans, provider error propagation, deterministic ordering, and empty-input `embedMany` fast-path behavior.
- Add and align EmbeddingModel behavior tests in `effect` for embedding usage, batching, ordering, and error handling.
- Add `OpenAiEmbeddingModel` in `@effect/ai-openai`, including model / make / layer constructors, config overrides, and provider output index validation with deterministic reordering.
- Add OpenAI-compatible EmbeddingModel provider support in `@effect/ai-openai-compat`, including config overrides, layer constructors, and output index validation.
- [#1771](https://github.com/Effect-TS/effect-smol/pull/1771) [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e) Thanks @tim-smart! - Add `EmbeddingModel.ModelDimensions` and require dimensions in embedding provider `model` constructors.
- [#1765](https://github.com/Effect-TS/effect-smol/pull/1765) [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2) Thanks @tim-smart! - retry incremental prompt on invalid request
- [#1760](https://github.com/Effect-TS/effect-smol/pull/1760) [`273f4c6`](https://github.com/Effect-TS/effect-smol/commit/273f4c6517f5adc4994734a46698c52015890d1a) Thanks @tim-smart! - handle openai ws error events
- Updated dependencies [[`f2f75ee`](https://github.com/Effect-TS/effect-smol/commit/f2f75ee564bce1cd95f5189c7bdeeed4f92dacb1), [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f), [`5d704ee`](https://github.com/Effect-TS/effect-smol/commit/5d704ee10d20e8eb107e34bb8a21feb5aa4a7685), [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e), [`58217d3`](https://github.com/Effect-TS/effect-smol/commit/58217d318a7d716ccd707cce0f41573946939c28), [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2), [`e3b44b6`](https://github.com/Effect-TS/effect-smol/commit/e3b44b6a2af9ee21dc5c1e928f0c20af857fa7a9), [`e1472b7`](https://github.com/Effect-TS/effect-smol/commit/e1472b7525c5d57a48bdec2353c3b742f7f916c0), [`7686320`](https://github.com/Effect-TS/effect-smol/commit/7686320cd123fa352b5c3d076fb18a3cac0a9bba)]:
- effect@4.0.0-beta.34
## 4.0.0-beta.33
### Patch Changes
- [#1638](https://github.com/Effect-TS/effect-smol/pull/1638) [`e198fea`](https://github.com/Effect-TS/effect-smol/commit/e198fea76c89a36c6f05ea7b7d11d222cf5a240c) Thanks @tim-smart! - add OpenAiClient.withWebSocketMode
- Updated dependencies [[`571447d`](https://github.com/Effect-TS/effect-smol/commit/571447da67334449f8ae3d6ecb3d77ea4e0c4295)]:
- effect@4.0.0-beta.33
## 4.0.0-beta.32
### Patch Changes
- Updated dependencies [[`bf8fff8`](https://github.com/Effect-TS/effect-smol/commit/bf8fff8a5f54b6df74cb7bbb42346fe9ba52435a), [`1af3ef3`](https://github.com/Effect-TS/effect-smol/commit/1af3ef3e3ca7fd417d0fc15f8ca8fe207eba4f74), [`27fea0f`](https://github.com/Effect-TS/effect-smol/commit/27fea0f66910de5905f40fd63f8ddbb6f7ac5aba), [`2ad6c1b`](https://github.com/Effect-TS/effect-smol/commit/2ad6c1b2c85a3a0fe351e3d56636a75eb76b4b4e), [`398ac3e`](https://github.com/Effect-TS/effect-smol/commit/398ac3e01cb75efce0e4e2913d1450cf65866732), [`51fe22f`](https://github.com/Effect-TS/effect-smol/commit/51fe22f3266e417b6c541aaed4b75d246fac91e7), [`4605db6`](https://github.com/Effect-TS/effect-smol/commit/4605db69cfacddbdbf1525865ddfde135158090c), [`f4de1b0`](https://github.com/Effect-TS/effect-smol/commit/f4de1b087c998d0bad1d9468f70b7d16c13b9f6f), [`60214f2`](https://github.com/Effect-TS/effect-smol/commit/60214f2080b2aeb091f691140eb20acb741691c3), [`c4b8b0f`](https://github.com/Effect-TS/effect-smol/commit/c4b8b0ffa8efb47c4cd7578a8943d6868509373f), [`6d9393a`](https://github.com/Effect-TS/effect-smol/commit/6d9393a0770a18722d23340e77f15455de341245), [`6de4efe`](https://github.com/Effect-TS/effect-smol/commit/6de4efe463c783614ceb0c094d77a336a899cbe0), [`4f969d1`](https://github.com/Effect-TS/effect-smol/commit/4f969d1563ba755ffa116c8ae409bb3436bd881d), [`6cc67c8`](https://github.com/Effect-TS/effect-smol/commit/6cc67c855e054ee3f3ac3485dca5f7805e79e8fb), [`8531a22`](https://github.com/Effect-TS/effect-smol/commit/8531a22ffbb52e11a030b09f358cafbfdf5edff7), [`b226760`](https://github.com/Effect-TS/effect-smol/commit/b22676067617f15c00722a3a63fd7c2c172c3d45), [`47a51ab`](https://github.com/Effect-TS/effect-smol/commit/47a51aba0ecdf3ef478bfa28a498bca188399bd4), [`1521d02`](https://github.com/Effect-TS/effect-smol/commit/1521d02e1f19f1d795edaaf862c1a1031d9c755e)]:
- effect@4.0.0-beta.32
## 4.0.0-beta.31
### Patch Changes
- Updated dependencies [[`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6f23f0e`](https://github.com/Effect-TS/effect-smol/commit/6f23f0ed4cba573cd9395c2e582f582fe7271544), [`654aaec`](https://github.com/Effect-TS/effect-smol/commit/654aaec593305521b65dd042c204d761cc6e8c28), [`2958a42`](https://github.com/Effect-TS/effect-smol/commit/2958a42078966a8713a98f00485ab36484d5eccf), [`95d27a2`](https://github.com/Effect-TS/effect-smol/commit/95d27a239ed5147302605ab0b3147a056541b0c7), [`0fbaea8`](https://github.com/Effect-TS/effect-smol/commit/0fbaea8f9555a8044cec31a770394db613fc78e2), [`21d5d5e`](https://github.com/Effect-TS/effect-smol/commit/21d5d5e0439fd4d9bb6e508377215b1087555d45), [`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6e49959`](https://github.com/Effect-TS/effect-smol/commit/6e499590357a104c81779b3176cd3f84e4f91064), [`8f5805d`](https://github.com/Effect-TS/effect-smol/commit/8f5805dbdd0d1bc0ff0727cc398c8d80e544edee), [`990df2c`](https://github.com/Effect-TS/effect-smol/commit/990df2c3ceeb32e659acc10cc9485617f7b3c423)]:
- effect@4.0.0-beta.31
## 4.0.0-beta.30
### Patch Changes
- [#1679](https://github.com/Effect-TS/effect-smol/pull/1679) [`d440fd4`](https://github.com/Effect-TS/effect-smol/commit/d440fd4e79f5943998249f5e67fb42505bd20228) Thanks @tim-smart! - Add support for OpenAI `keepalive` response stream events.
- Updated dependencies [[`c88e5b7`](https://github.com/Effect-TS/effect-smol/commit/c88e5b723ff09da4edaef6ce14d927ca01104a32), [`947d0e4`](https://github.com/Effect-TS/effect-smol/commit/947d0e4268ba5c4020ead380aa80812c7342408f), [`7517908`](https://github.com/Effect-TS/effect-smol/commit/75179085d159b88a1ab0bce70669d76dcf0d79a4), [`a49ecd5`](https://github.com/Effect-TS/effect-smol/commit/a49ecd5a183d7e7d33f47ff95e9d2dea5a12ead5), [`6993e33`](https://github.com/Effect-TS/effect-smol/commit/6993e3329122c834c20bacea72d8678232f4f103), [`514f2a2`](https://github.com/Effect-TS/effect-smol/commit/514f2a2ae54580fcacdbe2ea2196a83a852d0748), [`3214b47`](https://github.com/Effect-TS/effect-smol/commit/3214b47676de2d33fddc5fecfc2d226e6e83cc7b), [`95ec5ed`](https://github.com/Effect-TS/effect-smol/commit/95ec5ed345de77c893049e182d37a37cf164a268)]:
- effect@4.0.0-beta.30
## 4.0.0-beta.29
### Patch Changes
- Updated dependencies [[`9d93adb`](https://github.com/Effect-TS/effect-smol/commit/9d93adb1c1795d1978391b30d7d2972c88052662), [`b52721c`](https://github.com/Effect-TS/effect-smol/commit/b52721cf0d11a567722b060c8536e3bdd4161f07), [`a891c7b`](https://github.com/Effect-TS/effect-smol/commit/a891c7b12f415b2287613dd4b91a09dfd38ef30d), [`ef26cdf`](https://github.com/Effect-TS/effect-smol/commit/ef26cdfb65d9955fc7e161629191930c2cc2c63f), [`82fd3ed`](https://github.com/Effect-TS/effect-smol/commit/82fd3ed922063ee5a34f96f3993c15c7515e4f67)]:
- effect@4.0.0-beta.29
## 4.0.0-beta.28
### Patch Changes
- Updated dependencies [[`ff533f2`](https://github.com/Effect-TS/effect-smol/commit/ff533f203cd06302ad08032a27e01269b4a2d4c6), [`dc803ee`](https://github.com/Effect-TS/effect-smol/commit/dc803ee52ebd3e9f931118f0dfcb804542847556), [`d660b1c`](https://github.com/Effect-TS/effect-smol/commit/d660b1c99cb93d4f79715e91c7a4486801c0eefa), [`93a05e3`](https://github.com/Effect-TS/effect-smol/commit/93a05e3eaa624058b162aedd66aad70102837270), [`2a65cf6`](https://github.com/Effect-TS/effect-smol/commit/2a65cf6fd81ef63d944e6fb51f058d439bf4a834), [`a561a40`](https://github.com/Effect-TS/effect-smol/commit/a561a40cc41c548c2cf3153aca065ee92ee8aa57), [`29cd24d`](https://github.com/Effect-TS/effect-smol/commit/29cd24d1fe78480a72eeb38a90281ffddc0530bc), [`662a8e6`](https://github.com/Effect-TS/effect-smol/commit/662a8e6857dac64a7cd13bd8df4b0674654622f8), [`d2b52ba`](https://github.com/Effect-TS/effect-smol/commit/d2b52bae5b9336cf59729fbdcc4d7f09512b0cbf), [`407c3b4`](https://github.com/Effect-TS/effect-smol/commit/407c3b43a5d1414558e0e33b6f1fc0e6a6d489cc), [`42bc7ce`](https://github.com/Effect-TS/effect-smol/commit/42bc7ce5480f6f2953c39f8cb5c850d61df6f5a2), [`e741322`](https://github.com/Effect-TS/effect-smol/commit/e74132226cbfee24234311c7c1c13e6b7391384e), [`5c75fa8`](https://github.com/Effect-TS/effect-smol/commit/5c75fa8fb71163bc4c035ba1a215574dfd4badfc), [`747177b`](https://github.com/Effect-TS/effect-smol/commit/747177b0602f12d4461a843e953dfdffbeb0a429), [`326cd48`](https://github.com/Effect-TS/effect-smol/commit/326cd4828bce573fe985f35152155464bf4c5a70), [`627e922`](https://github.com/Effect-TS/effect-smol/commit/627e922b8d1e9521eae5e1caa5d667ad00b1619a), [`662287e`](https://github.com/Effect-TS/effect-smol/commit/662287e9abc76c941ccc2ee330aa07904d571341)]:
- effect@4.0.0-beta.28
## 4.0.0-beta.27
### Patch Changes
- Updated dependencies [[`903a839`](https://github.com/Effect-TS/effect-smol/commit/903a839e94239e6ec4568315af28e405bcad95f4), [`91a0168`](https://github.com/Effect-TS/effect-smol/commit/91a016836680a6669308ecf464d3584bcc4ae1b7), [`c890f9a`](https://github.com/Effect-TS/effect-smol/commit/c890f9a1b3a989ed22528bd5a43326342e05b142), [`1e985f2`](https://github.com/Effect-TS/effect-smol/commit/1e985f237d250b51b91de22dde77160c1e778ce7)]:
- effect@4.0.0-beta.27
## 4.0.0-beta.26
### Patch Changes
- Updated dependencies [[`fb21462`](https://github.com/Effect-TS/effect-smol/commit/fb21462642cdd5b1bada92f3eba18ae20445be42), [`2ed26b1`](https://github.com/Effect-TS/effect-smol/commit/2ed26b139805700e3df39efaa768ff01565e5c86), [`e832a57`](https://github.com/Effect-TS/effect-smol/commit/e832a57b570fe38f010c1fd99bceac5a325a9e07), [`7f01be7`](https://github.com/Effect-TS/effect-smol/commit/7f01be7f8db363d4b2e88e6b5571e96bb815786f), [`e965143`](https://github.com/Effect-TS/effect-smol/commit/e9651431e114479e6becf8ca7b1ed99ac7e91ccc), [`b9b80f1`](https://github.com/Effect-TS/effect-smol/commit/b9b80f1f15e152ceef0a727d150b7dc230abae99), [`98252aa`](https://github.com/Effect-TS/effect-smol/commit/98252aa0c0b17fc73fbdad65d0a1104965f9fc0f), [`56fbd94`](https://github.com/Effect-TS/effect-smol/commit/56fbd94311ad19a05001ad649d9e34ab00c74541), [`3faa109`](https://github.com/Effect-TS/effect-smol/commit/3faa109b7d093fbf14ad410d3e11d663f16e28f1), [`692ecfe`](https://github.com/Effect-TS/effect-smol/commit/692ecfed99fe58056b7a5afe001f4fcd1a61c446), [`1e70b72`](https://github.com/Effect-TS/effect-smol/commit/1e70b72d0b210474d0e96a15a5cfc279eae37e0c), [`ecf0782`](https://github.com/Effect-TS/effect-smol/commit/ecf07829ef2dfc01d8943c96c4fe9c1b44b97926)]:
- effect@4.0.0-beta.26
## 4.0.0-beta.25
### Patch Changes
- Updated dependencies [[`fa17bb5`](https://github.com/Effect-TS/effect-smol/commit/fa17bb5be9f2533d01e11322b14804c7dec43714), [`f46e5b5`](https://github.com/Effect-TS/effect-smol/commit/f46e5b5ca2a918ee4d9270167e79db223077c96f), [`ce4767c`](https://github.com/Effect-TS/effect-smol/commit/ce4767cadcacc6ce8ff4c3a0d0fbc82ede655f63), [`c830a8b`](https://github.com/Effect-TS/effect-smol/commit/c830a8b6c292a6528d7f9318759d34800b00372d)]:
- effect@4.0.0-beta.25
## 4.0.0-beta.24
### Patch Changes
- Updated dependencies [[`a909e1c`](https://github.com/Effect-TS/effect-smol/commit/a909e1c1ac2bc707527f5073776e3e7d239688d9), [`8814a4e`](https://github.com/Effect-TS/effect-smol/commit/8814a4ef78d67144d27689370af10099ea210399), [`3f942c5`](https://github.com/Effect-TS/effect-smol/commit/3f942c51cefa7b2ffa7c49e8c8a2c887570ba4c0), [`774ed59`](https://github.com/Effect-TS/effect-smol/commit/774ed59c52b2ab578bbb897c4f551f812231e1d2), [`f54b8d3`](https://github.com/Effect-TS/effect-smol/commit/f54b8d398fedad1815fd1f4c49814ab938cfc385)]:
- effect@4.0.0-beta.24
## 4.0.0-beta.23
### Patch Changes
- Updated dependencies [[`5c73c41`](https://github.com/Effect-TS/effect-smol/commit/5c73c41b69eaeab80fcd62c9bfda490b446d1966)]:
- effect@4.0.0-beta.23
## 4.0.0-beta.22
### Patch Changes
- Updated dependencies [[`0874332`](https://github.com/Effect-TS/effect-smol/commit/0874332f7c81118b06ac2eb105e0710211631479), [`c592dcd`](https://github.com/Effect-TS/effect-smol/commit/c592dcde0697e322065c8f418c0480ef910cb183), [`1dbe28d`](https://github.com/Effect-TS/effect-smol/commit/1dbe28dac8299cd3e218c9768450cfd173b5e294), [`564d730`](https://github.com/Effect-TS/effect-smol/commit/564d730b6bbf38dd8548a3b046e7a693b28699a4), [`3cfadc4`](https://github.com/Effect-TS/effect-smol/commit/3cfadc458b070c6cba6c5674b72a059f1e49118b), [`6634fd0`](https://github.com/Effect-TS/effect-smol/commit/6634fd07da067d80b8261fb2959d1a952b9e412e), [`d10dabe`](https://github.com/Effect-TS/effect-smol/commit/d10dabeb7af9a368f995829cd36ad08167cd8f95), [`f82f549`](https://github.com/Effect-TS/effect-smol/commit/f82f549a09e950e9d4987f279a800f4d953f0939), [`78a3382`](https://github.com/Effect-TS/effect-smol/commit/78a3382ddfbe034408f7480fa794733d9e82147b)]:
- effect@4.0.0-beta.22
## 4.0.0-beta.21
### Patch Changes
- Updated dependencies [[`e691909`](https://github.com/Effect-TS/effect-smol/commit/e691909495ccb162ea7bfa351dd74632b99997cb), [`d5f413f`](https://github.com/Effect-TS/effect-smol/commit/d5f413f3c8fc57f2413cc5649c2003d6d4e5a6d7), [`139d152`](https://github.com/Effect-TS/effect-smol/commit/139d152941e562a073b5be12e8d66c8a4d4a8a57), [`947e3d4`](https://github.com/Effect-TS/effect-smol/commit/947e3d436ab8a017efda9b29be523efd1ca8df28), [`84b2cce`](https://github.com/Effect-TS/effect-smol/commit/84b2ccefe2aa3a7413b86738a4dc33cdb311ca55), [`7f5305e`](https://github.com/Effect-TS/effect-smol/commit/7f5305e69f5a33309e77b08a576edb25d7daaee2), [`9e6fd84`](https://github.com/Effect-TS/effect-smol/commit/9e6fd8471c93a3c643929151a3bdb62cb9c0ca0e), [`fdb8a4b`](https://github.com/Effect-TS/effect-smol/commit/fdb8a4b172721fbefe98bd5aa6fe4f0efd1da3eb), [`0f986ef`](https://github.com/Effect-TS/effect-smol/commit/0f986ef22f196fe091a7afdbd179485a7d888882), [`9355fc0`](https://github.com/Effect-TS/effect-smol/commit/9355fc0ffb5b7382146a5aed9eea83974b10d007)]:
- effect@4.0.0-beta.21
## 4.0.0-beta.20
### Patch Changes
- [#1529](https://github.com/Effect-TS/effect-smol/pull/1529) [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8) Thanks @tim-smart! - Add dedicated AiError metadata interfaces per reason so provider packages can safely augment metadata without conflicting module declarations.
- [#1528](https://github.com/Effect-TS/effect-smol/pull/1528) [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34) Thanks @tim-smart! - Add `Model.ModelName` and provide it from AI model constructors.
- Updated dependencies [[`842a624`](https://github.com/Effect-TS/effect-smol/commit/842a624f79d5e1407460b0ef3ab27d14d48ccf74), [`4785eef`](https://github.com/Effect-TS/effect-smol/commit/4785eef5d7cf1edb96ef2509aed2ba4d1edf3862), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`12ee8e2`](https://github.com/Effect-TS/effect-smol/commit/12ee8e27df7eb393d83a5e403390d0cfc82ca732), [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34), [`989d1cc`](https://github.com/Effect-TS/effect-smol/commit/989d1cca936fce0cc459057825ba40e3f5ef3827)]:
- effect@4.0.0-beta.20
## 4.0.0-beta.19
### Patch Changes
- [#1522](https://github.com/Effect-TS/effect-smol/pull/1522) [`19984c2`](https://github.com/Effect-TS/effect-smol/commit/19984c2c1b23ef06d69f9bbd4c4ad992a03c6860) Thanks @IMax153! - Fix missing `yield*` in `OpenAiLanguageModel.prepareResponseFormat`
- Updated dependencies []:
- effect@4.0.0-beta.19
## 4.0.0-beta.18
### Patch Changes
- [#1520](https://github.com/Effect-TS/effect-smol/pull/1520) [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d) Thanks @IMax153! - Ensure that OpenAI JSON schemas for tool calls and structured outputs are properly transformed
- Updated dependencies [[`01e31fd`](https://github.com/Effect-TS/effect-smol/commit/01e31fdf8e5206849d23cbafd23a346f2f177ab8), [`0890aab`](https://github.com/Effect-TS/effect-smol/commit/0890aab15ed9c5ba52c383a72fdc6a444d7504d5), [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d)]:
- effect@4.0.0-beta.18
## 4.0.0-beta.17
### Patch Changes
- Updated dependencies [[`8f59c32`](https://github.com/Effect-TS/effect-smol/commit/8f59c32922597a48392744f7203e284866747781)]:
- effect@4.0.0-beta.17
## 4.0.0-beta.16
### Patch Changes
- Updated dependencies [[`bf9096c`](https://github.com/Effect-TS/effect-smol/commit/bf9096c52a7d8791d93d232739e523eb84f6625a), [`29f81ca`](https://github.com/Effect-TS/effect-smol/commit/29f81ca07c67dba265804b140a7487fb15a5fc6b), [`68eb28c`](https://github.com/Effect-TS/effect-smol/commit/68eb28c2b0fc67a9f6204ade9bd16c5b37803bfb)]:
- effect@4.0.0-beta.16
## 4.0.0-beta.15
### Patch Changes
- [#1502](https://github.com/Effect-TS/effect-smol/pull/1502) [`285b7e6`](https://github.com/Effect-TS/effect-smol/commit/285b7e667167566d5788367d5155b19c79f1bf22) Thanks @tim-smart! - allow undefined for ai config
- Updated dependencies [[`24ae609`](https://github.com/Effect-TS/effect-smol/commit/24ae60995d2fd7d621be356cdfdfd328c79639ba), [`0e3c059`](https://github.com/Effect-TS/effect-smol/commit/0e3c059987caa55ebd0c134f7c7b147c639c328e), [`e843b0a`](https://github.com/Effect-TS/effect-smol/commit/e843b0a7d7e7b600a0b3bd477f24e2e4cd26bc8b), [`f4389a2`](https://github.com/Effect-TS/effect-smol/commit/f4389a2cca3c5bbf00d69779f52ce41255f15a28), [`5b73de0`](https://github.com/Effect-TS/effect-smol/commit/5b73de095b3402d0c5c74092ace6ce18ebfad566), [`595d2d6`](https://github.com/Effect-TS/effect-smol/commit/595d2d6e7d50419f3532bd39266191532ace38f2)]:
- effect@4.0.0-beta.15
## 4.0.0-beta.14
### Patch Changes
- Updated dependencies [[`c414700`](https://github.com/Effect-TS/effect-smol/commit/c414700ef1932e4b67d0102856de417336912350), [`a30c969`](https://github.com/Effect-TS/effect-smol/commit/a30c9699c0d736cf3952041e45d508b7d58907a9)]:
- effect@4.0.0-beta.14
## 4.0.0-beta.13
### Patch Changes
- Updated dependencies [[`368f4c3`](https://github.com/Effect-TS/effect-smol/commit/368f4c363dd117e6f5a19ad77b161176cfd29fdd), [`db8a579`](https://github.com/Effect-TS/effect-smol/commit/db8a579e93e93ff73b1e60712732e03b597b916b), [`668b703`](https://github.com/Effect-TS/effect-smol/commit/668b70337e9ddbb0d1ae2282a95c282ce404e562), [`d40e76b`](https://github.com/Effect-TS/effect-smol/commit/d40e76b973543979e60e04a6baca04a8c65bdfc2), [`6e18cf8`](https://github.com/Effect-TS/effect-smol/commit/6e18cf883e9905ca718a6697b6a2a4bbd42739aa), [`86062e8`](https://github.com/Effect-TS/effect-smol/commit/86062e8a0c61bca5412fc40d2cf151d676901f08), [`c27ce75`](https://github.com/Effect-TS/effect-smol/commit/c27ce75d34c74dcfc6dba1bf77f1ce88f410a0de), [`e2d4fbf`](https://github.com/Effect-TS/effect-smol/commit/e2d4fbfeeda6a5d2a4c5aeb0501d8240c248b9eb), [`114ab42`](https://github.com/Effect-TS/effect-smol/commit/114ab42ad0edc590d29169675a493e0e915aa58f), [`484caec`](https://github.com/Effect-TS/effect-smol/commit/484caec47cccac8b86db2910742e406dfc7173ab)]:
- effect@4.0.0-beta.13
## 4.0.0-beta.12
### Patch Changes
- Updated dependencies [[`70a74e8`](https://github.com/Effect-TS/effect-smol/commit/70a74e88a8767c9d4acdb9e5f25aec9a33588d07), [`b5b6e10`](https://github.com/Effect-TS/effect-smol/commit/b5b6e10621d54bf8c9857fec0d647ced78ecd857), [`f5ce5a9`](https://github.com/Effect-TS/effect-smol/commit/f5ce5a915359c6ebf254079e1da23cab6cde34fb), [`a29eb70`](https://github.com/Effect-TS/effect-smol/commit/a29eb702ffe3fc58bd28c4d7857298cd65d73668), [`c7b36e5`](https://github.com/Effect-TS/effect-smol/commit/c7b36e541a23e9a00f64e25b23851e51a37dfce5), [`9381d6d`](https://github.com/Effect-TS/effect-smol/commit/9381d6d4d9d819a81a46e56d0364c76e92a4fbca), [`88439f1`](https://github.com/Effect-TS/effect-smol/commit/88439f13ca13549f3e4822c48c4f019c14fc2bcc), [`e35307d`](https://github.com/Effect-TS/effect-smol/commit/e35307dbeb8eb26a9923f958b894a8eaaf259bf2), [`c7df4bc`](https://github.com/Effect-TS/effect-smol/commit/c7df4bce34009474c63d62a807abfdafb76971eb), [`accaf3b`](https://github.com/Effect-TS/effect-smol/commit/accaf3be7ac8da36e2334c509c23b8c9e88ea160), [`3e1c270`](https://github.com/Effect-TS/effect-smol/commit/3e1c2707bbdf67720af1509642b8ced195790882), [`6cd81f7`](https://github.com/Effect-TS/effect-smol/commit/6cd81f73baad86f5bbfa455a55d75cde71e9611a), [`f222da3`](https://github.com/Effect-TS/effect-smol/commit/f222da3cdb44554f3324c2c52d0d005ee575053e), [`61f901d`](https://github.com/Effect-TS/effect-smol/commit/61f901d830005b66e22d1de889fda132aeea97cd)]:
- effect@4.0.0-beta.12
## 4.0.0-beta.11
### Patch Changes
- Updated dependencies [[`88659ed`](https://github.com/Effect-TS/effect-smol/commit/88659edb26e3623d557dccfe914c2c949672da16), [`f2915e8`](https://github.com/Effect-TS/effect-smol/commit/f2915e8e2efe80d50c281e53f297b9701d6dc199), [`eb71ace`](https://github.com/Effect-TS/effect-smol/commit/eb71acebbe0f228e4920278013beee3b67d62310), [`2a16999`](https://github.com/Effect-TS/effect-smol/commit/2a169996c7513d377ac47adbfd68e1490457135c), [`d42dd52`](https://github.com/Effect-TS/effect-smol/commit/d42dd52f11203f8e749fb5d3ecf7153e4a5a6814), [`339adaf`](https://github.com/Effect-TS/effect-smol/commit/339adaf850a62a892adebcb208c2d9dddf3b97b3), [`de19645`](https://github.com/Effect-TS/effect-smol/commit/de1964526d01102dd1cb99c8cfdd3e8df1f49ef1), [`9b1dc3b`](https://github.com/Effect-TS/effect-smol/commit/9b1dc3bcf2a1b68d0a67e3465db5ad01a1a56997), [`e4cb2f5`](https://github.com/Effect-TS/effect-smol/commit/e4cb2f55b30f4771ec1bf613ced36d6d96464dd5), [`8bced95`](https://github.com/Effect-TS/effect-smol/commit/8bced954ecb35d4489197a57b0efe927e7d75f49), [`9431420`](https://github.com/Effect-TS/effect-smol/commit/94314207c8019918200fbcb97aec992219f801f0), [`948dca2`](https://github.com/Effect-TS/effect-smol/commit/948dca22e4f672ba7a6db57f9899272bec7c08b8), [`d18e327`](https://github.com/Effect-TS/effect-smol/commit/d18e32765a2665e31ffb31e746bf983fcfac34c5), [`ab512f7`](https://github.com/Effect-TS/effect-smol/commit/ab512f7be1c0e6b359da921e22cd4944e4c57d3e)]:
- effect@4.0.0-beta.11
## 4.0.0-beta.10
### Patch Changes
- Updated dependencies [[`371acab`](https://github.com/Effect-TS/effect-smol/commit/371acabb58d56f3a7a5e3e33d3d5fdc9f5573c74), [`856d774`](https://github.com/Effect-TS/effect-smol/commit/856d7741f1e296dd5048c6ff2b44b95d023e6ae4), [`b9e9202`](https://github.com/Effect-TS/effect-smol/commit/b9e92023c38caa322975d77cfe83e2d34ac9305a), [`1d1a974`](https://github.com/Effect-TS/effect-smol/commit/1d1a974bd280c81bff5d4505491cda03ba7a3f36), [`6bfe2a6`](https://github.com/Effect-TS/effect-smol/commit/6bfe2a659bc6335db75709931f405da45301cba2), [`b12c811`](https://github.com/Effect-TS/effect-smol/commit/b12c81157be287b1649c210616a244b50ec094d2), [`d17d98a`](https://github.com/Effect-TS/effect-smol/commit/d17d98ad78e2b44d95ef434adab79ac3c35e75ab), [`68c3c7c`](https://github.com/Effect-TS/effect-smol/commit/68c3c7cb1e06ed94fa5c4c123a234b4ccbfdecd8)]:
- effect@4.0.0-beta.10
## 4.0.0-beta.9
### Patch Changes
- Updated dependencies [[`3386557`](https://github.com/Effect-TS/effect-smol/commit/338655731564a7be9f8859dedbf4d5bcac6eb350), [`b6666e3`](https://github.com/Effect-TS/effect-smol/commit/b6666e3cf6bd44ba1a8704e65c256c30359cb422)]:
- effect@4.0.0-beta.9
## 4.0.0-beta.8
### Patch Changes
- [#1354](https://github.com/Effect-TS/effect-smol/pull/1354) [`b94962c`](https://github.com/Effect-TS/effect-smol/commit/b94962c249d46cf96cdf2e41188dc9feda41536a) Thanks @IMax153! - Fix the generated schemas for ai providers
- Updated dependencies [[`246e672`](https://github.com/Effect-TS/effect-smol/commit/246e672dbbd7848d60e0c78fd66671b2f10b3752), [`807dec0`](https://github.com/Effect-TS/effect-smol/commit/807dec03801b4c58a6d00c237b6d98d6386911df)]:
- effect@4.0.0-beta.8
## 4.0.0-beta.7
### Patch Changes
- Updated dependencies [[`a2bda6d`](https://github.com/Effect-TS/effect-smol/commit/a2bda6d4ef6de9d9b0c53ae2df5434f778d6161a), [`1f95a2b`](https://github.com/Effect-TS/effect-smol/commit/1f95a2b5aa9524bb38f4437f4691a664bf463ca1), [`a8d5e79`](https://github.com/Effect-TS/effect-smol/commit/a8d5e792fec201a83af0eb92fc79928d055125fd), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`06d8a03`](https://github.com/Effect-TS/effect-smol/commit/06d8a0391631e6130e3ab25227e59817852e227f), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`f9e883e`](https://github.com/Effect-TS/effect-smol/commit/f9e883e266fbda870336ee62f46b7ac85ba3de6e), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430)]:
- effect@4.0.0-beta.7
## 4.0.0-beta.6
### Patch Changes
- Updated dependencies [[`3247da2`](https://github.com/Effect-TS/effect-smol/commit/3247da28331f345f68be5dbd2974a7e03d300fe1), [`f205705`](https://github.com/Effect-TS/effect-smol/commit/f2057050dbd034b8c186be2d40c3d03ee63a5a3b), [`f35022c`](https://github.com/Effect-TS/effect-smol/commit/f35022c212e4111527e1bb43f360a67b2b49fa85), [`8622721`](https://github.com/Effect-TS/effect-smol/commit/86227217b02d43680a3c6f3c21731b1d852c91f5), [`fc660ab`](https://github.com/Effect-TS/effect-smol/commit/fc660ab8b5ebae38b8d6b96cbf2f9b880cc09253), [`f37dc33`](https://github.com/Effect-TS/effect-smol/commit/f37dc335f64622fa9ce8d6d1d5dd8fc3f260257b), [`3662f32`](https://github.com/Effect-TS/effect-smol/commit/3662f328fcfa3b2fa01ffa79da40e12e93fcede8), [`a7d436f`](https://github.com/Effect-TS/effect-smol/commit/a7d436f438dcd7f49b9485e4e95a4511f31fad7d), [`6856a41`](https://github.com/Effect-TS/effect-smol/commit/6856a415d7eddd9d73d60919e976f1d071421be4), [`8c417d0`](https://github.com/Effect-TS/effect-smol/commit/8c417d03475e5e12d00dca0c4781d0af7e66b86c), [`5419570`](https://github.com/Effect-TS/effect-smol/commit/5419570ba47ce882a3a10882707b46f66e464906), [`449c5ed`](https://github.com/Effect-TS/effect-smol/commit/449c5ed5318e8a874e730420bcf52918fa2ec80f), [`4b5ec12`](https://github.com/Effect-TS/effect-smol/commit/4b5ec12f87f95f2a3cd8fe4d5b26c6eb0529381a), [`df87937`](https://github.com/Effect-TS/effect-smol/commit/df879375fc3b169c43f9c434b3775e12b80dffe4), [`5dbfca8`](https://github.com/Effect-TS/effect-smol/commit/5dbfca8d1dbb6d18d1605d4f8562e99c86e2ff11), [`e629497`](https://github.com/Effect-TS/effect-smol/commit/e6294973d55597ab6b6deca6babbe1e946b2c91d), [`981c991`](https://github.com/Effect-TS/effect-smol/commit/981c991cd78db34def815d5754379d737157f005), [`1ca2ed6`](https://github.com/Effect-TS/effect-smol/commit/1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe), [`45722bd`](https://github.com/Effect-TS/effect-smol/commit/45722bde974458311f11ad237711363a10ec6894), [`eb2a85e`](https://github.com/Effect-TS/effect-smol/commit/eb2a85ed4dc162b2535d304799333a5a20477fd0)]:
- effect@4.0.0-beta.6
## 4.0.0-beta.5
### Patch Changes
- Updated dependencies [[`f6e133e`](https://github.com/Effect-TS/effect-smol/commit/f6e133e9a16b32317bd09ff08c12b97a0ae44600), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8), [`a88e206`](https://github.com/Effect-TS/effect-smol/commit/a88e206e44dc66ca5a2b45bedc797877c5dbb083), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8)]:
- effect@4.0.0-beta.5
## 4.0.0-beta.4
### Patch Changes
- Updated dependencies [[`c5a18ef`](https://github.com/Effect-TS/effect-smol/commit/c5a18ef44171e3880bf983faee74529908974b32), [`bc6b885`](https://github.com/Effect-TS/effect-smol/commit/bc6b885b94d887a200657c0775dfa874dc15bc0c)]:
- effect@4.0.0-beta.4
## 4.0.0-beta.3
### Patch Changes
- Updated dependencies [[`3a0cf36`](https://github.com/Effect-TS/effect-smol/commit/3a0cf36eff106ba48d74e133c1598cd40613e530), [`c4da328`](https://github.com/Effect-TS/effect-smol/commit/c4da328d32fad1d61e0e538f5d371edf61521d7e)]:
- effect@4.0.0-beta.3
## 4.0.0-beta.2
### Patch Changes
- Updated dependencies [[`a22ce73`](https://github.com/Effect-TS/effect-smol/commit/a22ce73b2bd9305b7ba665694d2255c0e6d5a8d0), [`ebdabf7`](https://github.com/Effect-TS/effect-smol/commit/ebdabf79ff4e62c8384aa8cf9a8d2787d536ee78), [`8f663bb`](https://github.com/Effect-TS/effect-smol/commit/8f663bb121021bf12bd264e8ae385187cb7a5dae)]:
- effect@4.0.0-beta.2
## 4.0.0-beta.1
### Patch Changes
- Updated dependencies [[`0fecf70`](https://github.com/Effect-TS/effect-smol/commit/0fecf70048057623eed7c584a06671773a2b1743), [`709569e`](https://github.com/Effect-TS/effect-smol/commit/709569ed76bead9ebb0670599e4d890a07ca5a43)]:
- effect@4.0.0-beta.1
## 4.0.0-beta.0
### Major Changes
- [#1183](https://github.com/Effect-TS/effect-smol/pull/1183) [`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66) Thanks @tim-smart! - v4 beta
### Patch Changes
- Updated dependencies [[`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66)]:
- effect@4.0.0-beta.0

View File

@@ -0,0 +1,41 @@
# yaml-language-server: $schema=../../tools/ai-codegen/codegen.schema.json
spec: https://app.stainless.com/api/spec/documented/openai/openapi.documented.yml
output: src/Generated.ts
name: OpenAiClient
header: |
/**
* @since 1.0.0
*/
patches:
- '[{"op":"replace","path":"/openapi","value":"3.0.0"}]'
- '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/user/nullable","value":true}]'
- '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/safety_identifier/nullable","value":true}]'
- '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/prompt_cache_key/nullable","value":true}]'
- '[{"op":"add","path":"/components/schemas/Response/allOf/2/properties/usage/nullable","value":true}]'
- '[{"op":"remove","path":"/components/schemas/ResponseFunctionCallArgumentsDoneEvent/required/2"}]'
- '[{"op":"remove","path":"/components/schemas/WebSearchActionSearch/required/1"}]'
- '[{"op":"add","path":"/components/schemas/ModelResponseProperties/properties/prompt_cache_retention/anyOf/0/enum/1","value":"in_memory"}]'
- '[{"op":"add","path":"/components/schemas/PromptCacheRetentionEnum/enum/1","value":"in-memory"}]'
- '[{"op":"replace","path":"/components/schemas/OpenAIFile/properties/expires_at","value":{"anyOf":[{"type":"integer","format":"unixtime","description":"The Unix timestamp (in seconds) for when the file will expire."},{"type":"null"}]}}]'
- '[{"op":"add","path":"/components/schemas/OpenAIFile/properties/status_details/nullable","value":true}]'
# Add missing keepalive stream event support
- '[{"op":"add","path":"/components/schemas/ResponseKeepAliveEvent","value":{"type":"object","title":"Keep alive","description":"A keepalive event emitted during long-running response streams.","properties":{"type":{"type":"string","enum":["keepalive"],"description":"The type of the keepalive event. Always `keepalive`.","default":"keepalive","x-stainless-const":true},"sequence_number":{"type":"integer","description":"The sequence number of this keepalive event."}},"required":["type","sequence_number"]}}]'
- '[{"op":"add","path":"/components/schemas/ResponseStreamEvent/anyOf/-","value":{"$ref":"#/components/schemas/ResponseKeepAliveEvent"}}]'
# Add missing ResponseApplyPatchCallOperationDiffDeltaEvent schema
- '[{"op":"add","path":"/components/schemas/ResponseApplyPatchCallOperationDiffDeltaEvent","value":{"title":"ResponseApplyPatchCallOperationDiffDelta","type":"object","description":"Event representing a delta for an apply_patch tool call operation diff.","properties":{"type":{"type":"string","enum":["response.apply_patch_call_operation_diff.delta"],"description":"The event type identifier.","x-stainless-const":true},"sequence_number":{"type":"integer","description":"The sequence number of this event."},"output_index":{"type":"integer","description":"The index of the output this delta applies to."},"item_id":{"type":"string","description":"Unique identifier for the API item associated with this event."},"delta":{"type":"string","description":"The incremental diff data for the apply_patch tool call."}},"required":["type","output_index","item_id","delta","sequence_number"]}}]'
# Add missing ResponseApplyPatchCallOperationDiffDoneEvent schema (delta is optional - not always sent by OpenAI)
- '[{"op":"add","path":"/components/schemas/ResponseApplyPatchCallOperationDiffDoneEvent","value":{"title":"ResponseApplyPatchCallOperationDiffDone","type":"object","description":"Event indicating that the operation diff for an apply_patch tool call is complete.","properties":{"type":{"type":"string","enum":["response.apply_patch_call_operation_diff.done"],"description":"The event type identifier.","x-stainless-const":true},"sequence_number":{"type":"integer","description":"The sequence number of this event."},"output_index":{"type":"integer","description":"The index of the output this event applies to."},"item_id":{"type":"string","description":"Unique identifier for the API item associated with this event."},"delta":{"type":"string","description":"The final diff data for the apply_patch tool call."}},"required":["type","output_index","item_id","sequence_number"]}}]'
# Add both events to ResponseStreamEvent anyOf array
- '[{"op":"add","path":"/components/schemas/ResponseStreamEvent/anyOf/-","value":{"$ref":"#/components/schemas/ResponseApplyPatchCallOperationDiffDeltaEvent"}}]'
- '[{"op":"add","path":"/components/schemas/ResponseStreamEvent/anyOf/-","value":{"$ref":"#/components/schemas/ResponseApplyPatchCallOperationDiffDoneEvent"}}]'
excludeAnnotations:
- examples
- default
disableAdditionalProperties: true
replacements:
# Schema.Unknown doesn't work with Schema.toCodecJson (used by HttpClientResponse.schemaBodyJson)
# Replace with Schema.Json which properly handles arbitrary JSON values
- from: "Schema.Record(Schema.String, Schema.Unknown)"
to: "Schema.Record(Schema.String, Schema.Json)"
- from: "{ readonly [x: string]: unknown }"
to: "{ readonly [x: string]: Schema.Json }"

View File

@@ -0,0 +1,23 @@
{
"$schema": "../../node_modules/@effect/docgen/schema.json",
"srcLink": "https://github.com/Effect-TS/effect/tree/main/packages/ai/openai/src/",
"exclude": ["src/Generated.ts", "src/internal/**/*.ts"],
"examplesCompilerOptions": {
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "Bundler",
"module": "ES2022",
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"rewriteRelativeImportExtensions": true,
"allowImportingTsExtensions": true,
"paths": {
"effect": ["../../../effect/src/index.js"],
"effect/*": ["../../../effect/src/*.js"]
},
"plugins": [
{ "name": "@effect/language-service", "includeSuggestionsInTsc": false }
]
}
}

View File

@@ -0,0 +1,66 @@
{
"name": "@effect/ai-openai",
"version": "4.0.0-beta.99",
"type": "module",
"license": "MIT",
"description": "An OpenAI provider integration for Effect AI SDK",
"homepage": "https://effect.website",
"repository": {
"type": "git",
"url": "https://github.com/Effect-TS/effect.git",
"directory": "packages/ai/openai"
},
"bugs": {
"url": "https://github.com/Effect-TS/effect/issues"
},
"tags": [
"typescript",
"ai",
"openai"
],
"keywords": [
"typescript",
"ai",
"openai"
],
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts",
"./*": "./src/*.ts",
"./internal/*": null,
"./*/index": null
},
"files": [
"src/**/*.ts",
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map"
],
"publishConfig": {
"access": "public",
"provenance": true,
"exports": {
"./package.json": "./package.json",
".": "./dist/index.js",
"./*": "./dist/*.js",
"./internal/*": null,
"./*/index": null
}
},
"scripts": {
"codegen": "effect-utils codegen",
"build": "tsc -b tsconfig.json && pnpm babel",
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
"check": "tsc -b tsconfig.json",
"test": "vitest",
"coverage": "vitest --coverage"
},
"devDependencies": {
"effect": "workspace:^"
},
"peerDependencies": {
"effect": "workspace:^"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,730 @@
/**
* The `OpenAiClient` module defines the low-level Effect service used by the
* OpenAI integration for Responses API and embedding requests. It builds a
* configured HTTP client with authentication and OpenAI organization or project
* headers, exposes helpers for non-streaming responses, SSE response streams,
* WebSocket response streams, and embeddings, and maps transport or decoding
* failures into `AiError`.
*
* @since 4.0.0
*/
import * as Array from "effect/Array"
import type * as Config from "effect/Config"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { identity } from "effect/Function"
import * as Function from "effect/Function"
import * as Layer from "effect/Layer"
import * as Predicate from "effect/Predicate"
import * as Queue from "effect/Queue"
import * as RcRef from "effect/RcRef"
import * as Redacted from "effect/Redacted"
import * as Schema from "effect/Schema"
import * as Scope from "effect/Scope"
import * as Semaphore from "effect/Semaphore"
import * as Stream from "effect/Stream"
import * as AiError from "effect/unstable/ai/AiError"
import * as ResponseIdTracker from "effect/unstable/ai/ResponseIdTracker"
import * as Sse from "effect/unstable/encoding/Sse"
import * as Headers from "effect/unstable/http/Headers"
import * as HttpBody from "effect/unstable/http/HttpBody"
import * as HttpClient from "effect/unstable/http/HttpClient"
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import * as Socket from "effect/unstable/socket/Socket"
import * as Errors from "./internal/errors.ts"
import { OpenAiConfig } from "./OpenAiConfig.ts"
import * as OpenAiSchema from "./OpenAiSchema.ts"
// =============================================================================
// Service Interface
// =============================================================================
/**
* Effect service interface for the handwritten OpenAI client.
*
* **Details**
*
* Provides the configured HTTP client plus helpers for Responses API calls, streaming Responses events, and embeddings. Transport and schema decoding failures are mapped to `AiError`.
*
* @category models
* @since 4.0.0
*/
export interface Service {
/**
* The transformed HTTP client used by this service.
*/
readonly client: HttpClient.HttpClient
/**
* Create a response using the OpenAI responses endpoint.
*/
readonly createResponse: (
options: typeof OpenAiSchema.CreateResponse.Encoded
) => Effect.Effect<
readonly [body: typeof OpenAiSchema.Response.Type, response: HttpClientResponse.HttpClientResponse],
AiError.AiError
>
/**
* Create a streaming response using the OpenAI responses endpoint.
*/
readonly createResponseStream: (
options: Omit<typeof OpenAiSchema.CreateResponse.Encoded, "stream">
) => Effect.Effect<
readonly [
response: HttpClientResponse.HttpClientResponse,
stream: Stream.Stream<typeof OpenAiSchema.ResponseStreamEvent.Type, AiError.AiError>
],
AiError.AiError
>
/**
* Create embeddings using the OpenAI embeddings endpoint.
*/
readonly createEmbedding: (
options: typeof OpenAiSchema.CreateEmbeddingRequest.Encoded
) => Effect.Effect<typeof OpenAiSchema.CreateEmbeddingResponse.Type, AiError.AiError>
}
// =============================================================================
// Service Identifier
// =============================================================================
/**
* Service tag for the OpenAI client.
*
* **When to use**
*
* Use when accessing or providing the OpenAI client service through Effect's
* context.
*
* @see {@link make} for constructing an OpenAI client effectfully
* @see {@link layer} for providing a client from explicit options
* @see {@link layerConfig} for providing a client from `Config`
*
* @category services
* @since 4.0.0
*/
export class OpenAiClient extends Context.Service<OpenAiClient, Service>()(
"@effect/ai-openai/OpenAiClient"
) {}
// =============================================================================
// Options
// =============================================================================
/**
* Options for configuring the OpenAI client.
*
* @category options
* @since 4.0.0
*/
export type Options = {
/**
* The OpenAI API key.
*/
readonly apiKey?: Redacted.Redacted<string> | undefined
/**
* The base URL for the OpenAI API.
*
* @default "https://api.openai.com/v1"
*/
readonly apiUrl?: string | undefined
/**
* Optional organization ID for multi-org accounts.
*/
readonly organizationId?: Redacted.Redacted<string> | undefined
/**
* Optional project ID for project-scoped requests.
*/
readonly projectId?: Redacted.Redacted<string> | undefined
/**
* Optional transformer for the HTTP client.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}
// =============================================================================
// Constructor
// =============================================================================
const RedactedOpenAiHeaders = {
OpenAiOrganization: "OpenAI-Organization",
OpenAiProject: "OpenAI-Project"
}
/**
* Creates an OpenAI client service with the given options.
*
* **When to use**
*
* Use when you need the OpenAI client service value inside an effect.
*
* **Details**
*
* The returned service uses the current `HttpClient`, prepends `apiUrl` or
* `https://api.openai.com/v1`, adds the bearer token and optional OpenAI
* organization/project headers, accepts JSON responses, filters for successful
* HTTP statuses, and applies `transformClient` when provided.
*
* **Gotchas**
*
* A scoped `OpenAiConfig.withClientTransform` is applied when request helpers
* run, after the `transformClient` option supplied to `make`.
*
* @see {@link layer} for providing this client from explicit options
* @see {@link layerConfig} for loading client settings from `Config`
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(
function*(
options: Options
): Effect.fn.Return<Service, never, HttpClient.HttpClient> {
const baseClient = yield* HttpClient.HttpClient
const apiUrl = options.apiUrl ?? "https://api.openai.com/v1"
const httpClient = baseClient.pipe(
HttpClient.mapRequest(Function.flow(
HttpClientRequest.prependUrl(apiUrl),
options.apiKey
? HttpClientRequest.bearerToken(Redacted.value(options.apiKey))
: identity,
options.organizationId
? HttpClientRequest.setHeader(
RedactedOpenAiHeaders.OpenAiOrganization,
Redacted.value(options.organizationId)
)
: identity,
options.projectId
? HttpClientRequest.setHeader(
RedactedOpenAiHeaders.OpenAiProject,
Redacted.value(options.projectId)
)
: identity,
HttpClientRequest.acceptJson
)),
HttpClient.filterStatusOk,
options.transformClient
? options.transformClient
: identity
)
const resolveHttpClient = Effect.map(
OpenAiConfig.getOrUndefined,
(config) =>
Predicate.isNotUndefined(config?.transformClient)
? config.transformClient(httpClient)
: httpClient
)
const decodeResponse = HttpClientResponse.schemaBodyJson(OpenAiSchema.Response)
const createResponse = (
payload: typeof OpenAiSchema.CreateResponse.Encoded
): Effect.Effect<
[body: typeof OpenAiSchema.Response.Type, response: HttpClientResponse.HttpClientResponse],
AiError.AiError
> =>
Effect.flatMap(resolveHttpClient, (client) =>
client.execute(
HttpClientRequest.post("/responses", {
body: HttpBody.jsonUnsafe(payload)
})
).pipe(
Effect.flatMap((response) =>
decodeResponse(response).pipe(
Effect.map((body): [typeof OpenAiSchema.Response.Type, HttpClientResponse.HttpClientResponse] => [
body,
response
])
)
),
Effect.catchTags({
HttpClientError: (error) => Errors.mapHttpClientError(error, "createResponse"),
SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createResponse"))
})
))
const buildResponseStream = (
response: HttpClientResponse.HttpClientResponse
): [
HttpClientResponse.HttpClientResponse,
Stream.Stream<typeof OpenAiSchema.ResponseStreamEvent.Type, AiError.AiError>
] => {
const stream = response.stream.pipe(
Stream.decodeText(),
Stream.pipeThroughChannel(Sse.decodeDataSchema(OpenAiSchema.ResponseStreamEvent)),
Stream.takeUntil((event) =>
event.data.type === "response.completed" ||
event.data.type === "response.incomplete"
),
Stream.map((event) => event.data),
Stream.catchTags({
// TODO: handle SSE retries
Retry: (error) => Stream.die(error),
HttpClientError: (error) => Stream.fromEffect(Errors.mapHttpClientError(error, "createResponseStream")),
SchemaError: (error) => Stream.fail(Errors.mapSchemaError(error, "createResponseStream"))
})
)
return [response, stream]
}
const createResponseStream: Service["createResponseStream"] = (payload) =>
Effect.contextWith((services) => {
const socket = Context.getOrUndefined(services, OpenAiSocket)
if (socket) return socket.createResponseStream(payload)
return Effect.flatMap(resolveHttpClient, (client) =>
client.execute(
HttpClientRequest.post("/responses", {
body: HttpBody.jsonUnsafe({ ...payload, stream: true })
})
).pipe(
Effect.map(buildResponseStream),
Effect.catchTag(
"HttpClientError",
(error) => Errors.mapHttpClientError(error, "createResponseStream")
)
))
})
const decodeEmbedding = HttpClientResponse.schemaBodyJson(OpenAiSchema.CreateEmbeddingResponse)
const createEmbedding = (
payload: typeof OpenAiSchema.CreateEmbeddingRequest.Encoded
): Effect.Effect<typeof OpenAiSchema.CreateEmbeddingResponse.Type, AiError.AiError> =>
Effect.flatMap(resolveHttpClient, (client) =>
client.execute(
HttpClientRequest.post("/embeddings", {
body: HttpBody.jsonUnsafe(payload)
})
).pipe(
Effect.flatMap(decodeEmbedding),
Effect.catchTags({
HttpClientError: (error) => Errors.mapHttpClientError(error, "createEmbedding"),
SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createEmbedding"))
})
))
return OpenAiClient.of({
client: httpClient,
createResponse,
createResponseStream,
createEmbedding
})
},
Effect.updateService(
Headers.CurrentRedactedNames,
Array.appendAll(Object.values(RedactedOpenAiHeaders))
)
)
// =============================================================================
// Layers
// =============================================================================
/**
* Creates a layer for the OpenAI client with the given options.
*
* **When to use**
*
* Use when you already have explicit `Options` values, such as an API key or
* custom API URL, and want to provide `OpenAiClient` as a `Layer`.
*
* @see {@link make} for constructing the client service effectfully
* @see {@link layerConfig} for loading client settings from `Config`
*
* @category layers
* @since 4.0.0
*/
export const layer = (options: Options): Layer.Layer<OpenAiClient, never, HttpClient.HttpClient> =>
Layer.effect(OpenAiClient, make(options))
/**
* Creates a layer for the OpenAI client from provided `Config` values.
*
* **When to use**
*
* Use when you need client settings for OpenAI-compatible APIs to be read from
* Effect `Config` values while providing `OpenAiClient` as a `Layer`.
*
* **Details**
*
* Only config values supplied in `options` are loaded. Omitted fields are
* passed to `make` as `undefined`, and `transformClient` is forwarded as a
* plain option.
*
* @see {@link make} for constructing the client service effectfully
* @see {@link layer} for providing the client from already-resolved options
*
* @category layers
* @since 4.0.0
*/
export const layerConfig = (options?: {
/**
* The config value to load for the API key.
*/
readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The config value to load for the API URL.
*/
readonly apiUrl?: Config.Config<string> | undefined
/**
* The config value to load for the organization ID.
*/
readonly organizationId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The config value to load for the project ID.
*/
readonly projectId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* Optional transformer for the HTTP client.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}): Layer.Layer<OpenAiClient, Config.ConfigError, HttpClient.HttpClient> =>
Layer.effect(
OpenAiClient,
Effect.gen(function*() {
const apiKey = Predicate.isNotUndefined(options?.apiKey)
? yield* options.apiKey :
undefined
const apiUrl = Predicate.isNotUndefined(options?.apiUrl)
? yield* options.apiUrl :
undefined
const organizationId = Predicate.isNotUndefined(options?.organizationId)
? yield* options.organizationId
: undefined
const projectId = Predicate.isNotUndefined(options?.projectId)
? yield* options.projectId :
undefined
return yield* make({
apiKey,
apiUrl,
organizationId,
projectId,
transformClient: options?.transformClient
})
})
)
// =============================================================================
// Websocket mode
// =============================================================================
/**
* Response stream event emitted by the OpenAI Responses API.
*
* @category Events
* @since 4.0.0
*/
export type ResponseStreamEvent = typeof OpenAiSchema.ResponseStreamEvent.Type
/**
* Service for creating OpenAI response streams over a WebSocket connection.
*
* **When to use**
*
* Use when you need direct access to the WebSocket-backed response streaming
* service rather than wrapping an effect with WebSocket mode.
*
* **Details**
*
* `createResponseStream` sends a `response.create` message over the WebSocket
* connection and returns an HTTP response together with a stream of
* `ResponseStreamEvent` values.
*
* **Gotchas**
*
* WebSocket response streams are serialized to one request at a time by the
* shared socket service.
*
* @see {@link withWebSocketMode} for enabling WebSocket mode for one effect
* @see {@link layerWebSocketMode} for providing WebSocket mode through a layer
*
* @category Websocket mode
* @since 4.0.0
*/
export class OpenAiSocket extends Context.Service<OpenAiSocket, {
/**
* Create a streaming response using the OpenAI responses endpoint.
*/
readonly createResponseStream: (
options: Omit<typeof OpenAiSchema.CreateResponse.Encoded, "stream">
) => Effect.Effect<
readonly [
response: HttpClientResponse.HttpClientResponse,
stream: Stream.Stream<ResponseStreamEvent, AiError.AiError>
],
AiError.AiError
>
}>()("@effect/ai-openai/OpenAiClient/OpenAiSocket") {}
const makeSocket = Effect.gen(function*() {
const client = yield* OpenAiClient
const tracker = yield* ResponseIdTracker.make
const socketScope = yield* Effect.scope
const makeRequest = Effect.flatMap(
OpenAiConfig.getOrUndefined,
(config) => {
const httpClient = Predicate.isNotUndefined(config?.transformClient)
? config.transformClient(client.client)
: client.client
return Effect.orDie(httpClient.preprocess(HttpClientRequest.post("/responses")))
}
)
const makeWebSocket = yield* Socket.WebSocketConstructor
const decoder = new TextDecoder()
const queueRef: RcRef.RcRef<
{
readonly send: (message: typeof OpenAiSchema.CreateResponse.Encoded) => Effect.Effect<void, AiError.AiError>
readonly incoming: Queue.Dequeue<ResponseStreamEvent, AiError.AiError>
}
> = yield* RcRef.make({
idleTimeToLive: 60_000,
acquire: Effect.gen(function*() {
const scope = yield* Effect.scope
const request = yield* makeRequest
const socket = yield* Socket.makeWebSocket(request.url.replace(/^http/, "ws")).pipe(
Effect.provideService(Socket.WebSocketConstructor, (url) =>
makeWebSocket(url, {
headers: request.headers
} as any))
)
const write = yield* socket.writer
yield* Scope.addFinalizerExit(scope, () => {
tracker.clearUnsafe()
return Effect.void
})
const incoming = yield* Queue.unbounded<ResponseStreamEvent, AiError.AiError>()
const send = (message: typeof OpenAiSchema.CreateResponse.Encoded) =>
write(JSON.stringify({
type: "response.create",
...message
})).pipe(
Effect.mapError((_error) =>
AiError.make({
module: "OpenAiClient",
method: "createResponseStream",
reason: new AiError.NetworkError({
reason: "TransportError",
request: {
method: "POST",
url: request.url,
urlParams: [],
hash: undefined,
headers: request.headers
},
description: "Failed to send message over WebSocket"
})
})
)
)
yield* socket.runRaw((msg) => {
const text = typeof msg === "string" ? msg : decoder.decode(msg)
try {
const event = decodeEvent(text)
if (event.type === "error" && "status" in event) {
const status = Number(event.status)
const error = "error" in event ? event.error as typeof ErrorEvent.Type.error : event
const json = JSON.stringify(error)
return Effect.fail(
AiError.make({
module: "OpenAiClient",
method: "createResponseStream",
reason: AiError.reasonFromHttpStatus({
description: json,
status: isNaN(status) ? errorTypeToStatus[error.type] ?? 500 : status,
metadata: error as any,
http: {
body: json,
request: {
method: "POST",
url: request.url,
urlParams: [],
hash: undefined,
headers: request.headers
}
}
})
})
)
}
Queue.offerUnsafe(incoming, event)
} catch {}
}).pipe(
Effect.catchTag("SocketError", (error) =>
AiError.make({
module: "OpenAiClient",
method: "createResponseStream",
reason: new AiError.NetworkError({
reason: "TransportError",
request: {
method: "POST",
url: request.url,
urlParams: [],
hash: undefined,
headers: request.headers
},
description: error.message
})
})),
Effect.catchCause((cause) => Queue.failCause(incoming, cause)),
Effect.ensuring(Effect.forkIn(RcRef.invalidate(queueRef), socketScope, {
startImmediately: true
})),
Effect.forkScoped({ startImmediately: true })
)
return { send, incoming } as const
})
})
// Prime the websocket
yield* Effect.scoped(RcRef.get(queueRef))
// Websocket mode only allows one request at a time
const semaphore = Semaphore.makeUnsafe(1)
const request = yield* makeRequest
return OpenAiSocket.context({
createResponseStream(options) {
const stream = Stream.unwrap(Effect.gen(function*() {
const scope = yield* Effect.scope
yield* Effect.acquireRelease(
semaphore.take(1),
() => semaphore.release(1),
{ interruptible: true }
)
const { send, incoming } = yield* RcRef.get(queueRef)
let done = false
yield* Scope.addFinalizerExit(
scope,
() => done ? Effect.void : RcRef.invalidate(queueRef)
)
yield* send(options).pipe(
Effect.forkScoped({ startImmediately: true })
)
return Stream.fromQueue(incoming).pipe(
Stream.takeUntil((e) => {
done = e.type === "response.completed" || e.type === "response.incomplete"
return done
})
)
}))
return Effect.succeed([
HttpClientResponse.fromWeb(request, new Response()),
stream
])
}
}).pipe(
Context.add(ResponseIdTracker.ResponseIdTracker, tracker)
)
})
const ErrorEvent = Schema.Struct({
type: Schema.Literal("error"),
status: Schema.Number.pipe(
Schema.withDecodingDefault(Effect.succeed(500))
),
error: Schema.Struct({
type: Schema.String,
message: Schema.String
})
})
const errorTypeToStatus: Record<string, number> = {
invalid_request_error: 400,
invalid_api_key_error: 401,
insufficient_quota_error: 429,
rate_limit_error: 429,
service_unavailable_error: 503
}
const AllEvents = Schema.Union([ErrorEvent, OpenAiSchema.ResponseStreamEvent])
const decodeEvent = Schema.decodeUnknownSync(Schema.fromJsonString(AllEvents))
/**
* Uses OpenAI's WebSocket mode for response streams within the provided effect.
*
* **When to use**
*
* Use to enable WebSocket mode around one effect that creates OpenAI response
* streams.
*
* **Gotchas**
*
* This only works with the following WebSocket constructor layers:
*
* - `NodeSocket.layerWebSocketConstructorWS`
* - `BunSocket.layerWebSocketConstructor`
*
* These constructor layers support the non-standard options needed to set the
* Authorization header.
*
* @see {@link layerWebSocketMode} for providing WebSocket mode through a layer
* @see {@link OpenAiSocket} for direct access to the WebSocket-backed streaming service
*
* @category Websocket mode
* @since 4.0.0
*/
export const withWebSocketMode = <A, E, R>(
effect: Effect.Effect<A, E, R>
): Effect.Effect<
A,
E,
Exclude<R, OpenAiSocket | ResponseIdTracker.ResponseIdTracker> | OpenAiClient | Socket.WebSocketConstructor
> =>
Effect.scopedWith((scope) =>
Effect.flatMap(
Scope.provide(makeSocket, scope),
(services) => Effect.provideContext(effect, services)
)
)
/**
* Uses OpenAI's websocket mode for all responses that use the Layer.
*
* **When to use**
*
* Use to provide WebSocket mode through layer composition for effects that use
* OpenAI response streaming.
*
* **Gotchas**
*
* This only works with the following WebSocket constructor layers:
*
* - `NodeSocket.layerWebSocketConstructorWS`
* - `BunSocket.layerWebSocketConstructor`
*
* These constructor layers support the non-standard options needed to set the
* Authorization header.
*
* @see {@link withWebSocketMode} for enabling WebSocket mode around a single effect
*
* @category Websocket mode
* @since 4.0.0
*/
export const layerWebSocketMode: Layer.Layer<
OpenAiSocket | ResponseIdTracker.ResponseIdTracker,
never,
OpenAiClient | Socket.WebSocketConstructor
> = Layer.effectContext(makeSocket)

View File

@@ -0,0 +1,202 @@
/**
* @since 4.0.0
*/
import * as Array from "effect/Array"
import type * as Config from "effect/Config"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { identity } from "effect/Function"
import * as Function from "effect/Function"
import * as Layer from "effect/Layer"
import * as Predicate from "effect/Predicate"
import * as Redacted from "effect/Redacted"
import * as Headers from "effect/unstable/http/Headers"
import * as HttpClient from "effect/unstable/http/HttpClient"
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import * as Generated from "./Generated.ts"
import { OpenAiConfig } from "./OpenAiConfig.ts"
// =============================================================================
// Service Identifier
// =============================================================================
/**
* Service identifier for the generated OpenAI client.
*
* @since 4.0.0
* @category service
*/
export class OpenAiClientGenerated extends Context.Service<OpenAiClientGenerated, Generated.OpenAiClient>()(
"@effect/ai-openai/OpenAiClientGenerated"
) {}
// =============================================================================
// Options
// =============================================================================
/**
* Options for configuring the generated OpenAI client.
*
* @since 4.0.0
* @category options
*/
export type Options = {
/**
* The OpenAI API key.
*/
readonly apiKey?: Redacted.Redacted<string> | undefined
/**
* The base URL for the OpenAI API.
*
* @default "https://api.openai.com/v1"
*/
readonly apiUrl?: string | undefined
/**
* Optional organization ID for multi-org accounts.
*/
readonly organizationId?: Redacted.Redacted<string> | undefined
/**
* Optional project ID for project-scoped requests.
*/
readonly projectId?: Redacted.Redacted<string> | undefined
/**
* Optional transformer for the HTTP client.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}
const RedactedOpenAiHeaders = {
OpenAiOrganization: "OpenAI-Organization",
OpenAiProject: "OpenAI-Project"
}
// =============================================================================
// Constructor
// =============================================================================
/**
* Creates a generated OpenAI client service with the given options.
*
* @since 4.0.0
* @category constructors
*/
export const make = Effect.fnUntraced(
function*(options: Options): Effect.fn.Return<Generated.OpenAiClient, never, HttpClient.HttpClient> {
const baseClient = yield* HttpClient.HttpClient
const apiUrl = options.apiUrl ?? "https://api.openai.com/v1"
const httpClient = baseClient.pipe(
HttpClient.mapRequest(Function.flow(
HttpClientRequest.prependUrl(apiUrl),
options.apiKey
? HttpClientRequest.bearerToken(Redacted.value(options.apiKey))
: identity,
options.organizationId
? HttpClientRequest.setHeader(
RedactedOpenAiHeaders.OpenAiOrganization,
Redacted.value(options.organizationId)
)
: identity,
options.projectId
? HttpClientRequest.setHeader(
RedactedOpenAiHeaders.OpenAiProject,
Redacted.value(options.projectId)
)
: identity,
HttpClientRequest.acceptJson
)),
options.transformClient
? options.transformClient
: identity
)
return Generated.make(httpClient, {
transformClient: Effect.fnUntraced(function*(client) {
const config = yield* OpenAiConfig.getOrUndefined
if (Predicate.isNotUndefined(config?.transformClient)) {
return config.transformClient(client)
}
return client
})
})
},
Effect.updateService(
Headers.CurrentRedactedNames,
Array.appendAll(Object.values(RedactedOpenAiHeaders))
)
)
// =============================================================================
// Layers
// =============================================================================
/**
* Creates a layer for the generated OpenAI client with the given options.
*
* @since 4.0.0
* @category layers
*/
export const layer = (options: Options): Layer.Layer<OpenAiClientGenerated, never, HttpClient.HttpClient> =>
Layer.effect(OpenAiClientGenerated, make(options))
/**
* Creates a layer for the generated OpenAI client, loading the requisite
* configuration via Effect's `Config` module.
*
* @since 4.0.0
* @category layers
*/
export const layerConfig = (options?: {
/**
* The config value to load for the API key.
*/
readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The config value to load for the API URL.
*/
readonly apiUrl?: Config.Config<string> | undefined
/**
* The config value to load for the organization ID.
*/
readonly organizationId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The config value to load for the project ID.
*/
readonly projectId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* Optional transformer for the HTTP client.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}): Layer.Layer<OpenAiClientGenerated, Config.ConfigError, HttpClient.HttpClient> =>
Layer.effect(
OpenAiClientGenerated,
Effect.gen(function*() {
const apiKey = Predicate.isNotUndefined(options?.apiKey)
? yield* options.apiKey :
undefined
const apiUrl = Predicate.isNotUndefined(options?.apiUrl)
? yield* options.apiUrl :
undefined
const organizationId = Predicate.isNotUndefined(options?.organizationId)
? yield* options.organizationId
: undefined
const projectId = Predicate.isNotUndefined(options?.projectId)
? yield* options.projectId :
undefined
return yield* make({
apiKey,
apiUrl,
organizationId,
projectId,
transformClient: options?.transformClient
})
})
)

View File

@@ -0,0 +1,93 @@
/**
* The `OpenAiConfig` module lets a workflow temporarily customize the HTTP
* client used by `@effect/ai-openai` request helpers. OpenAI client, language
* model, and embedding code read this scoped transform when they execute
* provider calls.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import type { HttpClient } from "effect/unstable/http/HttpClient"
/**
* Context service for scoped OpenAI configuration used by provider operations.
*
* **When to use**
*
* Use to provide scoped OpenAI client configuration, such as an HTTP client
* transform, to OpenAI provider operations without passing it through each call.
*
* @see {@link withClientTransform} for scoping an HTTP client transformation
*
* @category services
* @since 4.0.0
*/
export class OpenAiConfig extends Context.Service<
OpenAiConfig,
OpenAiConfig.Service
>()("@effect/ai-openai/OpenAiConfig") {
/**
* Gets the configured OpenAI service from the current context when present.
*
* @since 4.0.0
*/
static readonly getOrUndefined: Effect.Effect<typeof OpenAiConfig.Service | undefined> = Effect.map(
Effect.context<never>(),
(context) => context.mapUnsafe.get(OpenAiConfig.key)
)
}
/**
* Types used by the `OpenAiConfig` context service.
*
* @since 4.0.0
*/
export declare namespace OpenAiConfig {
/**
* Configuration values read by OpenAI provider operations when executing
* requests.
*
* @category models
* @since 4.0.0
*/
export interface Service {
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined
}
}
/**
* Provides a scoped transform for the OpenAI HTTP client used by provider
* operations.
*
* **When to use**
*
* Use when you need temporary OpenAI HTTP client customization for a single
* effect or workflow without rebuilding the client layer.
*
* **Details**
*
* Supports both data-first and data-last forms. The transform is stored in the
* scoped `OpenAiConfig` service and read by OpenAI provider operations while
* running the supplied effect.
*
* **Gotchas**
*
* If a transform is already present in the scoped config, this helper replaces
* it. Compose transforms manually when both should apply.
*
* @category configuration
* @since 4.0.0
*/
export const withClientTransform: {
(transform: (client: HttpClient) => HttpClient): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<A, E, R>(self: Effect.Effect<A, E, R>, transform: (client: HttpClient) => HttpClient): Effect.Effect<A, E, R>
} = dual(2, <A, E, R>(
self: Effect.Effect<A, E, R>,
transformClient: (client: HttpClient) => HttpClient
) =>
Effect.flatMap(
OpenAiConfig.getOrUndefined,
(config) => Effect.provideService(self, OpenAiConfig, { ...config, transformClient })
))

View File

@@ -0,0 +1,258 @@
/**
* The `OpenAiEmbeddingModel` module provides the OpenAI implementation of
* Effect AI's `EmbeddingModel` service. It sends embedding requests through
* `OpenAiClient`, exposes constructors for layers and `AiModel` values,
* supports scoped request configuration overrides, and checks that OpenAI
* returns one numeric vector for each requested input.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Layer from "effect/Layer"
import type { Simplify } from "effect/Types"
import * as AiError from "effect/unstable/ai/AiError"
import * as EmbeddingModel from "effect/unstable/ai/EmbeddingModel"
import * as AiModel from "effect/unstable/ai/Model"
import { OpenAiClient } from "./OpenAiClient.ts"
import type * as OpenAiSchema from "./OpenAiSchema.ts"
/**
* Model identifiers supported by OpenAI's embeddings API.
*
* @category models
* @since 4.0.0
*/
export type Model = "text-embedding-ada-002" | "text-embedding-3-small" | "text-embedding-3-large"
/**
* Context service for OpenAI embedding model configuration.
*
* **When to use**
*
* Use when you need scoped OpenAI request defaults or overrides for embedding
* requests from Effect context.
*
* **Details**
*
* The service stores the OpenAI create-embedding request payload without
* `input`, carrying options such as `model`, `dimensions`, `encoding_format`,
* and `user`.
*
* @see {@link withConfigOverride} for scoping embedding request overrides
*
* @category services
* @since 4.0.0
*/
export class Config extends Context.Service<
Config,
Simplify<
& Partial<
Omit<
typeof OpenAiSchema.CreateEmbeddingRequest.Encoded,
"input"
>
>
& {
readonly [x: string]: unknown
}
>
>()("@effect/ai-openai/OpenAiEmbeddingModel/Config") {}
/**
* Creates an `AiModel` for an OpenAI embedding model with its configured vector dimensions.
*
* **When to use**
*
* Use to provide an OpenAI `EmbeddingModel` and its `Dimensions` service to an
* Effect program.
*
* @see {@link layer} for providing only the embedding model service
* @see {@link withConfigOverride} for scoped request configuration overrides
*
* @category constructors
* @since 4.0.0
*/
export const model = (
model: (string & {}) | Model,
options: {
readonly dimensions: number
readonly config?: Omit<typeof Config.Service, "model" | "dimensions">
}
): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel | EmbeddingModel.Dimensions, OpenAiClient> =>
AiModel.make(
"openai",
model,
Layer.merge(
layer({
model,
config: {
...options.config,
dimensions: options.dimensions
}
}),
Layer.succeed(EmbeddingModel.Dimensions, options.dimensions)
)
)
/**
* Creates an OpenAI embedding model service.
*
* **When to use**
*
* Use to construct the `EmbeddingModel.Service` effectfully when
* `OpenAiClient` is already available in the environment.
*
* **Details**
*
* The `model` option is sent with each embedding request. Constructor `config`
* supplies create-embedding request fields other than `model` and `input`, and
* scoped overrides from `withConfigOverride` are merged last for each request.
*
* **Gotchas**
*
* The service expects numeric embedding vectors. It fails with
* `InvalidOutputError` when the provider returns base64 embeddings,
* out-of-range indexes, duplicate indexes, or an unexpected number of
* embeddings.
*
* @see {@link layer} for providing the embedding model service as a layer
* @see {@link model} for creating an `AiModel` that also provides dimensions
* @see {@link withConfigOverride} for scoped request configuration overrides
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
readonly model: (string & {}) | Model
readonly config?: Omit<typeof Config.Service, "model"> | undefined
}): Effect.fn.Return<EmbeddingModel.Service, never, OpenAiClient> {
const client = yield* OpenAiClient
const makeConfig = Effect.gen(function*() {
const services = yield* Effect.context<never>()
return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
})
return yield* EmbeddingModel.make({
embedMany: Effect.fnUntraced(function*({ inputs }) {
const config = yield* makeConfig
const response = yield* client.createEmbedding({ ...config, input: inputs })
return yield* mapProviderResponse(inputs.length, response)
})
})
})
/**
* Creates a layer for the OpenAI embedding model.
*
* **When to use**
*
* Use when composing application layers and you want OpenAI to satisfy
* `EmbeddingModel.EmbeddingModel` while supplying `OpenAiClient` from another
* layer.
*
* **Gotchas**
*
* Use the default floating-point embedding format. The service expects numeric
* vectors and fails with `InvalidOutputError` if OpenAI returns base64
* embeddings.
*
* @see {@link make} for constructing the embedding model service effectfully
* @see {@link model} for creating an `AiModel` that also provides embedding dimensions
*
* @category layers
* @since 4.0.0
*/
export const layer = (options: {
readonly model: (string & {}) | Model
readonly config?: Omit<typeof Config.Service, "model"> | undefined
}): Layer.Layer<EmbeddingModel.EmbeddingModel, never, OpenAiClient> =>
Layer.effect(EmbeddingModel.EmbeddingModel, make(options))
/**
* Provides config overrides for OpenAI embedding model operations.
*
* **When to use**
*
* Use when you need scoped OpenAI embedding request defaults for a single
* effect or workflow without rebuilding the embedding model service.
*
* **Details**
*
* Supports both data-first and data-last forms. Existing scoped config is read
* first, then the provided overrides are applied so override fields take
* precedence.
*
* @see {@link Config} for the scoped embedding request configuration service
*
* @category configuration
* @since 4.0.0
*/
export const withConfigOverride: {
(overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
} = dual<
(
overrides: typeof Config.Service
) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
>(2, (self, overrides) =>
Effect.flatMap(
Effect.serviceOption(Config),
(config) =>
Effect.provideService(self, Config, {
...(config._tag === "Some" ? config.value : {}),
...overrides
})
))
const mapProviderResponse = (
inputLength: number,
response: typeof OpenAiSchema.CreateEmbeddingResponse.Type
): Effect.Effect<EmbeddingModel.ProviderResponse, AiError.AiError> => {
if (response.data.length !== inputLength) {
return Effect.fail(
invalidOutput("Provider returned " + response.data.length + " embeddings but expected " + inputLength)
)
}
const results = new Array<Array<number>>(inputLength)
const seen = new Set<number>()
for (const entry of response.data) {
if (!Number.isInteger(entry.index) || entry.index < 0 || entry.index >= inputLength) {
return Effect.fail(invalidOutput("Provider returned invalid embedding index: " + entry.index))
}
if (seen.has(entry.index)) {
return Effect.fail(invalidOutput("Provider returned duplicate embedding index: " + entry.index))
}
if (!Array.isArray(entry.embedding)) {
return Effect.fail(invalidOutput("Provider returned non-vector embedding at index " + entry.index))
}
seen.add(entry.index)
results[entry.index] = [...entry.embedding]
}
if (seen.size !== inputLength) {
return Effect.fail(
invalidOutput("Provider returned embeddings for " + seen.size + " inputs but expected " + inputLength)
)
}
return Effect.succeed({
results,
usage: {
inputTokens: response.usage?.prompt_tokens
}
})
}
const invalidOutput = (description: string): AiError.AiError =>
AiError.make({
module: "OpenAiEmbeddingModel",
method: "embedMany",
reason: new AiError.InvalidOutputError({ description })
})

View File

@@ -0,0 +1,242 @@
/**
* OpenAI error metadata augmentation.
*
* Provides OpenAI-specific metadata fields for AI error types through module
* augmentation, enabling typed access to OpenAI error details.
*
* @since 4.0.0
*/
/**
* OpenAI-specific error metadata fields.
*
* @category models
* @since 4.0.0
*/
export type OpenAiErrorMetadata = {
/**
* The OpenAI error code returned by the API.
*/
readonly errorCode: string | null
/**
* The OpenAI error type returned by the API.
*/
readonly errorType: string | null
/**
* The unique request ID for debugging with OpenAI support.
*/
readonly requestId: string | null
}
/**
* OpenAI-specific rate limit metadata fields.
*
* **Details**
*
* Extends base error metadata with rate limit specific information from
* OpenAI's rate limit headers.
*
* @category models
* @since 4.0.0
*/
export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
/**
* The rate limit type (e.g. "requests", "tokens").
*/
readonly limit: string | null
/**
* Number of remaining requests in the current window.
*/
readonly remaining: number | null
/**
* Time until the request rate limit resets.
*/
readonly resetRequests: string | null
/**
* Time until the token rate limit resets.
*/
readonly resetTokens: string | null
}
declare module "effect/unstable/ai/AiError" {
/**
* OpenAI metadata attached to `RateLimitError` values.
*
* **Details**
*
* Captures OpenAI error details together with rate limit header information
* from responses where the provider rejected the request because a limit was
* reached.
*
* @category configuration
* @since 4.0.0
*/
export interface RateLimitErrorMetadata {
/**
* OpenAI-specific details for the rate limit response.
*/
readonly openai?: OpenAiRateLimitMetadata | null
}
/**
* OpenAI metadata attached to `QuotaExhaustedError` values.
*
* **Details**
*
* Preserves provider error details for failures caused by exhausted account,
* billing, or usage quota.
*
* @category configuration
* @since 4.0.0
*/
export interface QuotaExhaustedErrorMetadata {
/**
* OpenAI-specific details for the quota exhaustion response.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `AuthenticationError` values.
*
* **Details**
*
* Preserves provider error details for failed API key, authorization, or
* permission checks.
*
* @category configuration
* @since 4.0.0
*/
export interface AuthenticationErrorMetadata {
/**
* OpenAI-specific details for the authentication failure.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `ContentPolicyError` values.
*
* **Details**
*
* Preserves provider error details when OpenAI rejects input or output because
* it violates a content policy.
*
* @category configuration
* @since 4.0.0
*/
export interface ContentPolicyErrorMetadata {
/**
* OpenAI-specific details for the content policy response.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `InvalidRequestError` values.
*
* **Details**
*
* Preserves provider error details for malformed requests, unsupported
* parameters, or other request validation failures reported by OpenAI.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidRequestErrorMetadata {
/**
* OpenAI-specific details for the invalid request response.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `InternalProviderError` values.
*
* **Details**
*
* Preserves provider error details for OpenAI-side failures such as transient
* server errors.
*
* @category configuration
* @since 4.0.0
*/
export interface InternalProviderErrorMetadata {
/**
* OpenAI-specific details for the internal provider response.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `InvalidOutputError` values.
*
* **Details**
*
* Preserves provider error details when an OpenAI response cannot be parsed or
* validated as the expected output.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidOutputErrorMetadata {
/**
* OpenAI-specific details for the invalid output response.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `StructuredOutputError` values.
*
* **Details**
*
* Preserves provider error details when OpenAI returns content that does not
* satisfy the requested structured output schema.
*
* @category configuration
* @since 4.0.0
*/
export interface StructuredOutputErrorMetadata {
/**
* OpenAI-specific details for the structured output failure.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `UnsupportedSchemaError` values.
*
* **Details**
*
* Preserves provider error details when an unsupported schema failure is
* associated with an OpenAI response.
*
* @category configuration
* @since 4.0.0
*/
export interface UnsupportedSchemaErrorMetadata {
/**
* OpenAI-specific details for the unsupported schema failure.
*/
readonly openai?: OpenAiErrorMetadata | null
}
/**
* OpenAI metadata attached to `UnknownError` values.
*
* **Details**
*
* Preserves provider error details for OpenAI failures that do not map cleanly
* to a more specific AI error category.
*
* @category configuration
* @since 4.0.0
*/
export interface UnknownErrorMetadata {
/**
* OpenAI-specific details for the unclassified provider failure.
*/
readonly openai?: OpenAiErrorMetadata | null
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
/**
* The `OpenAiTelemetry` module defines OpenAI-specific telemetry attributes
* and a helper for adding them to a tracing span. It keeps the standard GenAI
* telemetry attributes and adds request and response metadata, such as response
* format, service tier, and system fingerprint, under the `gen_ai.openai.*`
* OpenTelemetry namespaces.
*
* @since 4.0.0
*/
import { dual } from "effect/Function"
import * as String from "effect/String"
import type { Span } from "effect/Tracer"
import type { Simplify } from "effect/Types"
import * as Telemetry from "effect/unstable/ai/Telemetry"
/**
* The attributes used to describe telemetry in the context of Generative
* Artificial Intelligence (GenAI) Models requests and responses.
*
* **Details**
*
* These attributes follow the OpenTelemetry generative AI semantic
* conventions:
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/
*
* @category models
* @since 4.0.0
*/
export type OpenAiTelemetryAttributes = Simplify<
& Telemetry.GenAITelemetryAttributes
& Telemetry.AttributesWithPrefix<RequestAttributes, "gen_ai.openai.request">
& Telemetry.AttributesWithPrefix<ResponseAttributes, "gen_ai.openai.request">
>
/**
* All telemetry attributes which are part of the GenAI specification,
* including the OpenAI-specific attributes.
*
* @category models
* @since 4.0.0
*/
export type AllAttributes = Telemetry.AllAttributes & RequestAttributes & ResponseAttributes
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.openai.request`.
*
* @category models
* @since 4.0.0
*/
export interface RequestAttributes {
/**
* The response format that is requested.
*/
readonly responseFormat?: (string & {}) | WellKnownResponseFormat | null | undefined
/**
* The service tier requested. May be a specific tier, `default`, or `auto`.
*/
readonly serviceTier?: (string & {}) | WellKnownServiceTier | null | undefined
}
/**
* Telemetry attributes which are part of the GenAI specification and are
* namespaced by `gen_ai.openai.response`.
*
* @category models
* @since 4.0.0
*/
export interface ResponseAttributes {
/**
* The service tier used for the response.
*/
readonly serviceTier?: string | null | undefined
/**
* A fingerprint to track any eventual change in the Generative AI
* environment.
*/
readonly systemFingerprint?: string | null | undefined
}
/**
* The `gen_ai.openai.request.response_format` attribute has the following
* list of well-known values.
*
* **Details**
*
* If one of them applies, then the respective value **MUST** be used;
* otherwise, a custom value **MAY** be used.
*
* @category models
* @since 4.0.0
*/
export type WellKnownResponseFormat = "json_object" | "json_schema" | "text"
/**
* The `gen_ai.openai.request.service_tier` attribute has the following
* list of well-known values.
*
* **Details**
*
* If one of them applies, then the respective value **MUST** be used;
* otherwise, a custom value **MAY** be used.
*
* @category models
* @since 4.0.0
*/
export type WellKnownServiceTier = "auto" | "default"
/**
* Options accepted by `addGenAIAnnotations`, combining standard GenAI
* telemetry attributes with optional OpenAI request and response attributes.
*
* @category options
* @since 4.0.0
*/
export type OpenAiTelemetryAttributeOptions = Telemetry.GenAITelemetryAttributeOptions & {
openai?: {
request?: RequestAttributes | undefined
response?: ResponseAttributes | undefined
} | undefined
}
const addOpenAiRequestAttributes = Telemetry.addSpanAttributes("gen_ai.openai.request", String.camelToSnake)<
RequestAttributes
>
const addOpenAiResponseAttributes = Telemetry.addSpanAttributes("gen_ai.openai.response", String.camelToSnake)<
ResponseAttributes
>
/**
* Applies the specified OpenAI GenAI telemetry attributes to the provided
* `Span`.
*
* **When to use**
*
* Use to annotate an existing OpenTelemetry span with standard GenAI attributes
* plus OpenAI-specific request and response metadata.
*
* **Gotchas**
*
* Mutates the supplied `Span` in place.
*
* @see {@link OpenAiTelemetryAttributeOptions} for the accepted telemetry attributes
* @see {@link Telemetry.addGenAIAnnotations} for the provider-neutral annotation helper
*
* @category tracing
* @since 4.0.0
*/
export const addGenAIAnnotations: {
(options: OpenAiTelemetryAttributeOptions): (span: Span) => void
(span: Span, options: OpenAiTelemetryAttributeOptions): void
} = dual(2, (span: Span, options: OpenAiTelemetryAttributeOptions) => {
Telemetry.addGenAIAnnotations(span, options)
if (options.openai != null) {
if (options.openai.request != null) {
addOpenAiRequestAttributes(span, options.openai.request)
}
if (options.openai.response != null) {
addOpenAiResponseAttributes(span, options.openai.response)
}
}
})

View File

@@ -0,0 +1,342 @@
/**
* The `OpenAiTool` module defines OpenAI provider tools for Effect AI language
* model requests. It exposes typed descriptors for tools such as Apply Patch,
* Code Interpreter, File Search, Image Generation, MCP, Web Search, and
* shell-like local tools, including their provider names, configuration
* arguments, call parameters, success schemas, and handler requirements.
*
* @since 4.0.0
*/
import * as Schema from "effect/Schema"
import * as Tool from "effect/unstable/ai/Tool"
import * as Generated from "./Generated.ts"
/**
* Union of all OpenAI provider-defined tools.
*
* @category models
* @since 4.0.0
*/
export type OpenAiTool =
| ReturnType<typeof ApplyPatch>
| ReturnType<typeof CodeInterpreter>
| ReturnType<typeof FileSearch>
| ReturnType<typeof Shell>
| ReturnType<typeof ImageGeneration>
| ReturnType<typeof LocalShell>
| ReturnType<typeof Mcp>
| ReturnType<typeof WebSearch>
| ReturnType<typeof WebSearchPreview>
/**
* Defines the OpenAI Apply Patch tool that allows the model to apply diffs by creating,
* deleting, or updating files. This local tool runs in your environment and
* requires a handler to execute file operations.
*
* **When to use**
*
* Use when you want an OpenAI model to request structured file edits as create,
* delete, or update operations that your application executes through a local
* handler.
*
* @category tools
* @since 4.0.0
*/
export const ApplyPatch = Tool.providerDefined({
id: "openai.apply_patch",
customName: "OpenAiApplyPatch",
providerName: "apply_patch",
requiresHandler: true,
parameters: Schema.Struct({
call_id: Generated.ApplyPatchToolCall.fields.call_id,
operation: Generated.ApplyPatchToolCall.fields.operation
}),
success: Schema.Struct({
status: Generated.ApplyPatchToolCallOutput.fields.status,
output: Generated.ApplyPatchToolCallOutput.fields.output
})
})
/**
* Defines the OpenAI Code Interpreter tool that allows the model to execute Python code in
* a sandboxed environment.
*
* **When to use**
*
* Use to enable OpenAI-hosted Python execution for a model response.
*
* **Details**
*
* The tool is configured with a `container` argument. Successful tool calls
* expose `outputs`, which may contain logs or generated images, or `null` when
* no outputs are available.
*
* @category tools
* @since 4.0.0
*/
export const CodeInterpreter = Tool.providerDefined({
id: "openai.code_interpreter",
customName: "OpenAiCodeInterpreter",
providerName: "code_interpreter",
args: Schema.Struct({
container: Generated.CodeInterpreterTool.fields.container
}),
parameters: Schema.Struct({
code: Generated.CodeInterpreterToolCall.fields.code,
container_id: Generated.CodeInterpreterToolCall.fields.container_id
}),
success: Schema.Struct({
outputs: Generated.CodeInterpreterToolCall.fields.outputs
})
})
/**
* Defines the OpenAI File Search tool that enables the model to search through uploaded
* files and vector stores.
*
* **When to use**
*
* Use to let an OpenAI model search uploaded files through one or more vector
* stores.
*
* **Details**
*
* The tool requires `vector_store_ids` and accepts optional `filters`,
* `max_num_results`, and `ranking_options`. Successful tool calls expose the
* search `status`, generated `queries`, and optional `results`.
*
* @category tools
* @since 4.0.0
*/
export const FileSearch = Tool.providerDefined({
id: "openai.file_search",
customName: "OpenAiFileSearch",
providerName: "file_search",
args: Schema.Struct({
filters: Generated.FileSearchTool.fields.filters,
max_num_results: Generated.FileSearchTool.fields.max_num_results,
ranking_options: Generated.FileSearchTool.fields.ranking_options,
vector_store_ids: Generated.FileSearchTool.fields.vector_store_ids
}),
success: Schema.Struct({
status: Generated.FileSearchToolCall.fields.status,
queries: Generated.FileSearchToolCall.fields.queries,
results: Generated.FileSearchToolCall.fields.results
})
})
/**
* Defines the OpenAI Image Generation tool that enables the model to generate images using
* the GPT image models.
*
* **When to use**
*
* Use to enable OpenAI provider-defined image generation through a language
* model response.
*
* **Details**
*
* The tool configures the `image_generation` provider tool, including model,
* size, quality, output format, moderation, background, input-image options,
* and partial image settings. Successful tool calls expose `result` as base64
* image data or `null`.
*
* @category tools
* @since 4.0.0
*/
export const ImageGeneration = Tool.providerDefined({
id: "openai.image_generation",
customName: "OpenAiImageGeneration",
providerName: "image_generation",
args: Schema.Struct({
background: Generated.ImageGenTool.fields.background,
input_fidelity: Generated.ImageGenTool.fields.input_fidelity,
input_image_mask: Generated.ImageGenTool.fields.input_image_mask,
model: Generated.ImageGenTool.fields.model,
moderation: Generated.ImageGenTool.fields.moderation,
output_compression: Generated.ImageGenTool.fields.output_compression,
output_format: Generated.ImageGenTool.fields.output_format,
partial_images: Generated.ImageGenTool.fields.partial_images,
quality: Generated.ImageGenTool.fields.quality,
size: Generated.ImageGenTool.fields.size
}),
success: Schema.Struct({
result: Generated.ImageGenToolCall.fields.result
})
})
/**
* Defines the OpenAI Local Shell tool that enables the model to run a command with a local
* shell. This local tool runs in your environment and requires a handler to
* execute commands.
*
* **When to use**
*
* Use to let an OpenAI model request local shell commands that your application
* executes through a handler.
*
* **Details**
*
* The tool exposes a provider-defined `local_shell` call. It is marked as
* handler-required, so applications must provide the command execution policy
* and implementation.
*
* @category tools
* @since 4.0.0
*/
export const LocalShell = Tool.providerDefined({
id: "openai.local_shell",
customName: "OpenAiLocalShell",
providerName: "local_shell",
requiresHandler: true,
parameters: Schema.Struct({
action: Generated.LocalShellToolCall.fields.action
}),
success: Schema.Struct({
output: Generated.LocalShellToolCallOutput.fields.output
})
})
/**
* Defines the OpenAI MCP tool that gives the model access to additional tools via remote
* Model Context Protocol (MCP) servers.
*
* **When to use**
*
* Use to let an OpenAI model call tools exposed by a remote MCP server.
*
* **Details**
*
* The tool accepts MCP server configuration such as allowed tools,
* authorization, connector id, approval requirements, server metadata, and
* server URL. Tool call results include the called tool name, arguments, output,
* error, and server label.
*
* **Gotchas**
*
* This schema leaves both `server_url` and `connector_id` optional, but OpenAI
* may require a server URL or connector id for a usable MCP tool configuration.
*
* @category tools
* @since 4.0.0
*/
export const Mcp = Tool.providerDefined({
id: "openai.mcp",
customName: "OpenAiMcp",
providerName: "mcp",
args: Schema.Struct({
allowed_tools: Generated.MCPTool.fields.allowed_tools,
authorization: Generated.MCPTool.fields.authorization,
connector_id: Generated.MCPTool.fields.connector_id,
require_approval: Generated.MCPTool.fields.require_approval,
server_description: Generated.MCPTool.fields.server_description,
server_label: Generated.MCPTool.fields.server_label,
server_url: Generated.MCPTool.fields.server_url
}),
parameters: Schema.Unknown,
success: Schema.Struct({
type: Generated.MCPToolCall.fields.type,
name: Generated.MCPToolCall.fields.name,
arguments: Generated.MCPToolCall.fields.arguments,
output: Generated.MCPToolCall.fields.output,
error: Generated.MCPToolCall.fields.error,
server_label: Generated.MCPToolCall.fields.server_label
})
})
/**
* Defines the OpenAI shell tool for model-requested command execution.
*
* **When to use**
*
* Use to let an OpenAI model request shell commands that your application
* executes through a handler.
*
* **Details**
*
* The tool exposes a provider-defined `shell` call. It is marked as
* handler-required, so applications must provide the command execution policy
* and implementation.
*
* @category tools
* @since 4.0.0
*/
export const Shell = Tool.providerDefined({
id: "openai.shell",
customName: "OpenAiShell",
providerName: "shell",
requiresHandler: true,
parameters: Schema.Struct({
action: Generated.FunctionShellCall.fields.action
}),
success: Schema.Struct({
output: Generated.FunctionShellCallOutput.fields.output
})
})
/**
* Defines the OpenAI Web Search tool that enables the model to search the web for
* information.
*
* **When to use**
*
* Use to enable OpenAI provider-defined web search for a model response.
*
* **Details**
*
* The tool accepts optional filters, user location, and search context size.
* Successful calls expose the performed search action and status.
*
* @see {@link WebSearchPreview} for the preview web search provider tool
*
* @category tools
* @since 4.0.0
*/
export const WebSearch = Tool.providerDefined({
id: "openai.web_search",
customName: "OpenAiWebSearch",
providerName: "web_search",
args: Schema.Struct({
filters: Generated.WebSearchTool.fields.filters,
user_location: Generated.WebSearchTool.fields.user_location,
search_context_size: Generated.WebSearchTool.fields.search_context_size
}),
parameters: Schema.Struct({
action: Generated.WebSearchToolCall.fields.action
}),
success: Schema.Struct({
action: Generated.WebSearchToolCall.fields.action,
status: Generated.WebSearchToolCall.fields.status
})
})
/**
* Defines the OpenAI preview Web Search tool for model responses.
*
* **When to use**
*
* Use to enable the preview OpenAI web search provider tool.
*
* **Details**
*
* The preview tool accepts optional user location and search context size, then
* exposes the performed search action and status in successful calls.
*
* @see {@link WebSearch} for the stable web search provider tool
*
* @category tools
* @since 4.0.0
*/
export const WebSearchPreview = Tool.providerDefined({
id: "openai.web_search_preview",
customName: "OpenAiWebSearchPreview",
providerName: "web_search_preview",
args: Schema.Struct({
user_location: Generated.WebSearchPreviewTool.fields.user_location,
search_context_size: Generated.WebSearchPreviewTool.fields.search_context_size
}),
success: Schema.Struct({
action: Generated.WebSearchToolCall.fields.action,
status: Generated.WebSearchToolCall.fields.status
})
})

View File

@@ -0,0 +1,55 @@
/**
* @since 4.0.0
*/
// @barrel: Auto-generated exports. Do not edit manually.
/**
* @since 1.0.0
*/
export * as Generated from "./Generated.ts"
/**
* @since 4.0.0
*/
export * as OpenAiClient from "./OpenAiClient.ts"
/**
* @since 4.0.0
*/
export * as OpenAiClientGenerated from "./OpenAiClientGenerated.ts"
/**
* @since 4.0.0
*/
export * as OpenAiConfig from "./OpenAiConfig.ts"
/**
* @since 4.0.0
*/
export * as OpenAiEmbeddingModel from "./OpenAiEmbeddingModel.ts"
/**
* @since 4.0.0
*/
export * as OpenAiError from "./OpenAiError.ts"
/**
* @since 4.0.0
*/
export * as OpenAiLanguageModel from "./OpenAiLanguageModel.ts"
/**
* @since 4.0.0
*/
export * as OpenAiSchema from "./OpenAiSchema.ts"
/**
* @since 4.0.0
*/
export * as OpenAiTelemetry from "./OpenAiTelemetry.ts"
/**
* @since 4.0.0
*/
export * as OpenAiTool from "./OpenAiTool.ts"

View File

@@ -0,0 +1,349 @@
import * as Duration from "effect/Duration"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Number from "effect/Number"
import * as Option from "effect/Option"
import * as Predicate from "effect/Predicate"
import * as Redactable from "effect/Redactable"
import * as Schema from "effect/Schema"
import * as AiError from "effect/unstable/ai/AiError"
import type * as Response from "effect/unstable/ai/Response"
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import type { OpenAiErrorMetadata } from "../OpenAiError.ts"
// =============================================================================
// OpenAI Error Body Schema
// =============================================================================
/** @internal */
export const OpenAiErrorBody = Schema.Struct({
error: Schema.Struct({
message: Schema.String,
type: Schema.optional(Schema.NullOr(Schema.String)),
param: Schema.optional(Schema.NullOr(Schema.String)),
code: Schema.optional(Schema.NullOr(Schema.String))
})
})
// =============================================================================
// Error Mappers
// =============================================================================
/** @internal */
export const mapSchemaError = dual<
(method: string) => (error: Schema.SchemaError) => AiError.AiError,
(error: Schema.SchemaError, method: string) => AiError.AiError
>(2, (error, method) =>
AiError.make({
module: "OpenAiClient",
method,
reason: AiError.InvalidOutputError.fromSchemaError(error)
}))
/** @internal */
export const mapHttpClientError = dual<
(method: string) => (error: HttpClientError.HttpClientError) => Effect.Effect<never, AiError.AiError>,
(error: HttpClientError.HttpClientError, method: string) => Effect.Effect<never, AiError.AiError>
>(2, (error, method) => {
const reason = error.reason
switch (reason._tag) {
case "TransportError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "TransportError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "EncodeError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "EncodeError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "InvalidUrlError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.NetworkError({
reason: "InvalidUrlError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "StatusCodeError": {
return mapStatusCodeError(reason, method)
}
case "DecodeError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Failed to decode response"
})
}))
}
case "EmptyBodyError": {
return Effect.fail(AiError.make({
module: "OpenAiClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Response body was empty"
})
}))
}
}
})
/** @internal */
const mapStatusCodeError = Effect.fnUntraced(function*(
error: HttpClientError.StatusCodeError,
method: string
) {
const { request, response, description } = error
const status = response.status
const headers = response.headers as Record<string, string>
const requestId = headers["x-request-id"]
// Try to get the actual response body. The description from filterStatusOk
// is often just "non 2xx status code", so try reading from response.text
let body: string | undefined = description
if (!description || !description.startsWith("{")) {
const responseBody = yield* Effect.option(response.text)
if (Option.isSome(responseBody) && responseBody.value) {
body = responseBody.value
}
}
// Try to parse the body as JSON to extract error details
let json: unknown = undefined
// @effect-diagnostics effect/tryCatchInEffectGen:off
try {
json = Predicate.isNotUndefined(body) ? JSON.parse(body) : undefined
} catch {
json = undefined
}
const decoded = Schema.decodeUnknownOption(OpenAiErrorBody)(json)
const reason = mapStatusCodeToReason({
status,
headers,
message: Option.isSome(decoded) ? decoded.value.error.message : undefined,
http: buildHttpContext({ request, response, body }),
metadata: {
errorCode: Option.isSome(decoded) ? decoded.value.error.code ?? null : null,
errorType: Option.isSome(decoded) ? decoded.value.error.type ?? null : null,
requestId: requestId ?? null
}
})
return yield* AiError.make({ module: "OpenAiClient", method, reason })
})
// =============================================================================
// Rate Limits
// =============================================================================
/** @internal */
export const parseRateLimitHeaders = (headers: Record<string, string>) => {
const retryAfterRaw = headers["retry-after"]
let retryAfter: Duration.Duration | undefined
if (Predicate.isNotUndefined(retryAfterRaw)) {
const parsed = Number.parse(retryAfterRaw)
if (Option.isSome(parsed)) {
retryAfter = Duration.seconds(parsed.value)
}
}
const remainingRaw = headers["x-ratelimit-remaining-requests"]
const remaining = Predicate.isNotUndefined(remainingRaw)
? Option.getOrNull(Number.parse(remainingRaw))
: null
return {
retryAfter,
limit: headers["x-ratelimit-limit-requests"] ?? null,
remaining,
resetRequests: headers["x-ratelimit-reset-requests"] ?? null,
resetTokens: headers["x-ratelimit-reset-tokens"] ?? null
}
}
// =============================================================================
// HTTP Context
// =============================================================================
/** @internal */
export const buildHttpRequestDetails = (
request: HttpClientRequest.HttpClientRequest
): typeof Response.HttpRequestDetails.Type => ({
method: request.method,
url: request.url,
urlParams: Array.from(request.urlParams),
hash: Option.getOrUndefined(request.hash),
headers: Redactable.redact(request.headers) as Record<string, string>
})
/** @internal */
export const buildHttpContext = (params: {
readonly request: HttpClientRequest.HttpClientRequest
readonly response?: HttpClientResponse.HttpClientResponse
readonly body?: string | undefined
}): typeof AiError.HttpContext.Type => ({
request: buildHttpRequestDetails(params.request),
response: Predicate.isNotUndefined(params.response)
? {
status: params.response.status,
headers: Redactable.redact(params.response.headers) as Record<string, string>
}
: undefined,
body: params.body
})
// =============================================================================
// HTTP Status Code
// =============================================================================
const buildInvalidRequestDescription = (params: {
readonly status: number
readonly message: string | undefined
readonly method: string
readonly url: string
readonly errorCode: string | null
readonly errorType: string | null
readonly requestId: string | null
readonly body: string | undefined
}): string => {
const parts: Array<string> = []
// Primary message or status description
if (params.message) {
parts.push(params.message)
} else {
parts.push(`HTTP ${params.status}`)
}
// Request context
parts.push(`(${params.method} ${params.url})`)
// Error code/type if available
if (params.errorCode) {
parts.push(`[code: ${params.errorCode}]`)
} else if (params.errorType) {
parts.push(`[type: ${params.errorType}]`)
}
// Request ID for debugging
if (params.requestId) {
parts.push(`[requestId: ${params.requestId}]`)
}
// If no message and we have body, show truncated body
if (!params.message && params.body) {
const truncated = params.body.length > 200
? params.body.slice(0, 200) + "..."
: params.body
parts.push(`Response: ${truncated}`)
}
return parts.join(" ")
}
/** @internal */
export const mapStatusCodeToReason = ({ status, headers, message, metadata, http }: {
readonly status: number
readonly headers: Record<string, string>
readonly message: string | undefined
readonly metadata: OpenAiErrorMetadata
readonly http: typeof AiError.HttpContext.Type
}): AiError.AiErrorReason => {
const invalidRequestDescription = buildInvalidRequestDescription({
status,
message,
method: http.request.method,
url: http.request.url,
errorCode: metadata.errorCode,
errorType: metadata.errorType,
requestId: metadata.requestId,
body: http.body
})
switch (status) {
case 400:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 401:
return new AiError.AuthenticationError({
kind: "InvalidKey",
metadata,
http
})
case 403:
return new AiError.AuthenticationError({
kind: "InsufficientPermissions",
metadata,
http
})
case 404:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 409:
case 422:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openai: metadata },
http
})
case 429: {
// Best-effort detection: OpenAI returns insufficient_quota for billing/quota issues
if (
metadata.errorCode === "insufficient_quota" ||
metadata.errorType === "insufficient_quota"
) {
return new AiError.QuotaExhaustedError({
metadata: { openai: metadata },
http
})
}
const { retryAfter, ...rateLimitMetadata } = parseRateLimitHeaders(headers)
return new AiError.RateLimitError({
retryAfter,
metadata: {
openai: {
...metadata,
...rateLimitMetadata
}
},
http
})
}
default:
if (status >= 500) {
return new AiError.InternalProviderError({
description: message ?? "Server error",
metadata,
http
})
}
return new AiError.UnknownError({
description: message,
metadata,
http
})
}
}

View File

@@ -0,0 +1,27 @@
import type * as Response from "effect/unstable/ai/Response"
const finishReasonMap: Record<string, Response.FinishReason> = {
content_filter: "content-filter",
function_call: "tool-calls",
length: "length",
stop: "stop",
tool_calls: "tool-calls"
}
/** @internal */
export const escapeJSONDelta = (delta: string): string => JSON.stringify(delta).slice(1, -1)
/** @internal */
export const resolveFinishReason = (
finishReason: string | null | undefined,
hasToolCalls: boolean
): Response.FinishReason => {
if (finishReason == null) {
return hasToolCalls ? "tool-calls" : "stop"
}
const reason = finishReasonMap[finishReason]
if (reason == null) {
return hasToolCalls ? "tool-calls" : "unknown"
}
return reason
}

View File

@@ -0,0 +1,798 @@
import type * as Generated from "@effect/ai-openai/Generated"
import * as Errors from "@effect/ai-openai/internal/errors"
import * as OpenAiClient from "@effect/ai-openai/OpenAiClient"
import * as OpenAiClientGenerated from "@effect/ai-openai/OpenAiClientGenerated"
import * as OpenAiConfig from "@effect/ai-openai/OpenAiConfig"
import { assert, describe, it } from "@effect/vitest"
import { Config, ConfigProvider, Effect, Layer, Redacted, Schema, Stream } from "effect"
import type * as AiError from "effect/unstable/ai/AiError"
import * as HttpClient from "effect/unstable/http/HttpClient"
import * as HttpClientError from "effect/unstable/http/HttpClientError"
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
// =============================================================================
// Mock Helpers
// =============================================================================
const makeMockResponse = (options: {
readonly status: number
readonly body: unknown
readonly request?: HttpClientRequest.HttpClientRequest
}): HttpClientResponse.HttpClientResponse => {
// Always use a plain request for the response to avoid Redacted headers in error contexts
const request = HttpClientRequest.get(options.request?.url ?? "/")
const json = JSON.stringify(options.body)
return HttpClientResponse.fromWeb(
request,
new Response(json, {
status: options.status,
headers: { "content-type": "application/json" }
})
)
}
const makeMockStreamResponse = (options: {
readonly events: ReadonlyArray<unknown>
readonly request?: HttpClientRequest.HttpClientRequest
}): HttpClientResponse.HttpClientResponse => {
const request = HttpClientRequest.get(options.request?.url ?? "/")
const body = options.events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("")
return HttpClientResponse.fromWeb(
request,
new Response(body, {
status: 200,
headers: { "content-type": "text/event-stream" }
})
)
}
const makeMockHttpClient = (
handler: (
request: HttpClientRequest.HttpClientRequest
) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError>
): HttpClient.HttpClient =>
HttpClient.makeWith<HttpClientError.HttpClientError, never, HttpClientError.HttpClientError, never>(
(effect) =>
Effect.flatMap(effect, handler) as Effect.Effect<
HttpClientResponse.HttpClientResponse,
HttpClientError.HttpClientError,
never
>,
Effect.succeed
)
const makeResponseBody = (
overrides: Partial<typeof Generated.Response.Encoded> = {}
): typeof Generated.Response.Encoded => ({
id: "resp_test123",
object: "response",
created_at: 1,
model: "gpt-4o-mini",
status: "completed",
output: [],
metadata: null,
temperature: null,
top_p: null,
tools: [],
tool_choice: "auto",
error: null,
incomplete_details: null,
instructions: null,
parallel_tool_calls: false,
...overrides
})
// =============================================================================
// Tests
// =============================================================================
describe("OpenAiClient", () => {
describe("make", () => {
it.effect("sets Bearer token from apiKey", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("sk-test-12345")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
// Call method and ignore response parsing errors - we only care about the request
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
const authHeader = capturedRequest!.headers["authorization"]
assert.strictEqual(authHeader, "Bearer sk-test-12345")
}))
it.effect("prepends default URL", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.isTrue(capturedRequest!.url.startsWith("https://api.openai.com/v1"))
}))
it.effect("uses custom apiUrl when provided", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
apiUrl: "https://custom.api.com/v2"
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.isTrue(capturedRequest!.url.startsWith("https://custom.api.com/v2"))
}))
it.effect("sets OpenAI-Organization header when organizationId provided", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
organizationId: Redacted.make("org-12345")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["openai-organization"], "org-12345")
}))
it.effect("sets OpenAI-Project header when projectId provided", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
projectId: Redacted.make("proj-67890")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["openai-project"], "proj-67890")
}))
it.effect("applies transformClient option", () =>
Effect.gen(function*() {
let transformApplied = false
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
transformClient: (client) => {
transformApplied = true
return client
}
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isTrue(transformApplied)
}))
it.effect("exposes transformed HttpClient via client field", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
transformClient: (client) =>
client.pipe(HttpClient.mapRequest(HttpClientRequest.setHeader("x-client-field", "enabled")))
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.client.execute(HttpClientRequest.get("/responses")).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.isTrue(capturedRequest!.url.startsWith("https://api.openai.com/v1"))
assert.strictEqual(capturedRequest!.headers["authorization"], "Bearer test-key")
assert.strictEqual(capturedRequest!.headers["x-client-field"], "enabled")
}))
it.effect("applies OpenAiConfig transformClient after options transformClient", () =>
Effect.gen(function*() {
let optionsTransformApplied = false
let configTransformApplied = false
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: makeResponseBody(), request }))
})
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key"),
transformClient: (client) => {
optionsTransformApplied = true
return client.pipe(
HttpClient.mapRequest(HttpClientRequest.setHeader("x-openai-transform", "options"))
)
}
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({
model: "gpt-4o",
input: "test"
}).pipe(
OpenAiConfig.withClientTransform((client) => {
configTransformApplied = true
return client.pipe(
HttpClient.mapRequest(HttpClientRequest.setHeader("x-openai-transform", "config"))
)
})
)
assert.isTrue(optionsTransformApplied)
assert.isTrue(configTransformApplied)
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["x-openai-transform"], "config")
}))
})
describe("OpenAiClientGenerated", () => {
it.effect("sets Bearer token from apiKey", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: makeResponseBody(), request }))
})
const client = yield* OpenAiClientGenerated.make({
apiKey: Redacted.make("sk-generated-test")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({
payload: {
model: "gpt-4o",
input: "test"
}
})
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["authorization"], "Bearer sk-generated-test")
}))
it.effect("prepends custom apiUrl", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: makeResponseBody(), request }))
})
const client = yield* OpenAiClientGenerated.make({
apiKey: Redacted.make("test-key"),
apiUrl: "https://generated.example.test/v2"
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({
payload: {
model: "gpt-4o",
input: "test"
}
})
assert.isDefined(capturedRequest)
assert.isTrue(capturedRequest!.url.startsWith("https://generated.example.test/v2"))
}))
it.effect("applies OpenAiConfig transformClient after options transformClient", () =>
Effect.gen(function*() {
let optionsTransformApplied = false
let configTransformApplied = false
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const mockClient = makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: makeResponseBody(), request }))
})
const client = yield* OpenAiClientGenerated.make({
apiKey: Redacted.make("test-key"),
transformClient: (client) => {
optionsTransformApplied = true
return client.pipe(
HttpClient.mapRequest(HttpClientRequest.setHeader("x-openai-transform", "options"))
)
}
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
yield* client.createResponse({
payload: {
model: "gpt-4o",
input: "test"
}
}).pipe(
OpenAiConfig.withClientTransform((client) => {
configTransformApplied = true
return client.pipe(
HttpClient.mapRequest(HttpClientRequest.setHeader("x-openai-transform", "config"))
)
})
)
assert.isTrue(optionsTransformApplied)
assert.isTrue(configTransformApplied)
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["x-openai-transform"], "config")
}))
})
describe("layer", () => {
it.effect("creates working service", () => {
const HttpClientLayer = Layer.succeed(
HttpClient.HttpClient,
makeMockHttpClient(() => Effect.succeed(makeMockResponse({ status: 200, body: {} })))
)
const MainLayer = OpenAiClient.layer({
apiKey: Redacted.make("test-key")
}).pipe(Layer.provide(HttpClientLayer))
return Effect.gen(function*() {
const client = yield* OpenAiClient.OpenAiClient
assert.isNotNull(client.client)
}).pipe(Effect.provide(MainLayer))
})
it.effect("layerConfig loads from Config", () => {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const HttpClientLayer = Layer.succeed(
HttpClient.HttpClient,
makeMockHttpClient((request) => {
capturedRequest = request
return Effect.succeed(makeMockResponse({ status: 200, body: {}, request }))
})
)
const configProvider = ConfigProvider.fromEnv({
env: {
MY_API_KEY: "sk-config-key",
MY_API_URL: "https://config.api.com/v1"
}
})
// Use explicit config values to test the layerConfig mechanism
// Provide explicit configs that won't fail for optional fields
const MainLayer = OpenAiClient.layerConfig({
apiKey: Config.redacted("MY_API_KEY"),
apiUrl: Config.string("MY_API_URL")
}).pipe(
Layer.provide(HttpClientLayer),
Layer.provide(ConfigProvider.layer(configProvider))
)
return Effect.gen(function*() {
const client = yield* OpenAiClient.OpenAiClient
yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(Effect.ignore)
assert.isDefined(capturedRequest)
assert.strictEqual(capturedRequest!.headers["authorization"], "Bearer sk-config-key")
assert.isTrue(capturedRequest!.url.startsWith("https://config.api.com/v1"))
}).pipe(Effect.provide(MainLayer))
})
})
describe("error mapping", () => {
it.effect("maps TransportError to NetworkError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient(() =>
Effect.fail(
new HttpClientError.HttpClientError({
reason: new HttpClientError.TransportError({
request: HttpClientRequest.get("/"),
cause: new Error("Connection refused")
})
})
)
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.module, "OpenAiClient")
assert.strictEqual(result.method, "createResponse")
assert.strictEqual(result.reason._tag, "NetworkError")
}))
it.effect("maps 400 status to InvalidRequestError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 400,
body: { error: { message: "Bad request" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.module, "OpenAiClient")
assert.strictEqual(result.method, "createResponse")
assert.strictEqual(result.reason._tag, "InvalidRequestError")
}))
it.effect("maps 401 status to AuthenticationError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 401,
body: { error: { message: "Invalid API key" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "AuthenticationError")
assert.strictEqual((result.reason as AiError.AuthenticationError).kind, "InvalidKey")
}))
it.effect("maps 403 status to AuthenticationError with InsufficientPermissions", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 403,
body: { error: { message: "Access denied" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "AuthenticationError")
assert.strictEqual((result.reason as AiError.AuthenticationError).kind, "InsufficientPermissions")
}))
it.effect("maps 429 status to RateLimitError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 429,
body: { error: { message: "Rate limit exceeded", type: "rate_limit_error", code: null } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "RateLimitError")
assert.isTrue(result.isRetryable)
}))
it.effect("maps 429 with insufficient_quota code to QuotaExhaustedError", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 429,
body: {
error: {
message: "You exceeded your current quota",
type: "insufficient_quota",
code: "insufficient_quota"
}
},
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "QuotaExhaustedError")
assert.isFalse(result.isRetryable)
}))
it("mapStatusCodeToReason detects insufficient_quota as QuotaExhaustedError", () => {
const http = {
request: {
method: "POST" as const,
url: "https://api.openai.com",
urlParams: [],
hash: undefined,
headers: {}
}
}
const reason = Errors.mapStatusCodeToReason({
status: 429,
headers: {},
message: "You exceeded your current quota",
metadata: {
errorCode: "insufficient_quota",
errorType: "insufficient_quota",
requestId: null
},
http
})
assert.strictEqual(reason._tag, "QuotaExhaustedError")
})
it("OpenAiErrorBody decodes error with type and code", () => {
const json = {
error: {
message: "You exceeded your current quota",
type: "insufficient_quota",
code: "insufficient_quota"
}
}
const decoded = Schema.decodeUnknownSync(Errors.OpenAiErrorBody)(json)
assert.strictEqual(decoded.error.message, "You exceeded your current quota")
assert.strictEqual(decoded.error.type, "insufficient_quota")
assert.strictEqual(decoded.error.code, "insufficient_quota")
})
it.effect("maps 5xx status to InternalProviderError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 500,
body: { error: { message: "Internal server error" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "InternalProviderError")
assert.isTrue(result.isRetryable)
}))
it.effect("maps schema error to InvalidOutputError reason", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 200,
body: { invalid: "response" },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createResponse({ model: "gpt-4o", input: "test" }).pipe(
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.method, "createResponse")
assert.strictEqual(result.reason._tag, "InvalidOutputError")
}))
})
describe("createEmbedding", () => {
it.effect("maps 400 error to AiError", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 400,
body: { error: { message: "Invalid model" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createEmbedding({
model: "invalid-model",
input: "test"
}).pipe(Effect.flip)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.method, "createEmbedding")
assert.strictEqual(result.reason._tag, "InvalidRequestError")
}))
it.effect("maps 429 error to RateLimitError", () =>
Effect.gen(function*() {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 429,
body: { error: { message: "Rate limit exceeded" } },
request
}))
)
const client = yield* OpenAiClient.make({
apiKey: Redacted.make("test-key")
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, mockClient)))
const result = yield* client.createEmbedding({
model: "text-embedding-ada-002",
input: "test"
}).pipe(Effect.flip)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "RateLimitError")
}))
})
describe("createResponseStream", () => {
it.effect("accepts keepalive stream events", () => {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockStreamResponse({
request,
events: [
{
type: "response.created",
sequence_number: 1,
response: makeResponseBody({
id: "resp_stream",
status: "in_progress"
})
},
{
type: "keepalive",
sequence_number: 2
},
{
type: "response.completed",
sequence_number: 3,
response: makeResponseBody({
id: "resp_stream"
})
}
]
}))
)
const HttpClientLayer = Layer.succeed(HttpClient.HttpClient, mockClient)
const MainLayer = OpenAiClient.layer({
apiKey: Redacted.make("test-key")
}).pipe(Layer.provide(HttpClientLayer))
return Effect.gen(function*() {
const client = yield* OpenAiClient.OpenAiClient
const [_, stream] = yield* client.createResponseStream({
model: "gpt-4o",
input: "test"
})
const events = yield* Stream.runCollect(stream)
const parts = globalThis.Array.from(events)
assert.strictEqual(parts.length, 3)
const keepAlive = parts[1]
assert.strictEqual(keepAlive.type, "keepalive")
if (keepAlive.type === "keepalive" && "sequence_number" in keepAlive) {
assert.strictEqual(keepAlive.sequence_number, 2)
}
const completed = parts[2]
assert.isTrue(typeof completed === "object" && completed !== null && "type" in completed)
if (typeof completed === "object" && completed !== null && "type" in completed) {
assert.strictEqual(completed.type, "response.completed")
if (
completed.type === "response.completed" &&
"response" in completed &&
typeof completed.response === "object" &&
completed.response !== null &&
"id" in completed.response
) {
assert.strictEqual(completed.response.id, "resp_stream")
}
}
}).pipe(Effect.provide(MainLayer))
})
it.effect("maps HTTP error before stream starts", () => {
const mockClient = makeMockHttpClient((request) =>
Effect.succeed(makeMockResponse({
status: 500,
body: { error: { message: "Server error" } },
request
}))
)
const HttpClientLayer = Layer.succeed(HttpClient.HttpClient, mockClient)
const MainLayer = OpenAiClient.layer({
apiKey: Redacted.make("test-key")
}).pipe(Layer.provide(HttpClientLayer))
return Effect.gen(function*() {
const client = yield* OpenAiClient.OpenAiClient
const result = yield* client.createResponseStream({
model: "gpt-4o",
input: "test"
}).pipe(
Effect.andThen(([_, stream]) => Stream.runDrain(stream)),
Effect.flip
)
assert.strictEqual(result._tag, "AiError")
assert.strictEqual(result.reason._tag, "InternalProviderError")
}).pipe(Effect.provide(MainLayer))
})
})
})

View File

@@ -0,0 +1,289 @@
import { OpenAiClient, OpenAiEmbeddingModel } from "@effect/ai-openai"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Layer, Redacted } from "effect"
import { EmbeddingModel } from "effect/unstable/ai"
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
describe("OpenAiEmbeddingModel", () => {
it.effect("model provides dimensions service", () =>
Effect.gen(function*() {
const dimensions = yield* EmbeddingModel.Dimensions
assert.strictEqual(dimensions, 1536)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.model("text-embedding-3-small", { dimensions: 1536 })),
Effect.provideService(OpenAiClient.OpenAiClient, noopOpenAiClient)
))
it.effect("reorders embeddings by provider index", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
data: [
{ index: 1, embedding: [20, 21], object: "embedding" },
{ index: 0, embedding: [10, 11], object: "embedding" }
],
model: "text-embedding-3-small",
object: "list",
usage: {
prompt_tokens: 7,
total_tokens: 7
}
}))
})
))
)
const response = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["first", "second"])
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.deepStrictEqual(response.embeddings.map((embedding) => embedding.vector), [[10, 11], [20, 21]])
assert.strictEqual(response.usage.inputTokens, 7)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const requestBody = yield* getRequestBody(capturedRequest)
assert.strictEqual(requestBody.model, "text-embedding-3-small")
assert.deepStrictEqual(requestBody.input, ["first", "second"])
}))
it.effect("merges config and applies withConfigOverride precedence", () =>
Effect.gen(function*() {
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) => {
capturedRequest = request
return Effect.succeed(jsonResponse(request, {
data: [{ index: 0, embedding: [1, 2, 3], object: "embedding" }],
model: "override-model",
object: "list",
usage: {
prompt_tokens: 3,
total_tokens: 3
}
}))
})
))
)
yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
yield* model.embed("hello")
}).pipe(
OpenAiEmbeddingModel.withConfigOverride({
model: "override-model",
dimensions: 1024,
user: "request-user"
}),
Effect.provide(OpenAiEmbeddingModel.layer({
model: "base-model",
config: {
dimensions: 256,
user: "provider-user"
}
})),
Effect.provide(clientLayer)
)
assert.isDefined(capturedRequest)
if (capturedRequest === undefined) {
return
}
const requestBody = yield* getRequestBody(capturedRequest)
assert.strictEqual(requestBody.model, "override-model")
assert.strictEqual(requestBody.dimensions, 1024)
assert.strictEqual(requestBody.user, "request-user")
assert.deepStrictEqual(requestBody.input, ["hello"])
}))
it.effect("fails with InvalidOutputError when provider returns duplicate indices", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [
{ index: 0, embedding: [1], object: "embedding" },
{ index: 0, embedding: [2], object: "embedding" }
],
model: "text-embedding-3-small",
object: "list",
usage: {
prompt_tokens: 2,
total_tokens: 2
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["a", "b"]).pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
it.effect("fails with InvalidOutputError when provider returns out-of-range indices", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [
{ index: 0, embedding: [1], object: "embedding" },
{ index: 2, embedding: [2], object: "embedding" }
],
model: "text-embedding-3-small",
object: "list",
usage: {
prompt_tokens: 2,
total_tokens: 2
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["a", "b"]).pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
it.effect("fails with InvalidOutputError when provider returns wrong embedding count", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [{ index: 0, embedding: [1], object: "embedding" }],
model: "text-embedding-3-small",
object: "list",
usage: {
prompt_tokens: 1,
total_tokens: 1
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embedMany(["a", "b"]).pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
it.effect("fails with InvalidOutputError when provider returns base64 embeddings", () =>
Effect.gen(function*() {
const clientLayer = OpenAiClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provide(Layer.succeed(
HttpClient.HttpClient,
makeHttpClient((request) =>
Effect.succeed(jsonResponse(request, {
data: [{ index: 0, embedding: "AQID", object: "embedding" }],
model: "text-embedding-3-small",
object: "list",
usage: {
prompt_tokens: 1,
total_tokens: 1
}
}))
)
))
)
const error = yield* Effect.gen(function*() {
const model = yield* EmbeddingModel.EmbeddingModel
return yield* model.embed("a").pipe(Effect.flip)
}).pipe(
Effect.provide(OpenAiEmbeddingModel.layer({ model: "text-embedding-3-small" })),
Effect.provide(clientLayer)
)
assert.strictEqual(error._tag, "AiError")
assert.strictEqual(error.reason._tag, "InvalidOutputError")
}))
})
const makeHttpClient = (
handler: (
request: HttpClientRequest.HttpClientRequest
) => Effect.Effect<HttpClientResponse.HttpClientResponse, HttpClientError.HttpClientError>
) =>
HttpClient.makeWith(
Effect.fnUntraced(function*(requestEffect) {
const request = yield* requestEffect
return yield* handler(request)
}),
Effect.succeed as HttpClient.HttpClient.Preprocess<HttpClientError.HttpClientError, never>
)
const jsonResponse = (
request: HttpClientRequest.HttpClientRequest,
body: unknown
): HttpClientResponse.HttpClientResponse =>
HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(body), {
status: 200,
headers: {
"content-type": "application/json"
}
})
)
const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
Effect.gen(function*() {
const body = request.body
if (body._tag === "Uint8Array") {
const text = new TextDecoder().decode(body.body)
return JSON.parse(text)
}
return yield* Effect.die(new Error("Expected Uint8Array body"))
})
const noopOpenAiClient: OpenAiClient.Service = {
client: undefined as unknown as OpenAiClient.Service["client"],
createResponse: () => Effect.die(new Error("noop")),
createResponseStream: () => Effect.die(new Error("noop")),
createEmbedding: () => Effect.die(new Error("noop"))
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,273 @@
import * as OpenAiSchema from "@effect/ai-openai/OpenAiSchema"
import { assert, describe, it } from "@effect/vitest"
import { Effect, Schema, Stream } from "effect"
import * as Sse from "effect/unstable/encoding/Sse"
const makeResponse = (overrides: Record<string, unknown> = {}) => ({
id: "resp_123",
object: "response",
model: "gpt-4o-mini",
status: "completed",
created_at: 1,
output: [],
...overrides
})
describe("OpenAiSchema", () => {
it("decodes a representative response payload", () => {
const decoded = Schema.decodeUnknownSync(OpenAiSchema.Response)({
...makeResponse(),
output: [
{
id: "msg_1",
type: "message",
role: "assistant",
status: "completed",
content: [
{
type: "output_text",
text: "hello",
annotations: [
{
type: "url_citation",
url: "https://example.com",
start_index: 0,
end_index: 5,
title: "example"
}
]
},
{
type: "refusal",
refusal: "cannot comply"
}
]
},
{
id: "fc_1",
type: "function_call",
call_id: "call_1",
name: "lookup",
arguments: "{}",
status: "completed"
},
{
id: "reasoning_1",
type: "reasoning",
summary: [
{
type: "summary_text",
text: "thinking"
}
]
}
],
usage: {
input_tokens: 10,
output_tokens: 20,
total_tokens: 30
}
})
assert.strictEqual(decoded.id, "resp_123")
assert.strictEqual(decoded.output.length, 3)
assert.strictEqual(decoded.output[0].type, "message")
if (decoded.output[0].type === "message") {
assert.strictEqual(decoded.output[0].content[0].type, "output_text")
}
})
it("decodes required stream events", () => {
const response = makeResponse({ status: "in_progress" })
const applyPatchItem = {
id: "ap_1",
type: "apply_patch_call",
call_id: "call_ap",
operation: {
type: "update_file",
path: "README.md",
diff: "@@"
}
}
const events = [
{ type: "response.created", sequence_number: 1, response },
{
type: "response.completed",
sequence_number: 2,
response: makeResponse()
},
{ type: "response.incomplete", sequence_number: 3, response: makeResponse({ status: "incomplete" }) },
{ type: "response.failed", sequence_number: 4, response: makeResponse({ status: "failed" }) },
{ type: "response.output_item.added", sequence_number: 5, output_index: 0, item: applyPatchItem },
{ type: "response.output_item.done", sequence_number: 6, output_index: 0, item: applyPatchItem },
{
type: "response.output_text.delta",
sequence_number: 7,
item_id: "msg_1",
output_index: 0,
content_index: 0,
delta: "hel"
},
{
type: "response.output_text.annotation.added",
sequence_number: 8,
item_id: "msg_1",
output_index: 0,
content_index: 0,
annotation_index: 0,
annotation: {
type: "file_path",
file_id: "file_1",
index: 0
}
},
{
type: "response.reasoning_summary_part.added",
sequence_number: 9,
item_id: "reasoning_1",
output_index: 1,
summary_index: 0,
part: { type: "summary_text", text: "thinking" }
},
{
type: "response.reasoning_summary_part.done",
sequence_number: 10,
item_id: "reasoning_1",
output_index: 1,
summary_index: 0,
part: { type: "summary_text", text: "thinking" }
},
{
type: "response.reasoning_summary_text.delta",
sequence_number: 11,
item_id: "reasoning_1",
output_index: 1,
summary_index: 0,
delta: "..."
},
{
type: "response.function_call_arguments.delta",
sequence_number: 12,
item_id: "fc_1",
output_index: 2,
delta: "{"
},
{
type: "response.function_call_arguments.done",
sequence_number: 13,
item_id: "fc_1",
output_index: 2,
arguments: "{}"
},
{
type: "response.code_interpreter_call_code.delta",
sequence_number: 14,
item_id: "code_1",
output_index: 3,
delta: "print"
},
{
type: "response.code_interpreter_call_code.done",
sequence_number: 15,
item_id: "code_1",
output_index: 3,
code: "print('ok')"
},
{
type: "response.apply_patch_call_operation_diff.delta",
sequence_number: 16,
item_id: "ap_1",
output_index: 4,
delta: "@@"
},
{
type: "response.apply_patch_call_operation_diff.done",
sequence_number: 17,
item_id: "ap_1",
output_index: 4,
delta: "@@"
},
{
type: "response.image_generation_call.partial_image",
sequence_number: 18,
item_id: "img_1",
output_index: 5,
partial_image_b64: "AQID"
}
]
for (const event of events) {
const decoded = Schema.decodeUnknownSync(OpenAiSchema.ResponseStreamEvent)(event)
assert.strictEqual(decoded.type, event.type)
}
})
it.effect("keeps keepalive and unknown events tolerant in SSE decoding", () =>
Effect.gen(function*() {
const sseBody = [
{
type: "response.created",
sequence_number: 1,
response: makeResponse({ status: "in_progress" })
},
{
type: "keepalive",
sequence_number: 2,
heartbeat: true
},
{
type: "provider.future_event",
sequence_number: 3,
nested: { ok: true }
},
{
type: "response.completed",
sequence_number: 4,
response: makeResponse({ status: "completed" })
}
].map((event) => `data: ${JSON.stringify(event)}\n\n`).join("")
const events = yield* Stream.fromIterable([sseBody]).pipe(
Stream.pipeThroughChannel(Sse.decodeDataSchema(OpenAiSchema.ResponseStreamEvent)),
Stream.map((event) => event.data),
Stream.runCollect
)
const decoded = globalThis.Array.from(events)
assert.strictEqual(decoded.length, 4)
assert.strictEqual(decoded[1].type, "keepalive")
assert.strictEqual(decoded[2].type, "provider.future_event")
if (decoded[1].type === "keepalive") {
assert.strictEqual(decoded[1].sequence_number, 2)
}
}))
it.effect("does not silently decode malformed known events as unknown", () =>
Effect.gen(function*() {
const malformed = yield* Schema.decodeUnknownEffect(OpenAiSchema.ResponseStreamEvent)({
type: "response.completed",
sequence_number: 1
}).pipe(Effect.flip)
assert.isDefined(malformed)
}))
it("decodes embedding response variants (numeric + string/base64)", () => {
const numeric = Schema.decodeUnknownSync(OpenAiSchema.CreateEmbeddingResponse)({
object: "list",
model: "text-embedding-3-small",
data: [{ object: "embedding", index: 0, embedding: [0.1, 0.2] }],
usage: { prompt_tokens: 2, total_tokens: 2 }
})
const base64 = Schema.decodeUnknownSync(OpenAiSchema.CreateEmbeddingResponse)({
object: "list",
model: "text-embedding-3-small",
data: [{ object: "embedding", index: 0, embedding: "AQID" }],
usage: { prompt_tokens: 2, total_tokens: 2 }
})
assert.strictEqual(numeric.data[0].embedding[0], 0.1)
assert.strictEqual(base64.data[0].embedding, "AQID")
})
})

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"references": [
{ "path": "../../effect" }
]
}

View File

@@ -0,0 +1,6 @@
import { mergeConfig, type ViteUserConfig } from "vitest/config"
import shared from "../../../vitest.shared.ts"
const config: ViteUserConfig = {}
export default mergeConfig(shared, config)

View File

@@ -0,0 +1,725 @@
# @effect/ai-openrouter
## 4.0.0-beta.99
### Patch Changes
- Updated dependencies [[`8ce4795`](https://github.com/Effect-TS/effect/commit/8ce4795ccbaebca4292757db568c005a992546a4), [`80b539f`](https://github.com/Effect-TS/effect/commit/80b539f8aba68f478c75c35c2b4140c4ffc4fada), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`e6e6dba`](https://github.com/Effect-TS/effect/commit/e6e6dba6e9d86e7c2ad27dcedf289db76a19697f), [`bfb203e`](https://github.com/Effect-TS/effect/commit/bfb203e95aa439f731acad37fc3a9a831a190f1c), [`2e9a34a`](https://github.com/Effect-TS/effect/commit/2e9a34ac2bece4f3a206160480c991e3841dc67a), [`55d4eb3`](https://github.com/Effect-TS/effect/commit/55d4eb34f2c64d54f6a25a305b5c5438ebd7934e), [`bddb010`](https://github.com/Effect-TS/effect/commit/bddb010eac3d4436cb094edbbee7460c5440c162), [`a328835`](https://github.com/Effect-TS/effect/commit/a328835e50d76bc96648a1c1550456e8c9f81210), [`5560d05`](https://github.com/Effect-TS/effect/commit/5560d05aa6abdd29466d9c3412cc5e648b0adbde), [`8f6e3ad`](https://github.com/Effect-TS/effect/commit/8f6e3adb185b16e8820b98c509b308086f7ff1af), [`46997fa`](https://github.com/Effect-TS/effect/commit/46997fa60401f5e3c93daa4b61f7df8e31caaab4), [`9e6e12d`](https://github.com/Effect-TS/effect/commit/9e6e12d75c118cd265496f2880490d1f33a5c8bf), [`3394b93`](https://github.com/Effect-TS/effect/commit/3394b93d97d6f24fc38670641d1490289ffca7f1), [`febeabc`](https://github.com/Effect-TS/effect/commit/febeabc3f7c31094da000a23edeaabfe2ab00a38), [`54161c9`](https://github.com/Effect-TS/effect/commit/54161c98f6f3569e0c31842f54e6a257f9421c4c), [`385f7a4`](https://github.com/Effect-TS/effect/commit/385f7a4ee4a7359928597ea56d151dbaf5eb5802), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`7543afe`](https://github.com/Effect-TS/effect/commit/7543afea6f4d97d1f1ad876224323838a48daadd), [`44b9cf3`](https://github.com/Effect-TS/effect/commit/44b9cf3d240d726997b4bbcd0ede48e074d3c456), [`7eea4d0`](https://github.com/Effect-TS/effect/commit/7eea4d0b73ec554915d7066a71f46326ce2ba45f), [`0a8aa6a`](https://github.com/Effect-TS/effect/commit/0a8aa6acb90a72b91c24d17133c950e4cacd8abd), [`c8d9fcf`](https://github.com/Effect-TS/effect/commit/c8d9fcf7b030f7c474effbab2764ce7aee1c7209), [`9ca7f9a`](https://github.com/Effect-TS/effect/commit/9ca7f9a69363e4485645966d5a93b8f9597c5206), [`e7aca89`](https://github.com/Effect-TS/effect/commit/e7aca894bb32fbb785b5830837e6061c415a6015), [`55d7560`](https://github.com/Effect-TS/effect/commit/55d75609b8acf8a1b54c1b1c7fbbb65ec741aa3e), [`f809189`](https://github.com/Effect-TS/effect/commit/f809189ddf6b6011ba43a9901baaa734e315da2a), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`0ebdbe7`](https://github.com/Effect-TS/effect/commit/0ebdbe74463dc84385956d0b1e8c2b79ebab5400), [`7517d09`](https://github.com/Effect-TS/effect/commit/7517d09f12a0b183a81bd425962c4e280a68b05d), [`212493b`](https://github.com/Effect-TS/effect/commit/212493b9a1eb98cd1ef6959c707a2e5784a5ae91), [`88a54cc`](https://github.com/Effect-TS/effect/commit/88a54cc341006e3ebcb13482c618f62a680ce199), [`80ea8cb`](https://github.com/Effect-TS/effect/commit/80ea8cb9222ca73f564c8267ab2f82966fea027a), [`8df19f4`](https://github.com/Effect-TS/effect/commit/8df19f4fe81d90cc33ace88b9a77e5534f82d604)]:
- effect@4.0.0-beta.99
## 4.0.0-beta.98
### Patch Changes
- Updated dependencies [[`989603b`](https://github.com/Effect-TS/effect-smol/commit/989603b60ab1197b64acf214208e0d370cd1f842), [`214c458`](https://github.com/Effect-TS/effect-smol/commit/214c458084bb6995d543cd37d1055f24be3d454e), [`a037273`](https://github.com/Effect-TS/effect-smol/commit/a0372736ac34796969b051bbba4717d7983f1ebe), [`97fdaa9`](https://github.com/Effect-TS/effect-smol/commit/97fdaa9c1f522c65e579365d314a07878e2b904f), [`b24d248`](https://github.com/Effect-TS/effect-smol/commit/b24d248c8df44222ce642087cde2bd859a2dc709), [`19c222c`](https://github.com/Effect-TS/effect-smol/commit/19c222cac2353a3d7b7733caecb00556fffe9a5c), [`eec85dd`](https://github.com/Effect-TS/effect-smol/commit/eec85ddba09ea326fd268ee33eeffd47e50d4671), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`8849052`](https://github.com/Effect-TS/effect-smol/commit/884905232d1e9a365e046d8dde27bf9c5707f57f), [`c15e16a`](https://github.com/Effect-TS/effect-smol/commit/c15e16ad130d1fbde25d912b7ac55995066cb35b), [`01d00a3`](https://github.com/Effect-TS/effect-smol/commit/01d00a3abfbf1f37996cdbe738ea5137c646cdd7), [`8bd4589`](https://github.com/Effect-TS/effect-smol/commit/8bd458975a1b3a8ed042eccf317b93d28ded91e7), [`0082f4f`](https://github.com/Effect-TS/effect-smol/commit/0082f4f74fb139fd578f87f0a790e845133983dc), [`6e08428`](https://github.com/Effect-TS/effect-smol/commit/6e08428d980501b856f846ad3f3f0e4ea46e7786), [`388dcf9`](https://github.com/Effect-TS/effect-smol/commit/388dcf953f65d317547f34d40e6443c5f264205f), [`2b7ce2b`](https://github.com/Effect-TS/effect-smol/commit/2b7ce2b513e7ec2a77822f1116dc6ffb6ba93f4e), [`87bea7e`](https://github.com/Effect-TS/effect-smol/commit/87bea7e16259246f3bcdf565446394751abca953), [`ce38dc3`](https://github.com/Effect-TS/effect-smol/commit/ce38dc33bda805a684432cca071f4dc3c6b9a1ba), [`a807cd1`](https://github.com/Effect-TS/effect-smol/commit/a807cd170341deca8a1cfb52c4222585f2431bb9), [`fd8a356`](https://github.com/Effect-TS/effect-smol/commit/fd8a356f06a8c9ce4e7e0a13fc4021c178ed31de), [`c2a5edc`](https://github.com/Effect-TS/effect-smol/commit/c2a5edc3abd31ad5bc123362bc1213e03e4095c3), [`5946da3`](https://github.com/Effect-TS/effect-smol/commit/5946da3804a1be5e752b05b96bd058cdba50a1bf), [`4ae0c5f`](https://github.com/Effect-TS/effect-smol/commit/4ae0c5ffcbe6c56ddfcb05c639112a079483539e), [`5b2a0bc`](https://github.com/Effect-TS/effect-smol/commit/5b2a0bceea3a28a33a58555210c90a415dc74a76), [`72ac585`](https://github.com/Effect-TS/effect-smol/commit/72ac585884befde6af9208da738699a93f1bae79), [`5e8c1b8`](https://github.com/Effect-TS/effect-smol/commit/5e8c1b82bfafa121311f987a49ab75395e3647a7), [`0f9c078`](https://github.com/Effect-TS/effect-smol/commit/0f9c07841b04183f485ee6e6458de73b290b09f5)]:
- effect@4.0.0-beta.98
## 4.0.0-beta.97
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.97
## 4.0.0-beta.96
### Patch Changes
- Updated dependencies [[`1503f45`](https://github.com/Effect-TS/effect-smol/commit/1503f45cb5bb2a74f4705252ec505a1f0ade7e62), [`57fe793`](https://github.com/Effect-TS/effect-smol/commit/57fe79316ffbc380b30626a168981fb26ae97459), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`0c2f78f`](https://github.com/Effect-TS/effect-smol/commit/0c2f78f695ec474e1ff5474da183577975e418f5), [`97f29df`](https://github.com/Effect-TS/effect-smol/commit/97f29df457f7ffd07cfb4b379315c12c086af805)]:
- effect@4.0.0-beta.96
## 4.0.0-beta.95
### Patch Changes
- Updated dependencies [[`a482442`](https://github.com/Effect-TS/effect-smol/commit/a482442abdeb490e9652b854ec3495e4aa7273e7), [`fbefa85`](https://github.com/Effect-TS/effect-smol/commit/fbefa850fab2f0a302c20614496aeaaa2a8b5590), [`0b4a32f`](https://github.com/Effect-TS/effect-smol/commit/0b4a32f4260f0d8500942a133001b0d349328102), [`18a49e1`](https://github.com/Effect-TS/effect-smol/commit/18a49e1786679456258002ff9397faf02f678c2d), [`266cb90`](https://github.com/Effect-TS/effect-smol/commit/266cb90bb2c17aabc40563c32db334f09ba3d74b), [`912f095`](https://github.com/Effect-TS/effect-smol/commit/912f095a34572bbd3cedf6edb27878443e3e4a95), [`a6718f9`](https://github.com/Effect-TS/effect-smol/commit/a6718f9e00a15ca903b0732da46116cbf3d6aca7), [`bef5154`](https://github.com/Effect-TS/effect-smol/commit/bef51540a243aa2f872a00c01d0cd58b7a769baa), [`18e0564`](https://github.com/Effect-TS/effect-smol/commit/18e0564bd0f8ebbdfcaf1e2c21529948e9e4a81d), [`fb50f14`](https://github.com/Effect-TS/effect-smol/commit/fb50f14fc3657c1973785aa5b72ecf0b0d28e0b2)]:
- effect@4.0.0-beta.95
## 4.0.0-beta.94
### Patch Changes
- Updated dependencies [[`95a0e9b`](https://github.com/Effect-TS/effect-smol/commit/95a0e9bb62797af0e81c9998773405f248f218c5), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63), [`f11ce73`](https://github.com/Effect-TS/effect-smol/commit/f11ce73af60823754dc24194f4ffc561b9ea1c2d), [`ff30b6e`](https://github.com/Effect-TS/effect-smol/commit/ff30b6e7c2c63ffc56a4c5818d6d86b01b5ad528), [`1caab3c`](https://github.com/Effect-TS/effect-smol/commit/1caab3cc30f626efbf15e59d74f539a487e5c85c), [`aa80c47`](https://github.com/Effect-TS/effect-smol/commit/aa80c4775a04db87553e5568764cab7e32a72814), [`c2ae4fc`](https://github.com/Effect-TS/effect-smol/commit/c2ae4fce2f03a4cd1861c2b1179da7df656e662d), [`a0a3490`](https://github.com/Effect-TS/effect-smol/commit/a0a3490bbce765f199d8e077aceac504f0462e63)]:
- effect@4.0.0-beta.94
## 4.0.0-beta.93
### Patch Changes
- Updated dependencies [[`00652fe`](https://github.com/Effect-TS/effect-smol/commit/00652fe95c18f87208e91343eb8bf218faa2f677), [`6c58167`](https://github.com/Effect-TS/effect-smol/commit/6c5816746eaf91d2a3c7c899c5720809fa230ae3), [`2bc5415`](https://github.com/Effect-TS/effect-smol/commit/2bc541501a7ef89e542d7cb98e96beb53cd205cc), [`e11cccc`](https://github.com/Effect-TS/effect-smol/commit/e11cccc7d5fe631abccc7d6e3bd296938de0fa2e), [`ba7e77e`](https://github.com/Effect-TS/effect-smol/commit/ba7e77e046b8641a3a4e9750bb88ca4a1d063d3f), [`5713ee7`](https://github.com/Effect-TS/effect-smol/commit/5713ee7edbc3054efde407b2286bbfd45bbc6e1c)]:
- effect@4.0.0-beta.93
## 4.0.0-beta.92
### Patch Changes
- Updated dependencies [[`affdc13`](https://github.com/Effect-TS/effect-smol/commit/affdc139045cc325dce321a84a580fdc1b2da7b9)]:
- effect@4.0.0-beta.92
## 4.0.0-beta.91
### Patch Changes
- Updated dependencies [[`b135b25`](https://github.com/Effect-TS/effect-smol/commit/b135b2517fca9e7839734ace3699a7dfa75b9075), [`aaa21a3`](https://github.com/Effect-TS/effect-smol/commit/aaa21a369a171c600db294f2a4f640583043e150), [`3475ee6`](https://github.com/Effect-TS/effect-smol/commit/3475ee6c2bda6b05c6d7a12ce30c8bb840b5b1a6)]:
- effect@4.0.0-beta.91
## 4.0.0-beta.90
### Patch Changes
- Updated dependencies [[`d237fdf`](https://github.com/Effect-TS/effect-smol/commit/d237fdf726481f76eb52a6196e111b24122bc3d5)]:
- effect@4.0.0-beta.90
## 4.0.0-beta.89
### Patch Changes
- Updated dependencies [[`b7d46ab`](https://github.com/Effect-TS/effect-smol/commit/b7d46ab7e1a29d8711817bab583c9febf48a0dad), [`7777e15`](https://github.com/Effect-TS/effect-smol/commit/7777e1540fd3680dd8346723cffec812b9384669), [`5376197`](https://github.com/Effect-TS/effect-smol/commit/5376197ca8e50358a41b1fd3cec27bd1ec680ec6)]:
- effect@4.0.0-beta.89
## 4.0.0-beta.88
### Patch Changes
- Updated dependencies [[`911f1b8`](https://github.com/Effect-TS/effect-smol/commit/911f1b84790ce42b3a70c95b33e6f6fd9e74de8b), [`8beeeea`](https://github.com/Effect-TS/effect-smol/commit/8beeeea52879d8613a39468848f01c3092bd54d4), [`c306fcf`](https://github.com/Effect-TS/effect-smol/commit/c306fcfeb1ef38455156932a1faf49292b1318da)]:
- effect@4.0.0-beta.88
## 4.0.0-beta.87
### Patch Changes
- Updated dependencies [[`5a0c1a4`](https://github.com/Effect-TS/effect-smol/commit/5a0c1a4faee5707b5cc35e646ff1ffdad70f1956), [`1eea2ea`](https://github.com/Effect-TS/effect-smol/commit/1eea2ea3795ba47316b82b1ac8d4612c0ba389ed)]:
- effect@4.0.0-beta.87
## 4.0.0-beta.86
### Patch Changes
- Updated dependencies [[`0b5795a`](https://github.com/Effect-TS/effect-smol/commit/0b5795a0ab4395e8f15955d8d96f2303084bfc64), [`3e3a859`](https://github.com/Effect-TS/effect-smol/commit/3e3a859ec6351a9e0d31674aabbd48fcefabb12e), [`7dbec24`](https://github.com/Effect-TS/effect-smol/commit/7dbec240dbf3bca599a20c486632abce694ef5ab), [`d8c00a1`](https://github.com/Effect-TS/effect-smol/commit/d8c00a171ac7141e8adc08c332d1162d9a9d56fc), [`85b6317`](https://github.com/Effect-TS/effect-smol/commit/85b631701e935866f2762bd595237aa718370cd9), [`6d0fda0`](https://github.com/Effect-TS/effect-smol/commit/6d0fda0d0cbdfffc523c89c57dfdb1608f84fb12), [`108a933`](https://github.com/Effect-TS/effect-smol/commit/108a9335ff8571928197e5847a09c28ac83d6f46), [`7e1f455`](https://github.com/Effect-TS/effect-smol/commit/7e1f455fab5005d769b939c91e519d450f802cf9), [`46b3e79`](https://github.com/Effect-TS/effect-smol/commit/46b3e79944cfdae7901eb148135c85b7eb39834e)]:
- effect@4.0.0-beta.86
## 4.0.0-beta.85
### Patch Changes
- Updated dependencies [[`328d97c`](https://github.com/Effect-TS/effect-smol/commit/328d97cc53c0dcb89077a5623e35b095eaa59a8c), [`8441836`](https://github.com/Effect-TS/effect-smol/commit/8441836e6dde70e8ae2126be9cefe9b45798b134), [`074e436`](https://github.com/Effect-TS/effect-smol/commit/074e4361091289104cb0ab6959dc3b0ea7794a6a), [`c1dfd60`](https://github.com/Effect-TS/effect-smol/commit/c1dfd60663eb13a58916f3712d877499943b628a), [`2ba316b`](https://github.com/Effect-TS/effect-smol/commit/2ba316bd15fcbf1c50626500d44a2c9b3bec19f5), [`7ce7344`](https://github.com/Effect-TS/effect-smol/commit/7ce7344c41056c79e2ee19ee6a9346c0f1d227c1)]:
- effect@4.0.0-beta.85
## 4.0.0-beta.84
### Patch Changes
- [#2379](https://github.com/Effect-TS/effect-smol/pull/2379) [`717d1c8`](https://github.com/Effect-TS/effect-smol/commit/717d1c8b160d4de631b6b7938abcad9e3472c3d7) Thanks @walln! - Convert audio file parts in prompts into OpenRouter `input_audio` content blocks.
Previously, every non-image file part was converted into a generic `file` content block. OpenRouter only accepts audio as base64-encoded `input_audio` content parts, so audio attachments were rejected or mishandled by the upstream provider.
Audio file parts with a recognized `mediaType` (aac, aiff, flac, m4a, mp3, ogg, pcm16, pcm24, and wav) are now converted into `input_audio` blocks. Unsupported audio media types and `URL` audio data fail with an `AiError` since OpenRouter requires base64-encoded audio data and does not fetch audio URLs.
- Updated dependencies [[`87f52ba`](https://github.com/Effect-TS/effect-smol/commit/87f52ba16c4370ffa3f84bf8e53038e1419c284e), [`b8ee07f`](https://github.com/Effect-TS/effect-smol/commit/b8ee07ffda8903b5ec2e45a786ddcba59f128fda), [`867c0d7`](https://github.com/Effect-TS/effect-smol/commit/867c0d70a09079b040260d45a1e92ff04dbfbf2f), [`b93bc6c`](https://github.com/Effect-TS/effect-smol/commit/b93bc6c9cb27b909a41d094c97c4f9d25bbc6d6b), [`57d387f`](https://github.com/Effect-TS/effect-smol/commit/57d387f92c30ab63e15e3e641f0a903b65886610), [`bacca41`](https://github.com/Effect-TS/effect-smol/commit/bacca4141c2400effae1eabfdb36c89a459cf246), [`0f8ac79`](https://github.com/Effect-TS/effect-smol/commit/0f8ac7959d29ed68c68ce25aabd6bf0cb7e63ecc), [`25b4482`](https://github.com/Effect-TS/effect-smol/commit/25b448270c01317703f25107e1480d4cd0246d9a), [`9cf3a25`](https://github.com/Effect-TS/effect-smol/commit/9cf3a25c66b0c44a52be9829870c44517ea52db2), [`8def767`](https://github.com/Effect-TS/effect-smol/commit/8def7674b1787f91035298cda4d122937e87ef72)]:
- effect@4.0.0-beta.84
## 4.0.0-beta.83
### Patch Changes
- Updated dependencies [[`1f2e8ce`](https://github.com/Effect-TS/effect-smol/commit/1f2e8ceef09e0a791c850ed2ade01f97089596f9)]:
- effect@4.0.0-beta.83
## 4.0.0-beta.82
### Patch Changes
- Updated dependencies [[`193690b`](https://github.com/Effect-TS/effect-smol/commit/193690b642ea802bbed40d663bd677251bbe9dc3)]:
- effect@4.0.0-beta.82
## 4.0.0-beta.81
### Patch Changes
- Updated dependencies [[`93cb4f8`](https://github.com/Effect-TS/effect-smol/commit/93cb4f8fbfb9e07cb9dc86ce6b155fd1f8167914), [`60341d9`](https://github.com/Effect-TS/effect-smol/commit/60341d9ca744d0473ce3fab621ca9bd225af3a39), [`1105ab5`](https://github.com/Effect-TS/effect-smol/commit/1105ab56cb724212f7ea7b431396ce82e8fd0484), [`4500fbf`](https://github.com/Effect-TS/effect-smol/commit/4500fbfe00763d8a72af6e5d6c5988e8bd4ade36)]:
- effect@4.0.0-beta.81
## 4.0.0-beta.80
### Patch Changes
- Updated dependencies [[`d944330`](https://github.com/Effect-TS/effect-smol/commit/d94433090ee03f426d43e13b883abae4494e55e6), [`f48659f`](https://github.com/Effect-TS/effect-smol/commit/f48659fdcc84930ebc1e5b45b540c0f973389182), [`7652aaa`](https://github.com/Effect-TS/effect-smol/commit/7652aaa3bdbc39f241fe58b54b9a43b713e22e12), [`98630b7`](https://github.com/Effect-TS/effect-smol/commit/98630b7c8f679c352ba6796636c85688fa009d8d), [`90ae23c`](https://github.com/Effect-TS/effect-smol/commit/90ae23cf07284da5e1bcd9dffa882e85df7e617b)]:
- effect@4.0.0-beta.80
## 4.0.0-beta.79
### Patch Changes
- Updated dependencies [[`b9704dc`](https://github.com/Effect-TS/effect-smol/commit/b9704dc9de9f1649ad502371014fe869b69a49a3), [`a207113`](https://github.com/Effect-TS/effect-smol/commit/a207113f66837bb54416926718a9a7d66774d079), [`5e9b9e2`](https://github.com/Effect-TS/effect-smol/commit/5e9b9e217b164ebfd4a002dd4380b3b1563200c3), [`7c128ae`](https://github.com/Effect-TS/effect-smol/commit/7c128aef458a1e2d224712e51c483c9badad1d44), [`0ada457`](https://github.com/Effect-TS/effect-smol/commit/0ada457c0513d8d908254ab77ebb7d29d2b523d6), [`d7cc5a2`](https://github.com/Effect-TS/effect-smol/commit/d7cc5a2bede3de10943aa0c6bdb4f26836a91efd), [`aad63be`](https://github.com/Effect-TS/effect-smol/commit/aad63becf65e0a6b076e94f8973be7bbe7fbd46f), [`09809f6`](https://github.com/Effect-TS/effect-smol/commit/09809f60f19ec98232f98b33e33e02ecb7e4fbd6), [`2fddda5`](https://github.com/Effect-TS/effect-smol/commit/2fddda5311929f46b61e503f0ade4fc749e8c77d), [`5f21768`](https://github.com/Effect-TS/effect-smol/commit/5f2176833399757c4500d8875b7f2fba0393de75), [`f27003e`](https://github.com/Effect-TS/effect-smol/commit/f27003e00524ff83f20dd9909f62b2f8795efe03)]:
- effect@4.0.0-beta.79
## 4.0.0-beta.78
### Patch Changes
- Updated dependencies [[`7836b8e`](https://github.com/Effect-TS/effect-smol/commit/7836b8eb8bb0f3e04cdf554ee070caccf74f00c1), [`35d49a3`](https://github.com/Effect-TS/effect-smol/commit/35d49a3a09bdba6b513de87ddcead9e61a1042ba), [`4093258`](https://github.com/Effect-TS/effect-smol/commit/40932580e65bafab5f23c5f14b520cb411d0b2cd)]:
- effect@4.0.0-beta.78
## 4.0.0-beta.77
### Patch Changes
- Updated dependencies [[`6e9a5ca`](https://github.com/Effect-TS/effect-smol/commit/6e9a5ca62a61156fd67b2518ad3ab14ac0d25f23), [`302f398`](https://github.com/Effect-TS/effect-smol/commit/302f3984ce206e35d86ddd99d3b72be144850a51)]:
- effect@4.0.0-beta.77
## 4.0.0-beta.76
### Patch Changes
- Updated dependencies [[`016108a`](https://github.com/Effect-TS/effect-smol/commit/016108a472af7048ddbbfd05f233e67529fafe12), [`95c03d2`](https://github.com/Effect-TS/effect-smol/commit/95c03d2c55930668c215b5a41c23cf7742fead84), [`07299a3`](https://github.com/Effect-TS/effect-smol/commit/07299a33c09fd52faa9810d30835a2622c752386)]:
- effect@4.0.0-beta.76
## 4.0.0-beta.75
### Patch Changes
- Updated dependencies [[`81b187c`](https://github.com/Effect-TS/effect-smol/commit/81b187c17a0d8817b58232826939154010ae49d7), [`ad4b535`](https://github.com/Effect-TS/effect-smol/commit/ad4b535e17f94ce35261829d5a3675f0a7808b4e), [`a29c2e7`](https://github.com/Effect-TS/effect-smol/commit/a29c2e7e3570920156702671d6f3367cd0195f6c), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`1fdd9ae`](https://github.com/Effect-TS/effect-smol/commit/1fdd9aeed92b6bb70987c862e7f6f66ead0339b3), [`ffea4ec`](https://github.com/Effect-TS/effect-smol/commit/ffea4ecf2925f6a4c9fd13079d47584cbf2bed00), [`4255c9b`](https://github.com/Effect-TS/effect-smol/commit/4255c9ba78bb98c7838fbe9dccdd8465e9da5427)]:
- effect@4.0.0-beta.75
## 4.0.0-beta.74
### Patch Changes
- Updated dependencies [[`b1fc6a4`](https://github.com/Effect-TS/effect-smol/commit/b1fc6a4b4d0ca7fa9fd162799ae17c86f2f7ee8e)]:
- effect@4.0.0-beta.74
## 4.0.0-beta.73
### Patch Changes
- Updated dependencies [[`361ca30`](https://github.com/Effect-TS/effect-smol/commit/361ca30eb6e134feece547d6e00f82be4cb23f75), [`b9598c6`](https://github.com/Effect-TS/effect-smol/commit/b9598c6a209e75bfdb87ee3b024ecd1e3923ff6e)]:
- effect@4.0.0-beta.73
## 4.0.0-beta.72
### Patch Changes
- Updated dependencies [[`73e67d1`](https://github.com/Effect-TS/effect-smol/commit/73e67d119a84d697773eaecb4865c6a71eb1a9cb), [`01d71ec`](https://github.com/Effect-TS/effect-smol/commit/01d71ec5a75f3c2747a8d3b1ad9701d1e27b7ce5), [`fcd707e`](https://github.com/Effect-TS/effect-smol/commit/fcd707e091a16e1b35343c901cc4052274e32239)]:
- effect@4.0.0-beta.72
## 4.0.0-beta.71
### Patch Changes
- Updated dependencies [[`d8ac76b`](https://github.com/Effect-TS/effect-smol/commit/d8ac76b5bad458c42cebe8a0c1b3843f955ac293), [`2c3c00a`](https://github.com/Effect-TS/effect-smol/commit/2c3c00af6faba7b7d422af26a7a2bbc35636d230), [`3751e7c`](https://github.com/Effect-TS/effect-smol/commit/3751e7cf353e7a54cd692c37401207d9afba1e63), [`fc5f25b`](https://github.com/Effect-TS/effect-smol/commit/fc5f25b03ada5fc2431987768a74d3d3e75ca485), [`7ccced4`](https://github.com/Effect-TS/effect-smol/commit/7ccced42867c14c013b01160b3d292f14c05bd04), [`a2e1fe5`](https://github.com/Effect-TS/effect-smol/commit/a2e1fe5835c98c8ee4393a091b1d11b75126e349), [`4a4a36b`](https://github.com/Effect-TS/effect-smol/commit/4a4a36b10e6e616cad07584a43908f6a7e07e618), [`d350292`](https://github.com/Effect-TS/effect-smol/commit/d3502922b4740fa9d745797cbc3775cb67839b6d), [`730afb6`](https://github.com/Effect-TS/effect-smol/commit/730afb66696adf9bd5a328cbca29df9c05968771), [`df1b008`](https://github.com/Effect-TS/effect-smol/commit/df1b008f370f414c2a67a7b8139ef747af8e5fba), [`6d469d5`](https://github.com/Effect-TS/effect-smol/commit/6d469d567a7c41d7e5343bdee21d45b07b0e8190)]:
- effect@4.0.0-beta.71
## 4.0.0-beta.70
### Patch Changes
- Updated dependencies [[`af7782d`](https://github.com/Effect-TS/effect-smol/commit/af7782d3008d08b043f3a3f261516001514b2b4e), [`7212d70`](https://github.com/Effect-TS/effect-smol/commit/7212d701a3eee7b3553ff502e2c066126e52e839)]:
- effect@4.0.0-beta.70
## 4.0.0-beta.69
### Patch Changes
- Updated dependencies [[`70ea04a`](https://github.com/Effect-TS/effect-smol/commit/70ea04aa96a2a7859d738d414e1f0e3ed081a27a), [`d0ea8b0`](https://github.com/Effect-TS/effect-smol/commit/d0ea8b03f7d73ae076c1db12666141e480d11178), [`a57674b`](https://github.com/Effect-TS/effect-smol/commit/a57674b64845e9e75a456cf907bfdcb858859118), [`59aa334`](https://github.com/Effect-TS/effect-smol/commit/59aa334fbd0a504dda3c36f6d2ef1be7449b4b8b), [`8f4208e`](https://github.com/Effect-TS/effect-smol/commit/8f4208ee83bc7bdaa6793b5429847b45aab72470)]:
- effect@4.0.0-beta.69
## 4.0.0-beta.68
### Patch Changes
- Updated dependencies [[`af8267f`](https://github.com/Effect-TS/effect-smol/commit/af8267f2f3588c3fb611e9286f6f933f29ce1217), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`0176eaf`](https://github.com/Effect-TS/effect-smol/commit/0176eaf3ecd7c1b99a10268f2af02d7e8ce161e5), [`f136bb7`](https://github.com/Effect-TS/effect-smol/commit/f136bb763048cbc6b17edd26496dba3e2415b9fa), [`6f38f07`](https://github.com/Effect-TS/effect-smol/commit/6f38f07d5941a211b251383aaab0f4f55e8a6557), [`aec9c40`](https://github.com/Effect-TS/effect-smol/commit/aec9c401a53db227f18bf5e0c84db7130ad862d6)]:
- effect@4.0.0-beta.68
## 4.0.0-beta.67
### Patch Changes
- Updated dependencies [[`a42ef66`](https://github.com/Effect-TS/effect-smol/commit/a42ef6632abbddfa820995ae310ccc84ae8d9b6f), [`35594f8`](https://github.com/Effect-TS/effect-smol/commit/35594f811cafe471acd490114b103a1f8392c8d8), [`8bddd62`](https://github.com/Effect-TS/effect-smol/commit/8bddd628cb623f9533d345082583ff51cead6836), [`4be4c8d`](https://github.com/Effect-TS/effect-smol/commit/4be4c8d60862aa963869ee2ed9ffa048ffac0527), [`0c9d3ab`](https://github.com/Effect-TS/effect-smol/commit/0c9d3ab43eb721a370ed8306260cbac218c27e87), [`b156acc`](https://github.com/Effect-TS/effect-smol/commit/b156accd2691b4a051f823affdece7c39923ce85), [`d16c034`](https://github.com/Effect-TS/effect-smol/commit/d16c03434ee3e6dcd3bfc82b65d99e881d89025b), [`b559d68`](https://github.com/Effect-TS/effect-smol/commit/b559d68845f848a10153395778f035682d399075), [`a3de5d9`](https://github.com/Effect-TS/effect-smol/commit/a3de5d9215e5cc4a62e2666efbd7c1bf595eb84f), [`7e6c12e`](https://github.com/Effect-TS/effect-smol/commit/7e6c12ec9b3a5945f6c26e272cc8f6390541ad3e), [`098167a`](https://github.com/Effect-TS/effect-smol/commit/098167a220fe07da6f14455818733ab1b269c9dd)]:
- effect@4.0.0-beta.67
## 4.0.0-beta.66
### Patch Changes
- Updated dependencies [[`ca2498e`](https://github.com/Effect-TS/effect-smol/commit/ca2498e702ac2d83fb7187707b7eb069bdb261a2), [`cd7d1fb`](https://github.com/Effect-TS/effect-smol/commit/cd7d1fba7e2e2c5ac3ad64e1be433440a5bda436), [`19a7033`](https://github.com/Effect-TS/effect-smol/commit/19a703367ec817cffc41d152da9b594827408e2b), [`33d26b4`](https://github.com/Effect-TS/effect-smol/commit/33d26b4210b2e974f146a71e7eed962f8ce00900), [`856766b`](https://github.com/Effect-TS/effect-smol/commit/856766b2c506aaed6d2df1d63bf3a5b1b062e1d4), [`079c7df`](https://github.com/Effect-TS/effect-smol/commit/079c7df82559bb9ce10a86dffb85d25e6ce07dc3)]:
- effect@4.0.0-beta.66
## 4.0.0-beta.65
### Patch Changes
- Updated dependencies [[`6f11454`](https://github.com/Effect-TS/effect-smol/commit/6f11454a9b6c3bd00f6b35fd7af14a2f2d63a0a2)]:
- effect@4.0.0-beta.65
## 4.0.0-beta.64
### Patch Changes
- Updated dependencies [[`7d4877a`](https://github.com/Effect-TS/effect-smol/commit/7d4877a1929cdb690280ea254326c04f2ec97ea5)]:
- effect@4.0.0-beta.64
## 4.0.0-beta.63
### Patch Changes
- Updated dependencies [[`7f927ff`](https://github.com/Effect-TS/effect-smol/commit/7f927ffb7a9801dcfc4096c29e369d13d65cd0ac), [`a696b3e`](https://github.com/Effect-TS/effect-smol/commit/a696b3e83a8504cdbe261a18c10a1cc0619ae102)]:
- effect@4.0.0-beta.63
## 4.0.0-beta.62
### Patch Changes
- Updated dependencies [[`4ab4b90`](https://github.com/Effect-TS/effect-smol/commit/4ab4b9007dc27a52ffabc6fcb37c96eeec795bf7)]:
- effect@4.0.0-beta.62
## 4.0.0-beta.61
### Patch Changes
- Updated dependencies [[`50790af`](https://github.com/Effect-TS/effect-smol/commit/50790af9b190c38d10fb0723837d49b66432638f), [`71f7c3d`](https://github.com/Effect-TS/effect-smol/commit/71f7c3df997deda92c84146d569696dab3bd645c), [`aae8797`](https://github.com/Effect-TS/effect-smol/commit/aae8797b9cb383be0c182dd58d03d787c354238b)]:
- effect@4.0.0-beta.61
## 4.0.0-beta.60
### Patch Changes
- Updated dependencies [[`f69d567`](https://github.com/Effect-TS/effect-smol/commit/f69d5675dcff9f4137295752baf066b7153fdc09), [`7909c95`](https://github.com/Effect-TS/effect-smol/commit/7909c954b8f6244a35a4b429f8dd0dff45dad620), [`bbb4dcc`](https://github.com/Effect-TS/effect-smol/commit/bbb4dcc6c406b83a416b4ad3541cc02037c420e4), [`7af2207`](https://github.com/Effect-TS/effect-smol/commit/7af2207901eabf3132c1b7010a69b3899c06fbbe), [`848b40a`](https://github.com/Effect-TS/effect-smol/commit/848b40a4bd4bf54a5098617d50c33c88eee8270a)]:
- effect@4.0.0-beta.60
## 4.0.0-beta.59
### Patch Changes
- Updated dependencies [[`56837ea`](https://github.com/Effect-TS/effect-smol/commit/56837ea2a338395b35550641374e9e589bd8b71d)]:
- effect@4.0.0-beta.59
## 4.0.0-beta.58
### Patch Changes
- Updated dependencies [[`11993d4`](https://github.com/Effect-TS/effect-smol/commit/11993d4934c66f5dc611b8bbf553f01d501ef8f7), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec), [`96c8b22`](https://github.com/Effect-TS/effect-smol/commit/96c8b22c2057ccddbf10ed269d7697f22119b3ec)]:
- effect@4.0.0-beta.58
## 4.0.0-beta.57
### Patch Changes
- Updated dependencies [[`a971f5c`](https://github.com/Effect-TS/effect-smol/commit/a971f5cbd92dfe4274420bf0966595eb35531060), [`8e110c5`](https://github.com/Effect-TS/effect-smol/commit/8e110c5f02a429ccc43a91df8678e402138c0851)]:
- effect@4.0.0-beta.57
## 4.0.0-beta.56
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.56
## 4.0.0-beta.55
### Patch Changes
- Updated dependencies [[`42cc744`](https://github.com/Effect-TS/effect-smol/commit/42cc744570968deb365fb46d47b53d3277050c93), [`04855ce`](https://github.com/Effect-TS/effect-smol/commit/04855ceeca4d40c55a5750dd9893b691f8ea741a)]:
- effect@4.0.0-beta.55
## 4.0.0-beta.54
### Patch Changes
- Updated dependencies [[`e4b74f9`](https://github.com/Effect-TS/effect-smol/commit/e4b74f9c01a0e9b6cd58416de4af3a26d51da7c8), [`4c72808`](https://github.com/Effect-TS/effect-smol/commit/4c728081851c66dacf889a816535671bc841ae96)]:
- effect@4.0.0-beta.54
## 4.0.0-beta.53
### Patch Changes
- Updated dependencies [[`0768509`](https://github.com/Effect-TS/effect-smol/commit/07685094e931af07d104165195826a535b55fa7e), [`476aede`](https://github.com/Effect-TS/effect-smol/commit/476aede69c6efa06b5781ca5eb3e3b128ca29141), [`4f79c54`](https://github.com/Effect-TS/effect-smol/commit/4f79c542e7b508c235ff485d862cc8b29a8260c5), [`4be6a7c`](https://github.com/Effect-TS/effect-smol/commit/4be6a7cf35dab2a01d652f56dd35f0358c5a7e88), [`88927eb`](https://github.com/Effect-TS/effect-smol/commit/88927ebb896162cdba103b36553280b58e0facac)]:
- effect@4.0.0-beta.53
## 4.0.0-beta.52
### Patch Changes
- Updated dependencies [[`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`cf3a311`](https://github.com/Effect-TS/effect-smol/commit/cf3a311d863a8abb818840c3b80f847e621c43c1), [`8e04bfc`](https://github.com/Effect-TS/effect-smol/commit/8e04bfc95554b74eac205d67a20388e056b21499), [`131fdd5`](https://github.com/Effect-TS/effect-smol/commit/131fdd5b1f26531e265fe1a08f002002f47c276e)]:
- effect@4.0.0-beta.52
## 4.0.0-beta.51
### Patch Changes
- Updated dependencies [[`778d2af`](https://github.com/Effect-TS/effect-smol/commit/778d2afe9b5154bc1f9abae46d93ea7e54c87344), [`4e24dcf`](https://github.com/Effect-TS/effect-smol/commit/4e24dcf75037f65eebc1eb68623bc7cbf9d5512a), [`4b1c015`](https://github.com/Effect-TS/effect-smol/commit/4b1c0150e9bdb5559ed32d250deb66e17b4240c7), [`454f8ad`](https://github.com/Effect-TS/effect-smol/commit/454f8adad822929c3ef60f8280d0987226b049fd), [`6754a0c`](https://github.com/Effect-TS/effect-smol/commit/6754a0cd18626b06805a079cc5265525a5eb7d27), [`90f7fd5`](https://github.com/Effect-TS/effect-smol/commit/90f7fd5243871b30980964135db4512b8119fa82), [`d7e1519`](https://github.com/Effect-TS/effect-smol/commit/d7e151974934201fd93fa4c8a1192ee9a5d965a0), [`72a8122`](https://github.com/Effect-TS/effect-smol/commit/72a81228e09782bae512f7d041bbfbc78bc668d0)]:
- effect@4.0.0-beta.51
## 4.0.0-beta.50
### Patch Changes
- Updated dependencies [[`07be594`](https://github.com/Effect-TS/effect-smol/commit/07be594825de60f8e1b2102d21dbb9b8fc63b414), [`ae02433`](https://github.com/Effect-TS/effect-smol/commit/ae02433103ce28f53a0c9bfb4a44e75773289b7b)]:
- effect@4.0.0-beta.50
## 4.0.0-beta.49
### Patch Changes
- Updated dependencies [[`7d87873`](https://github.com/Effect-TS/effect-smol/commit/7d8787340ff549370f6f2a88b612e9ebbfd6ba45), [`c2f6f90`](https://github.com/Effect-TS/effect-smol/commit/c2f6f901b200a6e515b4f02c93ce8005b7bbf1c5), [`216f13c`](https://github.com/Effect-TS/effect-smol/commit/216f13c1fce454a21b489bb915714a17e791a1ac)]:
- effect@4.0.0-beta.49
## 4.0.0-beta.48
### Patch Changes
- Updated dependencies [[`4da56ec`](https://github.com/Effect-TS/effect-smol/commit/4da56ecff129b2da40137ffede23a73cc4e532d8), [`a5e6f77`](https://github.com/Effect-TS/effect-smol/commit/a5e6f774bab195cf50ecdc818240765f69a3bf4a), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070), [`f1ba5b8`](https://github.com/Effect-TS/effect-smol/commit/f1ba5b8584d325a541156928cecf041b37fd5070)]:
- effect@4.0.0-beta.48
## 4.0.0-beta.47
### Patch Changes
- Updated dependencies [[`c584726`](https://github.com/Effect-TS/effect-smol/commit/c58472674e750e6938df955044eab88feda95e45), [`86a91a4`](https://github.com/Effect-TS/effect-smol/commit/86a91a4f0c59286dfa9393232d8020dea70ed4db), [`131caf9`](https://github.com/Effect-TS/effect-smol/commit/131caf9525151a0cb29803a8f1dffa0f4f479d12), [`c3615c8`](https://github.com/Effect-TS/effect-smol/commit/c3615c88379b9daf252df0db72c6ac5a20326406)]:
- effect@4.0.0-beta.47
## 4.0.0-beta.46
### Patch Changes
- Updated dependencies [[`3a30b9e`](https://github.com/Effect-TS/effect-smol/commit/3a30b9e2ec2bd8b8193e1aa139f6878a07e3f5ee)]:
- effect@4.0.0-beta.46
## 4.0.0-beta.45
### Patch Changes
- Updated dependencies [[`5c3af6d`](https://github.com/Effect-TS/effect-smol/commit/5c3af6d554f60be34f8fc21d598d9a298ae11beb)]:
- effect@4.0.0-beta.45
## 4.0.0-beta.44
### Patch Changes
- [#1961](https://github.com/Effect-TS/effect-smol/pull/1961) [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970) Thanks @IMax153! - Rename the `ServiceMap` module to `Context` across exports, docs, and tests.
- Updated dependencies [[`e3f0621`](https://github.com/Effect-TS/effect-smol/commit/e3f0621454c3f5d11070d30619da27c9232cadc1), [`5b476ab`](https://github.com/Effect-TS/effect-smol/commit/5b476abc0bd7e9bb59135ea1bcad2e4936227ced), [`6b40e5a`](https://github.com/Effect-TS/effect-smol/commit/6b40e5a4a6bd2087c15a3d7374d25057fdedfa16), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`3b09fb3`](https://github.com/Effect-TS/effect-smol/commit/3b09fb31c40c2802b01f21c23bcdd1fe7fb0aa82), [`2370410`](https://github.com/Effect-TS/effect-smol/commit/237041062e5af4594d32db91597e34e70a632877), [`dabc272`](https://github.com/Effect-TS/effect-smol/commit/dabc272444a700eb629c07ba3e77671a841ca86e), [`08b63c3`](https://github.com/Effect-TS/effect-smol/commit/08b63c3df11bd35c9fd6090dbd166287fdc40664), [`dfff04c`](https://github.com/Effect-TS/effect-smol/commit/dfff04c4c2b1d352dfad83992a6dce1280c85cf9), [`9baed9e`](https://github.com/Effect-TS/effect-smol/commit/9baed9e17e84702e6e480fcef6f86404f9e24be9), [`7846792`](https://github.com/Effect-TS/effect-smol/commit/7846792adc7e1631d62d26d657bd7ba6139f369b), [`1556a24`](https://github.com/Effect-TS/effect-smol/commit/1556a247623636b7ebe438fb56d77f1a7bf957bb), [`7c11bc2`](https://github.com/Effect-TS/effect-smol/commit/7c11bc292ab8e46252fe8f7576fb685917bfb8b5), [`b5ea591`](https://github.com/Effect-TS/effect-smol/commit/b5ea5913ec1d45d0dd12a327b9dd966bda2f6d02), [`0853afa`](https://github.com/Effect-TS/effect-smol/commit/0853afaeb1633b2d7f8b66893bd01c3aa1ef2c22), [`ac845f3`](https://github.com/Effect-TS/effect-smol/commit/ac845f3ab40e0b8719576e7f9bc16ea2e0e02cd4), [`b80c462`](https://github.com/Effect-TS/effect-smol/commit/b80c46247480f47bb64fc480fab48a3f37bc8888), [`b3f535d`](https://github.com/Effect-TS/effect-smol/commit/b3f535d9a7ac13b5fb984c29f93561c57a081ff0), [`6fe2e93`](https://github.com/Effect-TS/effect-smol/commit/6fe2e93cc2f1b173ef89651d74b6a5d2626b3226), [`cda8004`](https://github.com/Effect-TS/effect-smol/commit/cda800451c1ffbdddfc08415aed7b2d91e0412ee), [`8335477`](https://github.com/Effect-TS/effect-smol/commit/8335477a8a936a24b5f3ee6203c1b268bd1bfc3c), [`8c836f9`](https://github.com/Effect-TS/effect-smol/commit/8c836f99ab1e896b9580a71d67773625baff2eaf), [`718ff6f`](https://github.com/Effect-TS/effect-smol/commit/718ff6fe3e3d3820cefd67d2bff1b2224fe08060), [`7eed84f`](https://github.com/Effect-TS/effect-smol/commit/7eed84fc33c5781a6fb11bf4fd189d424902ebd4), [`5df46fe`](https://github.com/Effect-TS/effect-smol/commit/5df46fe2f654d59ab5fc1578f4fc27fa40368ef9), [`82dd0f2`](https://github.com/Effect-TS/effect-smol/commit/82dd0f26c6442b07143762ef7bc33742d3978dd6), [`03ae41e`](https://github.com/Effect-TS/effect-smol/commit/03ae41e7304cffac9f18feea22b73468feafc43a), [`4677a0a`](https://github.com/Effect-TS/effect-smol/commit/4677a0a58f95eea38a211efcd3f345f237a9e44a), [`87e1fc8`](https://github.com/Effect-TS/effect-smol/commit/87e1fc8b67e4901d75f567b2fecc3841ab762cc4), [`c1af1b7`](https://github.com/Effect-TS/effect-smol/commit/c1af1b756f63291e9c0298cf95c98a6920a0c2a0), [`7bb5dce`](https://github.com/Effect-TS/effect-smol/commit/7bb5dce60e1d904ef049a0287dec2b2e6113c970), [`c8a877b`](https://github.com/Effect-TS/effect-smol/commit/c8a877b53e8f29616335719e5dd1c3992dddf780), [`7da961a`](https://github.com/Effect-TS/effect-smol/commit/7da961ae4916229d2246699a5d3b20e5b2dd2020)]:
- effect@4.0.0-beta.44
## 4.0.0-beta.43
### Patch Changes
- Updated dependencies [[`2ae33d0`](https://github.com/Effect-TS/effect-smol/commit/2ae33d050914915f7cb9c25ab0a020901e08d596), [`979811a`](https://github.com/Effect-TS/effect-smol/commit/979811a4c3f7ed21ed18ef560c49fb7f5569e80e), [`eb7dbef`](https://github.com/Effect-TS/effect-smol/commit/eb7dbeffa883386ad912815e62c0820cac1fdf8e), [`cf50eb4`](https://github.com/Effect-TS/effect-smol/commit/cf50eb49cb04706dae5185f624708117c413dee8), [`1d046fe`](https://github.com/Effect-TS/effect-smol/commit/1d046fe484560e23f3e22cb23eec6433f8f1fa02)]:
- effect@4.0.0-beta.43
## 4.0.0-beta.42
### Patch Changes
- Updated dependencies [[`924e216`](https://github.com/Effect-TS/effect-smol/commit/924e216caa7e0bbf22e994a0cd2ce8b1f0f0b3ee), [`80e7f0c`](https://github.com/Effect-TS/effect-smol/commit/80e7f0cd9116e811e97b0ce30a77a8d1ecd072aa), [`f8328bf`](https://github.com/Effect-TS/effect-smol/commit/f8328bf0314da3dc7f31d314f94a5840e8d5217f), [`66d1c06`](https://github.com/Effect-TS/effect-smol/commit/66d1c06039079129707a230f7ad8c676439d7133), [`bee800b`](https://github.com/Effect-TS/effect-smol/commit/bee800bf285192a01bec72a7b7b51bc1159434e6), [`8930441`](https://github.com/Effect-TS/effect-smol/commit/8930441dee6f94c59c583d18d3ebd677cf1f2623)]:
- effect@4.0.0-beta.42
## 4.0.0-beta.41
### Patch Changes
- [#1871](https://github.com/Effect-TS/effect-smol/pull/1871) [`977386d`](https://github.com/Effect-TS/effect-smol/commit/977386da5e2e8aac2c07e99175673e0b5771191b) Thanks @IMax153! - Fix HTTP Referer header name in the `OpenRouterClient`
- Updated dependencies [[`36f5c21`](https://github.com/Effect-TS/effect-smol/commit/36f5c2174d31ab42c4598bf81f178f40d0802283), [`d8ce758`](https://github.com/Effect-TS/effect-smol/commit/d8ce758669d6297ae932ac3251d83e7b49b22f30), [`11aab4c`](https://github.com/Effect-TS/effect-smol/commit/11aab4c6d37d5691adafc2d33da1a631b28ce814), [`3bc1efb`](https://github.com/Effect-TS/effect-smol/commit/3bc1efb53dd75b4a40de46f1f80c7f8a7d50af86), [`70e724e`](https://github.com/Effect-TS/effect-smol/commit/70e724e604604d4be1061cd8da0d360494998c84), [`738dee7`](https://github.com/Effect-TS/effect-smol/commit/738dee7edfd70af82dc4d2376db3a8ebe603eb48), [`2111963`](https://github.com/Effect-TS/effect-smol/commit/2111963f19b4c28c800664a8fac9590c1321885f), [`198a553`](https://github.com/Effect-TS/effect-smol/commit/198a553d9ce45f6a00bfc4d65ed0640669602d95)]:
- effect@4.0.0-beta.41
## 4.0.0-beta.40
### Patch Changes
- Updated dependencies [[`f62860f`](https://github.com/Effect-TS/effect-smol/commit/f62860f0e5e45978fabf7256ae620a13152a772a), [`973f281`](https://github.com/Effect-TS/effect-smol/commit/973f2812529aadc1cc54598b2039799fa72b80f8)]:
- effect@4.0.0-beta.40
## 4.0.0-beta.39
### Patch Changes
- Updated dependencies [[`f91fd3d`](https://github.com/Effect-TS/effect-smol/commit/f91fd3db39fe5628439fd175fba201a65a1aa9d0), [`edaae9d`](https://github.com/Effect-TS/effect-smol/commit/edaae9d65f464f941d7eddd723cd33d324f4b071), [`b47db0b`](https://github.com/Effect-TS/effect-smol/commit/b47db0bd5802064b6a24b3ea27c6ff2e0520d513), [`82d3c8e`](https://github.com/Effect-TS/effect-smol/commit/82d3c8e4f3f49b00df611b25aa6f8f74ec21b59b), [`7c22b31`](https://github.com/Effect-TS/effect-smol/commit/7c22b315d198dcbf44ae8cdb8b37879e1c9e3996)]:
- effect@4.0.0-beta.39
## 4.0.0-beta.38
### Patch Changes
- Updated dependencies [[`f4dbe5b`](https://github.com/Effect-TS/effect-smol/commit/f4dbe5b26b9c2d33fae024bf44afbdf8541792cd), [`a71a607`](https://github.com/Effect-TS/effect-smol/commit/a71a607c89fb6669a12a562c2c23be81dfbe1adb), [`66a0494`](https://github.com/Effect-TS/effect-smol/commit/66a0494ed75cd12f2721dcbb1d8a072e3d9e14b6), [`5ef7218`](https://github.com/Effect-TS/effect-smol/commit/5ef7218fc559d57301fe929b8a0cab4033f4f1fd), [`472d260`](https://github.com/Effect-TS/effect-smol/commit/472d260655bc311fba5c2c6e23bb77d8f7e36ba0)]:
- effect@4.0.0-beta.38
## 4.0.0-beta.37
### Patch Changes
- Updated dependencies [[`f7a0b71`](https://github.com/Effect-TS/effect-smol/commit/f7a0b711da8fdd645597dee29cacc5619c6afcf2), [`1e223c3`](https://github.com/Effect-TS/effect-smol/commit/1e223c30ccf835dfbb21284535d78549efaeca80), [`53740f4`](https://github.com/Effect-TS/effect-smol/commit/53740f47aa76d114b7d535649fb50efc54a09608), [`8c7cf89`](https://github.com/Effect-TS/effect-smol/commit/8c7cf89f719e580cbce1bf6c24e6996f1992a0a6), [`b6b81a9`](https://github.com/Effect-TS/effect-smol/commit/b6b81a940eaafcbc792d25413d6c02c707de31b2), [`8f4c1f9`](https://github.com/Effect-TS/effect-smol/commit/8f4c1f97ed60f8810b0b327b50117ffb2d8260d4), [`f2479f9`](https://github.com/Effect-TS/effect-smol/commit/f2479f9d3113b1f012db17a3852b4e28f478cf9c), [`c919921`](https://github.com/Effect-TS/effect-smol/commit/c9199217fad65529421d2cf95ecfff41257090fd), [`7af90c2`](https://github.com/Effect-TS/effect-smol/commit/7af90c2e3c99038eafa39650433839523790e2fe), [`f3be185`](https://github.com/Effect-TS/effect-smol/commit/f3be18569e5ca57c25eabf00df3ca601ebab43c7)]:
- effect@4.0.0-beta.37
## 4.0.0-beta.36
### Patch Changes
- Updated dependencies [[`60fcbcc`](https://github.com/Effect-TS/effect-smol/commit/60fcbcc43d09471e8f7e0969955d99dcefc5be81), [`0a60837`](https://github.com/Effect-TS/effect-smol/commit/0a6083713124440e630030375bab367e8d7df24e), [`49164d2`](https://github.com/Effect-TS/effect-smol/commit/49164d2c20a8d21b66514992c4a15d8521f6b36e), [`334b6e4`](https://github.com/Effect-TS/effect-smol/commit/334b6e4f76fe11941b516d61f57e268bc31f0ca6), [`5700695`](https://github.com/Effect-TS/effect-smol/commit/5700695f76ae6da6b94c9c87d4dd2b8054fb829b), [`f8f4456`](https://github.com/Effect-TS/effect-smol/commit/f8f445644f3aa7ec093cab7445198a62ba18a480), [`969d24f`](https://github.com/Effect-TS/effect-smol/commit/969d24fdfa48c4838e811983848d9cb4e9b3b12c), [`851eda0`](https://github.com/Effect-TS/effect-smol/commit/851eda0533946e39bacaaf581896320d7a4f3e8c), [`8059c1c`](https://github.com/Effect-TS/effect-smol/commit/8059c1c3eba9a90af7cd889ea261bcb8fff0c185), [`6f83295`](https://github.com/Effect-TS/effect-smol/commit/6f8329546a73eaddc7cb5e85ea8e37e73fbfb611), [`65f7f57`](https://github.com/Effect-TS/effect-smol/commit/65f7f5737575fed668987462c96d29a446707c32), [`e7fabd2`](https://github.com/Effect-TS/effect-smol/commit/e7fabd2265db690eae5cfc9b83730c84699aef61), [`89c3e98`](https://github.com/Effect-TS/effect-smol/commit/89c3e985401eb38f33a3ae21a94ad27de3c1d28b), [`53794ab`](https://github.com/Effect-TS/effect-smol/commit/53794ab7af30aa5c5004ecf53659fafbe4b10542)]:
- effect@4.0.0-beta.36
## 4.0.0-beta.35
### Patch Changes
- Updated dependencies [[`9252b43`](https://github.com/Effect-TS/effect-smol/commit/9252b43560f507709c2985abcf52a7837b23ddf8), [`7daf387`](https://github.com/Effect-TS/effect-smol/commit/7daf3870a656882a488a60f67881e6808c8f4d04), [`e1664a3`](https://github.com/Effect-TS/effect-smol/commit/e1664a38bc31ef4ceb4e9324c7226e1e99bf9c07), [`fdaa6e0`](https://github.com/Effect-TS/effect-smol/commit/fdaa6e0a41b6b6605438fa8557441792135380a2), [`19aa47e`](https://github.com/Effect-TS/effect-smol/commit/19aa47ef7b470e427620edca8970dd9cdd551216), [`c667dad`](https://github.com/Effect-TS/effect-smol/commit/c667dad07777b860e4764a3ba9a6cc41c236cd98), [`764d150`](https://github.com/Effect-TS/effect-smol/commit/764d1501bc5026b60fc8aef6cb02a5a87c762801), [`3c27098`](https://github.com/Effect-TS/effect-smol/commit/3c27098b5685a63db2c2eff654a250c94d3fcfa7)]:
- effect@4.0.0-beta.35
## 4.0.0-beta.34
### Patch Changes
- Updated dependencies [[`f2f75ee`](https://github.com/Effect-TS/effect-smol/commit/f2f75ee564bce1cd95f5189c7bdeeed4f92dacb1), [`342fc4b`](https://github.com/Effect-TS/effect-smol/commit/342fc4b051739e32e7977159f26ff9541eda664f), [`5d704ee`](https://github.com/Effect-TS/effect-smol/commit/5d704ee10d20e8eb107e34bb8a21feb5aa4a7685), [`00add69`](https://github.com/Effect-TS/effect-smol/commit/00add69b59551e9df34772eb927638b093f6d71e), [`58217d3`](https://github.com/Effect-TS/effect-smol/commit/58217d318a7d716ccd707cce0f41573946939c28), [`f4e2aba`](https://github.com/Effect-TS/effect-smol/commit/f4e2aba01b76d1e3059b297e3cc942284dfeafb2), [`e3b44b6`](https://github.com/Effect-TS/effect-smol/commit/e3b44b6a2af9ee21dc5c1e928f0c20af857fa7a9), [`e1472b7`](https://github.com/Effect-TS/effect-smol/commit/e1472b7525c5d57a48bdec2353c3b742f7f916c0), [`7686320`](https://github.com/Effect-TS/effect-smol/commit/7686320cd123fa352b5c3d076fb18a3cac0a9bba)]:
- effect@4.0.0-beta.34
## 4.0.0-beta.33
### Patch Changes
- Updated dependencies [[`571447d`](https://github.com/Effect-TS/effect-smol/commit/571447da67334449f8ae3d6ecb3d77ea4e0c4295)]:
- effect@4.0.0-beta.33
## 4.0.0-beta.32
### Patch Changes
- Updated dependencies [[`bf8fff8`](https://github.com/Effect-TS/effect-smol/commit/bf8fff8a5f54b6df74cb7bbb42346fe9ba52435a), [`1af3ef3`](https://github.com/Effect-TS/effect-smol/commit/1af3ef3e3ca7fd417d0fc15f8ca8fe207eba4f74), [`27fea0f`](https://github.com/Effect-TS/effect-smol/commit/27fea0f66910de5905f40fd63f8ddbb6f7ac5aba), [`2ad6c1b`](https://github.com/Effect-TS/effect-smol/commit/2ad6c1b2c85a3a0fe351e3d56636a75eb76b4b4e), [`398ac3e`](https://github.com/Effect-TS/effect-smol/commit/398ac3e01cb75efce0e4e2913d1450cf65866732), [`51fe22f`](https://github.com/Effect-TS/effect-smol/commit/51fe22f3266e417b6c541aaed4b75d246fac91e7), [`4605db6`](https://github.com/Effect-TS/effect-smol/commit/4605db69cfacddbdbf1525865ddfde135158090c), [`f4de1b0`](https://github.com/Effect-TS/effect-smol/commit/f4de1b087c998d0bad1d9468f70b7d16c13b9f6f), [`60214f2`](https://github.com/Effect-TS/effect-smol/commit/60214f2080b2aeb091f691140eb20acb741691c3), [`c4b8b0f`](https://github.com/Effect-TS/effect-smol/commit/c4b8b0ffa8efb47c4cd7578a8943d6868509373f), [`6d9393a`](https://github.com/Effect-TS/effect-smol/commit/6d9393a0770a18722d23340e77f15455de341245), [`6de4efe`](https://github.com/Effect-TS/effect-smol/commit/6de4efe463c783614ceb0c094d77a336a899cbe0), [`4f969d1`](https://github.com/Effect-TS/effect-smol/commit/4f969d1563ba755ffa116c8ae409bb3436bd881d), [`6cc67c8`](https://github.com/Effect-TS/effect-smol/commit/6cc67c855e054ee3f3ac3485dca5f7805e79e8fb), [`8531a22`](https://github.com/Effect-TS/effect-smol/commit/8531a22ffbb52e11a030b09f358cafbfdf5edff7), [`b226760`](https://github.com/Effect-TS/effect-smol/commit/b22676067617f15c00722a3a63fd7c2c172c3d45), [`47a51ab`](https://github.com/Effect-TS/effect-smol/commit/47a51aba0ecdf3ef478bfa28a498bca188399bd4), [`1521d02`](https://github.com/Effect-TS/effect-smol/commit/1521d02e1f19f1d795edaaf862c1a1031d9c755e)]:
- effect@4.0.0-beta.32
## 4.0.0-beta.31
### Patch Changes
- Updated dependencies [[`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6f23f0e`](https://github.com/Effect-TS/effect-smol/commit/6f23f0ed4cba573cd9395c2e582f582fe7271544), [`654aaec`](https://github.com/Effect-TS/effect-smol/commit/654aaec593305521b65dd042c204d761cc6e8c28), [`2958a42`](https://github.com/Effect-TS/effect-smol/commit/2958a42078966a8713a98f00485ab36484d5eccf), [`95d27a2`](https://github.com/Effect-TS/effect-smol/commit/95d27a239ed5147302605ab0b3147a056541b0c7), [`0fbaea8`](https://github.com/Effect-TS/effect-smol/commit/0fbaea8f9555a8044cec31a770394db613fc78e2), [`21d5d5e`](https://github.com/Effect-TS/effect-smol/commit/21d5d5e0439fd4d9bb6e508377215b1087555d45), [`5a84853`](https://github.com/Effect-TS/effect-smol/commit/5a8485397b7f321ae021640c1999821143659462), [`6e49959`](https://github.com/Effect-TS/effect-smol/commit/6e499590357a104c81779b3176cd3f84e4f91064), [`8f5805d`](https://github.com/Effect-TS/effect-smol/commit/8f5805dbdd0d1bc0ff0727cc398c8d80e544edee), [`990df2c`](https://github.com/Effect-TS/effect-smol/commit/990df2c3ceeb32e659acc10cc9485617f7b3c423)]:
- effect@4.0.0-beta.31
## 4.0.0-beta.30
### Patch Changes
- Updated dependencies [[`c88e5b7`](https://github.com/Effect-TS/effect-smol/commit/c88e5b723ff09da4edaef6ce14d927ca01104a32), [`947d0e4`](https://github.com/Effect-TS/effect-smol/commit/947d0e4268ba5c4020ead380aa80812c7342408f), [`7517908`](https://github.com/Effect-TS/effect-smol/commit/75179085d159b88a1ab0bce70669d76dcf0d79a4), [`a49ecd5`](https://github.com/Effect-TS/effect-smol/commit/a49ecd5a183d7e7d33f47ff95e9d2dea5a12ead5), [`6993e33`](https://github.com/Effect-TS/effect-smol/commit/6993e3329122c834c20bacea72d8678232f4f103), [`514f2a2`](https://github.com/Effect-TS/effect-smol/commit/514f2a2ae54580fcacdbe2ea2196a83a852d0748), [`3214b47`](https://github.com/Effect-TS/effect-smol/commit/3214b47676de2d33fddc5fecfc2d226e6e83cc7b), [`95ec5ed`](https://github.com/Effect-TS/effect-smol/commit/95ec5ed345de77c893049e182d37a37cf164a268)]:
- effect@4.0.0-beta.30
## 4.0.0-beta.29
### Patch Changes
- Updated dependencies [[`9d93adb`](https://github.com/Effect-TS/effect-smol/commit/9d93adb1c1795d1978391b30d7d2972c88052662), [`b52721c`](https://github.com/Effect-TS/effect-smol/commit/b52721cf0d11a567722b060c8536e3bdd4161f07), [`a891c7b`](https://github.com/Effect-TS/effect-smol/commit/a891c7b12f415b2287613dd4b91a09dfd38ef30d), [`ef26cdf`](https://github.com/Effect-TS/effect-smol/commit/ef26cdfb65d9955fc7e161629191930c2cc2c63f), [`82fd3ed`](https://github.com/Effect-TS/effect-smol/commit/82fd3ed922063ee5a34f96f3993c15c7515e4f67)]:
- effect@4.0.0-beta.29
## 4.0.0-beta.28
### Patch Changes
- Updated dependencies [[`ff533f2`](https://github.com/Effect-TS/effect-smol/commit/ff533f203cd06302ad08032a27e01269b4a2d4c6), [`dc803ee`](https://github.com/Effect-TS/effect-smol/commit/dc803ee52ebd3e9f931118f0dfcb804542847556), [`d660b1c`](https://github.com/Effect-TS/effect-smol/commit/d660b1c99cb93d4f79715e91c7a4486801c0eefa), [`93a05e3`](https://github.com/Effect-TS/effect-smol/commit/93a05e3eaa624058b162aedd66aad70102837270), [`2a65cf6`](https://github.com/Effect-TS/effect-smol/commit/2a65cf6fd81ef63d944e6fb51f058d439bf4a834), [`a561a40`](https://github.com/Effect-TS/effect-smol/commit/a561a40cc41c548c2cf3153aca065ee92ee8aa57), [`29cd24d`](https://github.com/Effect-TS/effect-smol/commit/29cd24d1fe78480a72eeb38a90281ffddc0530bc), [`662a8e6`](https://github.com/Effect-TS/effect-smol/commit/662a8e6857dac64a7cd13bd8df4b0674654622f8), [`d2b52ba`](https://github.com/Effect-TS/effect-smol/commit/d2b52bae5b9336cf59729fbdcc4d7f09512b0cbf), [`407c3b4`](https://github.com/Effect-TS/effect-smol/commit/407c3b43a5d1414558e0e33b6f1fc0e6a6d489cc), [`42bc7ce`](https://github.com/Effect-TS/effect-smol/commit/42bc7ce5480f6f2953c39f8cb5c850d61df6f5a2), [`e741322`](https://github.com/Effect-TS/effect-smol/commit/e74132226cbfee24234311c7c1c13e6b7391384e), [`5c75fa8`](https://github.com/Effect-TS/effect-smol/commit/5c75fa8fb71163bc4c035ba1a215574dfd4badfc), [`747177b`](https://github.com/Effect-TS/effect-smol/commit/747177b0602f12d4461a843e953dfdffbeb0a429), [`326cd48`](https://github.com/Effect-TS/effect-smol/commit/326cd4828bce573fe985f35152155464bf4c5a70), [`627e922`](https://github.com/Effect-TS/effect-smol/commit/627e922b8d1e9521eae5e1caa5d667ad00b1619a), [`662287e`](https://github.com/Effect-TS/effect-smol/commit/662287e9abc76c941ccc2ee330aa07904d571341)]:
- effect@4.0.0-beta.28
## 4.0.0-beta.27
### Patch Changes
- Updated dependencies [[`903a839`](https://github.com/Effect-TS/effect-smol/commit/903a839e94239e6ec4568315af28e405bcad95f4), [`91a0168`](https://github.com/Effect-TS/effect-smol/commit/91a016836680a6669308ecf464d3584bcc4ae1b7), [`c890f9a`](https://github.com/Effect-TS/effect-smol/commit/c890f9a1b3a989ed22528bd5a43326342e05b142), [`1e985f2`](https://github.com/Effect-TS/effect-smol/commit/1e985f237d250b51b91de22dde77160c1e778ce7)]:
- effect@4.0.0-beta.27
## 4.0.0-beta.26
### Patch Changes
- Updated dependencies [[`fb21462`](https://github.com/Effect-TS/effect-smol/commit/fb21462642cdd5b1bada92f3eba18ae20445be42), [`2ed26b1`](https://github.com/Effect-TS/effect-smol/commit/2ed26b139805700e3df39efaa768ff01565e5c86), [`e832a57`](https://github.com/Effect-TS/effect-smol/commit/e832a57b570fe38f010c1fd99bceac5a325a9e07), [`7f01be7`](https://github.com/Effect-TS/effect-smol/commit/7f01be7f8db363d4b2e88e6b5571e96bb815786f), [`e965143`](https://github.com/Effect-TS/effect-smol/commit/e9651431e114479e6becf8ca7b1ed99ac7e91ccc), [`b9b80f1`](https://github.com/Effect-TS/effect-smol/commit/b9b80f1f15e152ceef0a727d150b7dc230abae99), [`98252aa`](https://github.com/Effect-TS/effect-smol/commit/98252aa0c0b17fc73fbdad65d0a1104965f9fc0f), [`56fbd94`](https://github.com/Effect-TS/effect-smol/commit/56fbd94311ad19a05001ad649d9e34ab00c74541), [`3faa109`](https://github.com/Effect-TS/effect-smol/commit/3faa109b7d093fbf14ad410d3e11d663f16e28f1), [`692ecfe`](https://github.com/Effect-TS/effect-smol/commit/692ecfed99fe58056b7a5afe001f4fcd1a61c446), [`1e70b72`](https://github.com/Effect-TS/effect-smol/commit/1e70b72d0b210474d0e96a15a5cfc279eae37e0c), [`ecf0782`](https://github.com/Effect-TS/effect-smol/commit/ecf07829ef2dfc01d8943c96c4fe9c1b44b97926)]:
- effect@4.0.0-beta.26
## 4.0.0-beta.25
### Patch Changes
- Updated dependencies [[`fa17bb5`](https://github.com/Effect-TS/effect-smol/commit/fa17bb5be9f2533d01e11322b14804c7dec43714), [`f46e5b5`](https://github.com/Effect-TS/effect-smol/commit/f46e5b5ca2a918ee4d9270167e79db223077c96f), [`ce4767c`](https://github.com/Effect-TS/effect-smol/commit/ce4767cadcacc6ce8ff4c3a0d0fbc82ede655f63), [`c830a8b`](https://github.com/Effect-TS/effect-smol/commit/c830a8b6c292a6528d7f9318759d34800b00372d)]:
- effect@4.0.0-beta.25
## 4.0.0-beta.24
### Patch Changes
- Updated dependencies [[`a909e1c`](https://github.com/Effect-TS/effect-smol/commit/a909e1c1ac2bc707527f5073776e3e7d239688d9), [`8814a4e`](https://github.com/Effect-TS/effect-smol/commit/8814a4ef78d67144d27689370af10099ea210399), [`3f942c5`](https://github.com/Effect-TS/effect-smol/commit/3f942c51cefa7b2ffa7c49e8c8a2c887570ba4c0), [`774ed59`](https://github.com/Effect-TS/effect-smol/commit/774ed59c52b2ab578bbb897c4f551f812231e1d2), [`f54b8d3`](https://github.com/Effect-TS/effect-smol/commit/f54b8d398fedad1815fd1f4c49814ab938cfc385)]:
- effect@4.0.0-beta.24
## 4.0.0-beta.23
### Patch Changes
- Updated dependencies [[`5c73c41`](https://github.com/Effect-TS/effect-smol/commit/5c73c41b69eaeab80fcd62c9bfda490b446d1966)]:
- effect@4.0.0-beta.23
## 4.0.0-beta.22
### Patch Changes
- Updated dependencies [[`0874332`](https://github.com/Effect-TS/effect-smol/commit/0874332f7c81118b06ac2eb105e0710211631479), [`c592dcd`](https://github.com/Effect-TS/effect-smol/commit/c592dcde0697e322065c8f418c0480ef910cb183), [`1dbe28d`](https://github.com/Effect-TS/effect-smol/commit/1dbe28dac8299cd3e218c9768450cfd173b5e294), [`564d730`](https://github.com/Effect-TS/effect-smol/commit/564d730b6bbf38dd8548a3b046e7a693b28699a4), [`3cfadc4`](https://github.com/Effect-TS/effect-smol/commit/3cfadc458b070c6cba6c5674b72a059f1e49118b), [`6634fd0`](https://github.com/Effect-TS/effect-smol/commit/6634fd07da067d80b8261fb2959d1a952b9e412e), [`d10dabe`](https://github.com/Effect-TS/effect-smol/commit/d10dabeb7af9a368f995829cd36ad08167cd8f95), [`f82f549`](https://github.com/Effect-TS/effect-smol/commit/f82f549a09e950e9d4987f279a800f4d953f0939), [`78a3382`](https://github.com/Effect-TS/effect-smol/commit/78a3382ddfbe034408f7480fa794733d9e82147b)]:
- effect@4.0.0-beta.22
## 4.0.0-beta.21
### Patch Changes
- Updated dependencies [[`e691909`](https://github.com/Effect-TS/effect-smol/commit/e691909495ccb162ea7bfa351dd74632b99997cb), [`d5f413f`](https://github.com/Effect-TS/effect-smol/commit/d5f413f3c8fc57f2413cc5649c2003d6d4e5a6d7), [`139d152`](https://github.com/Effect-TS/effect-smol/commit/139d152941e562a073b5be12e8d66c8a4d4a8a57), [`947e3d4`](https://github.com/Effect-TS/effect-smol/commit/947e3d436ab8a017efda9b29be523efd1ca8df28), [`84b2cce`](https://github.com/Effect-TS/effect-smol/commit/84b2ccefe2aa3a7413b86738a4dc33cdb311ca55), [`7f5305e`](https://github.com/Effect-TS/effect-smol/commit/7f5305e69f5a33309e77b08a576edb25d7daaee2), [`9e6fd84`](https://github.com/Effect-TS/effect-smol/commit/9e6fd8471c93a3c643929151a3bdb62cb9c0ca0e), [`fdb8a4b`](https://github.com/Effect-TS/effect-smol/commit/fdb8a4b172721fbefe98bd5aa6fe4f0efd1da3eb), [`0f986ef`](https://github.com/Effect-TS/effect-smol/commit/0f986ef22f196fe091a7afdbd179485a7d888882), [`9355fc0`](https://github.com/Effect-TS/effect-smol/commit/9355fc0ffb5b7382146a5aed9eea83974b10d007)]:
- effect@4.0.0-beta.21
## 4.0.0-beta.20
### Patch Changes
- [#1529](https://github.com/Effect-TS/effect-smol/pull/1529) [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8) Thanks @tim-smart! - Add dedicated AiError metadata interfaces per reason so provider packages can safely augment metadata without conflicting module declarations.
- [#1528](https://github.com/Effect-TS/effect-smol/pull/1528) [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34) Thanks @tim-smart! - Add `Model.ModelName` and provide it from AI model constructors.
- Updated dependencies [[`842a624`](https://github.com/Effect-TS/effect-smol/commit/842a624f79d5e1407460b0ef3ab27d14d48ccf74), [`4785eef`](https://github.com/Effect-TS/effect-smol/commit/4785eef5d7cf1edb96ef2509aed2ba4d1edf3862), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`12ee8e2`](https://github.com/Effect-TS/effect-smol/commit/12ee8e27df7eb393d83a5e403390d0cfc82ca732), [`e542c94`](https://github.com/Effect-TS/effect-smol/commit/e542c942bee4729138b02222f4421220a90a57d8), [`8fac95b`](https://github.com/Effect-TS/effect-smol/commit/8fac95bd9e0338b7a82da8da579c1ac22afa045c), [`6f4ebd1`](https://github.com/Effect-TS/effect-smol/commit/6f4ebd193c2595983394127dd808601b75430d34), [`989d1cc`](https://github.com/Effect-TS/effect-smol/commit/989d1cca936fce0cc459057825ba40e3f5ef3827)]:
- effect@4.0.0-beta.20
## 4.0.0-beta.19
### Patch Changes
- Updated dependencies []:
- effect@4.0.0-beta.19
## 4.0.0-beta.18
### Patch Changes
- Updated dependencies [[`01e31fd`](https://github.com/Effect-TS/effect-smol/commit/01e31fdf8e5206849d23cbafd23a346f2f177ab8), [`0890aab`](https://github.com/Effect-TS/effect-smol/commit/0890aab15ed9c5ba52c383a72fdc6a444d7504d5), [`725260b`](https://github.com/Effect-TS/effect-smol/commit/725260b53f5142d6af7a93a2f9f464f974eda92d)]:
- effect@4.0.0-beta.18
## 4.0.0-beta.17
### Patch Changes
- Updated dependencies [[`8f59c32`](https://github.com/Effect-TS/effect-smol/commit/8f59c32922597a48392744f7203e284866747781)]:
- effect@4.0.0-beta.17
## 4.0.0-beta.16
### Patch Changes
- Updated dependencies [[`bf9096c`](https://github.com/Effect-TS/effect-smol/commit/bf9096c52a7d8791d93d232739e523eb84f6625a), [`29f81ca`](https://github.com/Effect-TS/effect-smol/commit/29f81ca07c67dba265804b140a7487fb15a5fc6b), [`68eb28c`](https://github.com/Effect-TS/effect-smol/commit/68eb28c2b0fc67a9f6204ade9bd16c5b37803bfb)]:
- effect@4.0.0-beta.16
## 4.0.0-beta.15
### Patch Changes
- [#1502](https://github.com/Effect-TS/effect-smol/pull/1502) [`285b7e6`](https://github.com/Effect-TS/effect-smol/commit/285b7e667167566d5788367d5155b19c79f1bf22) Thanks @tim-smart! - allow undefined for ai config
- Updated dependencies [[`24ae609`](https://github.com/Effect-TS/effect-smol/commit/24ae60995d2fd7d621be356cdfdfd328c79639ba), [`0e3c059`](https://github.com/Effect-TS/effect-smol/commit/0e3c059987caa55ebd0c134f7c7b147c639c328e), [`e843b0a`](https://github.com/Effect-TS/effect-smol/commit/e843b0a7d7e7b600a0b3bd477f24e2e4cd26bc8b), [`f4389a2`](https://github.com/Effect-TS/effect-smol/commit/f4389a2cca3c5bbf00d69779f52ce41255f15a28), [`5b73de0`](https://github.com/Effect-TS/effect-smol/commit/5b73de095b3402d0c5c74092ace6ce18ebfad566), [`595d2d6`](https://github.com/Effect-TS/effect-smol/commit/595d2d6e7d50419f3532bd39266191532ace38f2)]:
- effect@4.0.0-beta.15
## 4.0.0-beta.14
### Patch Changes
- Updated dependencies [[`c414700`](https://github.com/Effect-TS/effect-smol/commit/c414700ef1932e4b67d0102856de417336912350), [`a30c969`](https://github.com/Effect-TS/effect-smol/commit/a30c9699c0d736cf3952041e45d508b7d58907a9)]:
- effect@4.0.0-beta.14
## 4.0.0-beta.13
### Patch Changes
- Updated dependencies [[`368f4c3`](https://github.com/Effect-TS/effect-smol/commit/368f4c363dd117e6f5a19ad77b161176cfd29fdd), [`db8a579`](https://github.com/Effect-TS/effect-smol/commit/db8a579e93e93ff73b1e60712732e03b597b916b), [`668b703`](https://github.com/Effect-TS/effect-smol/commit/668b70337e9ddbb0d1ae2282a95c282ce404e562), [`d40e76b`](https://github.com/Effect-TS/effect-smol/commit/d40e76b973543979e60e04a6baca04a8c65bdfc2), [`6e18cf8`](https://github.com/Effect-TS/effect-smol/commit/6e18cf883e9905ca718a6697b6a2a4bbd42739aa), [`86062e8`](https://github.com/Effect-TS/effect-smol/commit/86062e8a0c61bca5412fc40d2cf151d676901f08), [`c27ce75`](https://github.com/Effect-TS/effect-smol/commit/c27ce75d34c74dcfc6dba1bf77f1ce88f410a0de), [`e2d4fbf`](https://github.com/Effect-TS/effect-smol/commit/e2d4fbfeeda6a5d2a4c5aeb0501d8240c248b9eb), [`114ab42`](https://github.com/Effect-TS/effect-smol/commit/114ab42ad0edc590d29169675a493e0e915aa58f), [`484caec`](https://github.com/Effect-TS/effect-smol/commit/484caec47cccac8b86db2910742e406dfc7173ab)]:
- effect@4.0.0-beta.13
## 4.0.0-beta.12
### Patch Changes
- Updated dependencies [[`70a74e8`](https://github.com/Effect-TS/effect-smol/commit/70a74e88a8767c9d4acdb9e5f25aec9a33588d07), [`b5b6e10`](https://github.com/Effect-TS/effect-smol/commit/b5b6e10621d54bf8c9857fec0d647ced78ecd857), [`f5ce5a9`](https://github.com/Effect-TS/effect-smol/commit/f5ce5a915359c6ebf254079e1da23cab6cde34fb), [`a29eb70`](https://github.com/Effect-TS/effect-smol/commit/a29eb702ffe3fc58bd28c4d7857298cd65d73668), [`c7b36e5`](https://github.com/Effect-TS/effect-smol/commit/c7b36e541a23e9a00f64e25b23851e51a37dfce5), [`9381d6d`](https://github.com/Effect-TS/effect-smol/commit/9381d6d4d9d819a81a46e56d0364c76e92a4fbca), [`88439f1`](https://github.com/Effect-TS/effect-smol/commit/88439f13ca13549f3e4822c48c4f019c14fc2bcc), [`e35307d`](https://github.com/Effect-TS/effect-smol/commit/e35307dbeb8eb26a9923f958b894a8eaaf259bf2), [`c7df4bc`](https://github.com/Effect-TS/effect-smol/commit/c7df4bce34009474c63d62a807abfdafb76971eb), [`accaf3b`](https://github.com/Effect-TS/effect-smol/commit/accaf3be7ac8da36e2334c509c23b8c9e88ea160), [`3e1c270`](https://github.com/Effect-TS/effect-smol/commit/3e1c2707bbdf67720af1509642b8ced195790882), [`6cd81f7`](https://github.com/Effect-TS/effect-smol/commit/6cd81f73baad86f5bbfa455a55d75cde71e9611a), [`f222da3`](https://github.com/Effect-TS/effect-smol/commit/f222da3cdb44554f3324c2c52d0d005ee575053e), [`61f901d`](https://github.com/Effect-TS/effect-smol/commit/61f901d830005b66e22d1de889fda132aeea97cd)]:
- effect@4.0.0-beta.12
## 4.0.0-beta.11
### Patch Changes
- Updated dependencies [[`88659ed`](https://github.com/Effect-TS/effect-smol/commit/88659edb26e3623d557dccfe914c2c949672da16), [`f2915e8`](https://github.com/Effect-TS/effect-smol/commit/f2915e8e2efe80d50c281e53f297b9701d6dc199), [`eb71ace`](https://github.com/Effect-TS/effect-smol/commit/eb71acebbe0f228e4920278013beee3b67d62310), [`2a16999`](https://github.com/Effect-TS/effect-smol/commit/2a169996c7513d377ac47adbfd68e1490457135c), [`d42dd52`](https://github.com/Effect-TS/effect-smol/commit/d42dd52f11203f8e749fb5d3ecf7153e4a5a6814), [`339adaf`](https://github.com/Effect-TS/effect-smol/commit/339adaf850a62a892adebcb208c2d9dddf3b97b3), [`de19645`](https://github.com/Effect-TS/effect-smol/commit/de1964526d01102dd1cb99c8cfdd3e8df1f49ef1), [`9b1dc3b`](https://github.com/Effect-TS/effect-smol/commit/9b1dc3bcf2a1b68d0a67e3465db5ad01a1a56997), [`e4cb2f5`](https://github.com/Effect-TS/effect-smol/commit/e4cb2f55b30f4771ec1bf613ced36d6d96464dd5), [`8bced95`](https://github.com/Effect-TS/effect-smol/commit/8bced954ecb35d4489197a57b0efe927e7d75f49), [`9431420`](https://github.com/Effect-TS/effect-smol/commit/94314207c8019918200fbcb97aec992219f801f0), [`948dca2`](https://github.com/Effect-TS/effect-smol/commit/948dca22e4f672ba7a6db57f9899272bec7c08b8), [`d18e327`](https://github.com/Effect-TS/effect-smol/commit/d18e32765a2665e31ffb31e746bf983fcfac34c5), [`ab512f7`](https://github.com/Effect-TS/effect-smol/commit/ab512f7be1c0e6b359da921e22cd4944e4c57d3e)]:
- effect@4.0.0-beta.11
## 4.0.0-beta.10
### Patch Changes
- Updated dependencies [[`371acab`](https://github.com/Effect-TS/effect-smol/commit/371acabb58d56f3a7a5e3e33d3d5fdc9f5573c74), [`856d774`](https://github.com/Effect-TS/effect-smol/commit/856d7741f1e296dd5048c6ff2b44b95d023e6ae4), [`b9e9202`](https://github.com/Effect-TS/effect-smol/commit/b9e92023c38caa322975d77cfe83e2d34ac9305a), [`1d1a974`](https://github.com/Effect-TS/effect-smol/commit/1d1a974bd280c81bff5d4505491cda03ba7a3f36), [`6bfe2a6`](https://github.com/Effect-TS/effect-smol/commit/6bfe2a659bc6335db75709931f405da45301cba2), [`b12c811`](https://github.com/Effect-TS/effect-smol/commit/b12c81157be287b1649c210616a244b50ec094d2), [`d17d98a`](https://github.com/Effect-TS/effect-smol/commit/d17d98ad78e2b44d95ef434adab79ac3c35e75ab), [`68c3c7c`](https://github.com/Effect-TS/effect-smol/commit/68c3c7cb1e06ed94fa5c4c123a234b4ccbfdecd8)]:
- effect@4.0.0-beta.10
## 4.0.0-beta.9
### Patch Changes
- Updated dependencies [[`3386557`](https://github.com/Effect-TS/effect-smol/commit/338655731564a7be9f8859dedbf4d5bcac6eb350), [`b6666e3`](https://github.com/Effect-TS/effect-smol/commit/b6666e3cf6bd44ba1a8704e65c256c30359cb422)]:
- effect@4.0.0-beta.9
## 4.0.0-beta.8
### Patch Changes
- [#1354](https://github.com/Effect-TS/effect-smol/pull/1354) [`b94962c`](https://github.com/Effect-TS/effect-smol/commit/b94962c249d46cf96cdf2e41188dc9feda41536a) Thanks @IMax153! - Fix the generated schemas for ai providers
- Updated dependencies [[`246e672`](https://github.com/Effect-TS/effect-smol/commit/246e672dbbd7848d60e0c78fd66671b2f10b3752), [`807dec0`](https://github.com/Effect-TS/effect-smol/commit/807dec03801b4c58a6d00c237b6d98d6386911df)]:
- effect@4.0.0-beta.8
## 4.0.0-beta.7
### Patch Changes
- Updated dependencies [[`a2bda6d`](https://github.com/Effect-TS/effect-smol/commit/a2bda6d4ef6de9d9b0c53ae2df5434f778d6161a), [`1f95a2b`](https://github.com/Effect-TS/effect-smol/commit/1f95a2b5aa9524bb38f4437f4691a664bf463ca1), [`a8d5e79`](https://github.com/Effect-TS/effect-smol/commit/a8d5e792fec201a83af0eb92fc79928d055125fd), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`a5386ba`](https://github.com/Effect-TS/effect-smol/commit/a5386ba67005dff697d45a45398f398773f58dcf), [`06d8a03`](https://github.com/Effect-TS/effect-smol/commit/06d8a0391631e6130e3ab25227e59817852e227f), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430), [`f9e883e`](https://github.com/Effect-TS/effect-smol/commit/f9e883e266fbda870336ee62f46b7ac85ba3de6e), [`8caac76`](https://github.com/Effect-TS/effect-smol/commit/8caac76a35821edfe03c75dab5eb056e8fc05430)]:
- effect@4.0.0-beta.7
## 4.0.0-beta.6
### Patch Changes
- Updated dependencies [[`3247da2`](https://github.com/Effect-TS/effect-smol/commit/3247da28331f345f68be5dbd2974a7e03d300fe1), [`f205705`](https://github.com/Effect-TS/effect-smol/commit/f2057050dbd034b8c186be2d40c3d03ee63a5a3b), [`f35022c`](https://github.com/Effect-TS/effect-smol/commit/f35022c212e4111527e1bb43f360a67b2b49fa85), [`8622721`](https://github.com/Effect-TS/effect-smol/commit/86227217b02d43680a3c6f3c21731b1d852c91f5), [`fc660ab`](https://github.com/Effect-TS/effect-smol/commit/fc660ab8b5ebae38b8d6b96cbf2f9b880cc09253), [`f37dc33`](https://github.com/Effect-TS/effect-smol/commit/f37dc335f64622fa9ce8d6d1d5dd8fc3f260257b), [`3662f32`](https://github.com/Effect-TS/effect-smol/commit/3662f328fcfa3b2fa01ffa79da40e12e93fcede8), [`a7d436f`](https://github.com/Effect-TS/effect-smol/commit/a7d436f438dcd7f49b9485e4e95a4511f31fad7d), [`6856a41`](https://github.com/Effect-TS/effect-smol/commit/6856a415d7eddd9d73d60919e976f1d071421be4), [`8c417d0`](https://github.com/Effect-TS/effect-smol/commit/8c417d03475e5e12d00dca0c4781d0af7e66b86c), [`5419570`](https://github.com/Effect-TS/effect-smol/commit/5419570ba47ce882a3a10882707b46f66e464906), [`449c5ed`](https://github.com/Effect-TS/effect-smol/commit/449c5ed5318e8a874e730420bcf52918fa2ec80f), [`4b5ec12`](https://github.com/Effect-TS/effect-smol/commit/4b5ec12f87f95f2a3cd8fe4d5b26c6eb0529381a), [`df87937`](https://github.com/Effect-TS/effect-smol/commit/df879375fc3b169c43f9c434b3775e12b80dffe4), [`5dbfca8`](https://github.com/Effect-TS/effect-smol/commit/5dbfca8d1dbb6d18d1605d4f8562e99c86e2ff11), [`e629497`](https://github.com/Effect-TS/effect-smol/commit/e6294973d55597ab6b6deca6babbe1e946b2c91d), [`981c991`](https://github.com/Effect-TS/effect-smol/commit/981c991cd78db34def815d5754379d737157f005), [`1ca2ed6`](https://github.com/Effect-TS/effect-smol/commit/1ca2ed67301a5dc40ae0ed94346b99f26fd22bbe), [`45722bd`](https://github.com/Effect-TS/effect-smol/commit/45722bde974458311f11ad237711363a10ec6894), [`eb2a85e`](https://github.com/Effect-TS/effect-smol/commit/eb2a85ed4dc162b2535d304799333a5a20477fd0)]:
- effect@4.0.0-beta.6
## 4.0.0-beta.5
### Patch Changes
- Updated dependencies [[`f6e133e`](https://github.com/Effect-TS/effect-smol/commit/f6e133e9a16b32317bd09ff08c12b97a0ae44600), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8), [`a88e206`](https://github.com/Effect-TS/effect-smol/commit/a88e206e44dc66ca5a2b45bedc797877c5dbb083), [`e3893cc`](https://github.com/Effect-TS/effect-smol/commit/e3893ccf2632338c7d8e745f639dcd825a9d42f8)]:
- effect@4.0.0-beta.5
## 4.0.0-beta.4
### Patch Changes
- Updated dependencies [[`c5a18ef`](https://github.com/Effect-TS/effect-smol/commit/c5a18ef44171e3880bf983faee74529908974b32), [`bc6b885`](https://github.com/Effect-TS/effect-smol/commit/bc6b885b94d887a200657c0775dfa874dc15bc0c)]:
- effect@4.0.0-beta.4
## 4.0.0-beta.3
### Patch Changes
- [#1306](https://github.com/Effect-TS/effect-smol/pull/1306) [`c9fb5a5`](https://github.com/Effect-TS/effect-smol/commit/c9fb5a5bfb1c331c91d592323f4027b72a3bc0b4) Thanks @Leka74! - Fix sparse array crash in `streamText` tool call handling.
- Updated dependencies [[`3a0cf36`](https://github.com/Effect-TS/effect-smol/commit/3a0cf36eff106ba48d74e133c1598cd40613e530), [`c4da328`](https://github.com/Effect-TS/effect-smol/commit/c4da328d32fad1d61e0e538f5d371edf61521d7e)]:
- effect@4.0.0-beta.3
## 4.0.0-beta.2
### Patch Changes
- Updated dependencies [[`a22ce73`](https://github.com/Effect-TS/effect-smol/commit/a22ce73b2bd9305b7ba665694d2255c0e6d5a8d0), [`ebdabf7`](https://github.com/Effect-TS/effect-smol/commit/ebdabf79ff4e62c8384aa8cf9a8d2787d536ee78), [`8f663bb`](https://github.com/Effect-TS/effect-smol/commit/8f663bb121021bf12bd264e8ae385187cb7a5dae)]:
- effect@4.0.0-beta.2
## 4.0.0-beta.1
### Patch Changes
- Updated dependencies [[`0fecf70`](https://github.com/Effect-TS/effect-smol/commit/0fecf70048057623eed7c584a06671773a2b1743), [`709569e`](https://github.com/Effect-TS/effect-smol/commit/709569ed76bead9ebb0670599e4d890a07ca5a43)]:
- effect@4.0.0-beta.1
## 4.0.0-beta.0
### Major Changes
- [#1183](https://github.com/Effect-TS/effect-smol/pull/1183) [`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66) Thanks @tim-smart! - v4 beta
### Patch Changes
- Updated dependencies [[`be642ab`](https://github.com/Effect-TS/effect-smol/commit/be642ab1b3b4cd49e53c9732d7aba1b367fddd66)]:
- effect@4.0.0-beta.0

View File

@@ -0,0 +1,35 @@
# yaml-language-server: $schema=../../tools/ai-codegen/codegen.schema.json
spec: https://openrouter.ai/openapi.yaml
output: src/Generated.ts
name: OpenRouterClient
header: |
/**
* @since 1.0.0
*/
excludeAnnotations:
- default
disableAdditionalProperties: true
patches:
# Replace OpenResponsesStreamEvent with a flat oneOf of $refs.
# The original uses allOf wrappers that inline OpenResponsesNonStreamingResponse
# into each variant, producing lines over 1M chars that crash dprint.
- '[{"op":"replace","path":"/components/schemas/OpenResponsesStreamEvent","value":{"oneOf":[{"$ref":"#/components/schemas/OpenResponsesCreatedEvent"},{"$ref":"#/components/schemas/OpenResponsesInProgressEvent"},{"$ref":"#/components/schemas/OpenResponsesCompletedEvent"},{"$ref":"#/components/schemas/OpenResponsesIncompleteEvent"},{"$ref":"#/components/schemas/OpenResponsesFailedEvent"},{"$ref":"#/components/schemas/OpenResponsesErrorEvent"},{"$ref":"#/components/schemas/OpenResponsesOutputItemAddedEvent"},{"$ref":"#/components/schemas/OpenResponsesOutputItemDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesContentPartAddedEvent"},{"$ref":"#/components/schemas/OpenResponsesContentPartDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesTextDeltaEvent"},{"$ref":"#/components/schemas/OpenResponsesTextDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesRefusalDeltaEvent"},{"$ref":"#/components/schemas/OpenResponsesRefusalDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesOutputTextAnnotationAddedEvent"},{"$ref":"#/components/schemas/OpenResponsesFunctionCallArgumentsDeltaEvent"},{"$ref":"#/components/schemas/OpenResponsesFunctionCallArgumentsDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningDeltaEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningSummaryPartAddedEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningSummaryPartDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningSummaryTextDeltaEvent"},{"$ref":"#/components/schemas/OpenResponsesReasoningSummaryTextDoneEvent"},{"$ref":"#/components/schemas/OpenResponsesImageGenCallInProgress"},{"$ref":"#/components/schemas/OpenResponsesImageGenCallGenerating"},{"$ref":"#/components/schemas/OpenResponsesImageGenCallPartialImage"},{"$ref":"#/components/schemas/OpenResponsesImageGenCallCompleted"}]}}]'
# Fix AssistantMessage images to include type:"image_url" discriminator and nullable
- '[{"op":"replace","path":"/components/schemas/AssistantMessage/properties/images","value":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","const":"image_url"},"image_url":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}},"required":["type","image_url"]}},{"type":"null"}]}}]'
# Add images to ChatStreamingMessageChunk (streaming delta)
- '[{"op":"add","path":"/components/schemas/ChatStreamingMessageChunk/properties/images","value":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","const":"image_url"},"image_url":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}},"required":["type","image_url"]}},{"type":"null"}]}}]'
# Add annotations to AssistantMessage (non-streaming)
- '[{"op":"add","path":"/components/schemas/AssistantMessage/properties/annotations","value":{"anyOf":[{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"url_citation"},"url_citation":{"type":"object","properties":{"url":{"type":"string"},"title":{"type":"string"},"start_index":{"type":"number"},"end_index":{"type":"number"},"content":{"type":"string"}},"required":["url"]}},"required":["type","url_citation"]},{"type":"object","properties":{"type":{"type":"string","const":"file_annotation"},"file_annotation":{"type":"object","properties":{"file_id":{"type":"string"},"quote":{"type":"string"}},"required":["file_id"]}},"required":["type","file_annotation"]},{"type":"object","properties":{"type":{"type":"string","const":"file"},"file":{"type":"object","properties":{"hash":{"type":"string"},"name":{"type":"string"},"content":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"text":{"type":"string"}},"required":["type"]}}},"required":["hash","name"]}},"required":["type","file"]}]}},{"type":"null"}]}}]'
# Add annotations to ChatStreamingMessageChunk (streaming delta)
- '[{"op":"add","path":"/components/schemas/ChatStreamingMessageChunk/properties/annotations","value":{"anyOf":[{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"url_citation"},"url_citation":{"type":"object","properties":{"url":{"type":"string"},"title":{"type":"string"},"start_index":{"type":"number"},"end_index":{"type":"number"},"content":{"type":"string"}},"required":["url"]}},"required":["type","url_citation"]},{"type":"object","properties":{"type":{"type":"string","const":"file_annotation"},"file_annotation":{"type":"object","properties":{"file_id":{"type":"string"},"quote":{"type":"string"}},"required":["file_id"]}},"required":["type","file_annotation"]},{"type":"object","properties":{"type":{"type":"string","const":"file"},"file":{"type":"object","properties":{"hash":{"type":"string"},"name":{"type":"string"},"content":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"text":{"type":"string"}},"required":["type"]}}},"required":["hash","name"]}},"required":["type","file"]}]}},{"type":"null"}]}}]'
# Make tool call delta fields nullable (models like kimi-k2.5, minimax-m2.5 send null)
- '[{"op":"replace","path":"/components/schemas/ChatStreamingMessageToolCall/properties/id","value":{"anyOf":[{"type":"string"},{"type":"null"}]}},{"op":"replace","path":"/components/schemas/ChatStreamingMessageToolCall/properties/type","value":{"anyOf":[{"type":"string","const":"function"},{"type":"null"}]}},{"op":"replace","path":"/components/schemas/ChatStreamingMessageToolCall/properties/function/properties/name","value":{"anyOf":[{"type":"string"},{"type":"null"}]}}]'
# Make finish_reason optional (only present on final streaming chunk)
- '[{"op":"remove","path":"/components/schemas/ChatStreamingChoice/required/1"}]'
replacements:
# Schema.Unknown doesn't work with Schema.toCodecJson (used by HttpClientResponse.schemaBodyJson)
# Replace with Schema.Json which properly handles arbitrary JSON values
- from: "Schema.Record(Schema.String, Schema.Unknown)"
to: "Schema.Record(Schema.String, Schema.Json)"
- from: "{ readonly [x: string]: unknown }"
to: "{ readonly [x: string]: Schema.Json }"

View File

@@ -0,0 +1,23 @@
{
"$schema": "../../node_modules/@effect/docgen/schema.json",
"srcLink": "https://github.com/Effect-TS/effect/tree/main/packages/ai/openrouter/src/",
"exclude": ["src/Generated.ts", "src/internal/**/*.ts"],
"examplesCompilerOptions": {
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "Bundler",
"module": "ES2022",
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"rewriteRelativeImportExtensions": true,
"allowImportingTsExtensions": true,
"paths": {
"effect": ["../../../effect/src/index.js"],
"effect/*": ["../../../effect/src/*.js"]
},
"plugins": [
{ "name": "@effect/language-service", "includeSuggestionsInTsc": false }
]
}
}

View File

@@ -0,0 +1,66 @@
{
"name": "@effect/ai-openrouter",
"version": "4.0.0-beta.99",
"type": "module",
"license": "MIT",
"description": "An OpenRouter provider integration for Effect AI SDK",
"homepage": "https://effect.website",
"repository": {
"type": "git",
"url": "https://github.com/Effect-TS/effect.git",
"directory": "packages/ai/openrouter"
},
"bugs": {
"url": "https://github.com/Effect-TS/effect/issues"
},
"tags": [
"typescript",
"ai",
"openrouter"
],
"keywords": [
"typescript",
"ai",
"openrouter"
],
"sideEffects": [],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts",
"./*": "./src/*.ts",
"./internal/*": null,
"./*/index": null
},
"files": [
"src/**/*.ts",
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts",
"dist/**/*.d.ts.map"
],
"publishConfig": {
"access": "public",
"provenance": true,
"exports": {
"./package.json": "./package.json",
".": "./dist/index.js",
"./*": "./dist/*.js",
"./internal/*": null,
"./*/index": null
}
},
"scripts": {
"codegen": "effect-utils codegen",
"build": "tsc -b tsconfig.json && pnpm babel",
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
"check": "tsc -b tsconfig.json",
"test": "vitest",
"coverage": "vitest --coverage"
},
"devDependencies": {
"effect": "workspace:^"
},
"peerDependencies": {
"effect": "workspace:^"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,357 @@
/**
* The `OpenRouterClient` module provides an Effect service for calling
* OpenRouter's chat completions API. It wraps the generated OpenRouter HTTP
* client with Effect-native constructors, layers, authentication and optional
* site ranking headers, typed errors, and streaming support.
*
* @since 4.0.0
*/
import type * as Config from "effect/Config"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { identity } from "effect/Function"
import * as Layer from "effect/Layer"
import * as Predicate from "effect/Predicate"
import type * as Redacted from "effect/Redacted"
import * as Schema from "effect/Schema"
import * as Stream from "effect/Stream"
import type * as AiError from "effect/unstable/ai/AiError"
import * as Sse from "effect/unstable/encoding/Sse"
import * as HttpBody from "effect/unstable/http/HttpBody"
import * as HttpClient from "effect/unstable/http/HttpClient"
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import * as Generated from "./Generated.ts"
import * as Errors from "./internal/errors.ts"
import { OpenRouterConfig } from "./OpenRouterConfig.ts"
// =============================================================================
// Service Interface
// =============================================================================
/**
* The OpenRouter client service interface.
*
* **Details**
*
* Provides methods for interacting with OpenRouter's Chat Completions API,
* including both synchronous and streaming message creation.
*
* @category models
* @since 4.0.0
*/
export interface Service {
readonly client: Generated.OpenRouterClient
readonly createChatCompletion: (
options: typeof Generated.ChatGenerationParams.Encoded
) => Effect.Effect<
[body: typeof Generated.SendChatCompletionRequest200.Type, response: HttpClientResponse.HttpClientResponse],
AiError.AiError
>
readonly createChatCompletionStream: (
options: Omit<typeof Generated.ChatGenerationParams.Encoded, "stream" | "stream_options">
) => Effect.Effect<
[
response: HttpClientResponse.HttpClientResponse,
stream: Stream.Stream<ChatStreamingResponseChunkData, AiError.AiError>
],
AiError.AiError
>
}
/**
* Decoded `data` payload from an OpenRouter chat completion streaming chunk.
*
* **Details**
*
* The payload contains streamed choices, model metadata, optional usage, and may
* include an OpenRouter error object for a streamed response.
*
* @category models
* @since 4.0.0
*/
export type ChatStreamingResponseChunkData = typeof Generated.ChatStreamingResponseChunk.fields.data.Type
// =============================================================================
// Service Identifier
// =============================================================================
/**
* Service tag for the OpenRouter client.
*
* **When to use**
*
* Use when accessing or providing the OpenRouter client service through
* Effect's context.
*
* @see {@link make} for constructing an OpenRouter client effectfully
* @see {@link layer} for providing a client from explicit options
* @see {@link layerConfig} for providing a client from `Config`
*
* @category services
* @since 4.0.0
*/
export class OpenRouterClient extends Context.Service<
OpenRouterClient,
Service
>()("@effect/ai-openrouter/OpenRouterClient") {}
// =============================================================================
// Options
// =============================================================================
/**
* Configuration for creating an OpenRouter client.
*
* @category options
* @since 4.0.0
*/
export type Options = {
readonly apiKey?: Redacted.Redacted<string> | undefined
readonly apiUrl?: string | undefined
/**
* Optional URL of your site for rankings on `openrouter.ai`.
*/
readonly siteReferrer?: string | undefined
/**
* Optional title of your site for rankings on `openrouter.ai`.
*/
readonly siteTitle?: string | undefined
/**
* Optional transformer for the underlying HTTP client.
*
* **When to use**
*
* Use to add middleware, logging, or custom request/response handling.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}
// =============================================================================
// Constructor
// =============================================================================
/**
* Creates an OpenRouter client service from explicit options.
*
* **When to use**
*
* Use when you need the OpenRouter client service value inside an effect.
*
* **Details**
*
* The returned service uses the current `HttpClient`, prepends `apiUrl` or
* `https://openrouter.ai/api/v1`, adds the bearer token and optional
* `HTTP-Referer` and `X-Title` headers, accepts JSON responses, and applies
* `transformClient` when provided.
*
* **Gotchas**
*
* Scoped `OpenRouterConfig.withClientTransform` applies to generated client
* request methods. Streaming chat completion requests are sent directly by this
* module and do not read that scoped transform.
*
* @see {@link layer} for providing this client from explicit options
* @see {@link layerConfig} for loading client settings from `Config`
*
* @category constructors
* @since 4.0.0
*/
export const make = Effect.fnUntraced(
function*(options: Options): Effect.fn.Return<Service, never, HttpClient.HttpClient> {
const baseClient = yield* HttpClient.HttpClient
const httpClient = baseClient.pipe(
HttpClient.mapRequest((request) =>
request.pipe(
HttpClientRequest.prependUrl(options.apiUrl ?? "https://openrouter.ai/api/v1"),
options.apiKey ? HttpClientRequest.bearerToken(options.apiKey) : identity,
options.siteReferrer ? HttpClientRequest.setHeader("HTTP-Referer", options.siteReferrer) : identity,
options.siteTitle ? HttpClientRequest.setHeader("X-Title", options.siteTitle) : identity,
HttpClientRequest.acceptJson
)
),
options.transformClient ?? identity
)
const httpClientOk = HttpClient.filterStatusOk(httpClient)
const client = Generated.make(httpClient, {
transformClient: Effect.fnUntraced(function*(client) {
const config = yield* OpenRouterConfig.getOrUndefined
if (Predicate.isNotUndefined(config?.transformClient)) {
return config.transformClient(client)
}
return client
})
})
const createChatCompletion: Service["createChatCompletion"] = (payload) =>
client.sendChatCompletionRequest({ payload, config: { includeResponse: true } }).pipe(
Effect.catchTags({
SendChatCompletionRequest400: (error) => Effect.fail(Errors.mapClientError(error, "createChatCompletion")),
SendChatCompletionRequest401: (error) => Effect.fail(Errors.mapClientError(error, "createChatCompletion")),
SendChatCompletionRequest429: (error) => Effect.fail(Errors.mapClientError(error, "createChatCompletion")),
SendChatCompletionRequest500: (error) => Effect.fail(Errors.mapClientError(error, "createChatCompletion")),
HttpClientError: (error) => Errors.mapHttpClientError(error, "createChatCompletion"),
SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createChatCompletion"))
})
)
const buildChatCompletionStream = (
response: HttpClientResponse.HttpClientResponse
): [
HttpClientResponse.HttpClientResponse,
Stream.Stream<ChatStreamingResponseChunkData, AiError.AiError>
] => {
const stream = response.stream.pipe(
Stream.decodeText(),
Stream.pipeThroughChannel(Sse.decode()),
Stream.mapEffect((event) => decodeChatCompletionSseData(event.data)),
Stream.takeWhile((data) => data !== "[DONE]"),
Stream.catchTags({
// TODO: handle SSE retries
Retry: (error) => Stream.die(error),
HttpClientError: (error) => Stream.fromEffect(Errors.mapHttpClientError(error, "createChatCompletionStream")),
SchemaError: (error) => Stream.fail(Errors.mapSchemaError(error, "createChatCompletionStream"))
})
) as any
return [response, stream]
}
const createChatCompletionStream: Service["createChatCompletionStream"] = (payload) =>
httpClientOk.execute(
HttpClientRequest.post("/chat/completions", {
body: HttpBody.jsonUnsafe({
...payload,
stream: true,
stream_options: { include_usage: true }
})
})
).pipe(
Effect.map(buildChatCompletionStream),
Effect.catchTag(
"HttpClientError",
(error) => Errors.mapHttpClientError(error, "createChatCompletionStream")
)
)
return OpenRouterClient.of({
client,
createChatCompletion,
createChatCompletionStream
})
}
)
// =============================================================================
// Layers
// =============================================================================
/**
* Creates a layer for the OpenRouter client with the given options.
*
* **When to use**
*
* Use when you already have the OpenRouter client options in code and want to
* provide `OpenRouterClient` as a layer.
*
* @see {@link make} for constructing the client service effectfully
* @see {@link layerConfig} for loading client settings from `Config`
*
* @category layers
* @since 4.0.0
*/
export const layer = (options: Options): Layer.Layer<OpenRouterClient, never, HttpClient.HttpClient> =>
Layer.effect(OpenRouterClient, make(options))
/**
* Creates a layer for the OpenRouter client from provided `Config` values.
*
* **When to use**
*
* Use when you need client settings for OpenRouter to be read from Effect
* `Config` values while providing `OpenRouterClient` as a layer.
*
* **Details**
*
* Only config values supplied in `options` are loaded. Omitted fields are
* passed to `make` as `undefined`, and `transformClient` is forwarded as a
* plain option.
*
* @see {@link make} for constructing the client service effectfully
* @see {@link layer} for providing the client from already-resolved options
*
* @category layers
* @since 4.0.0
*/
export const layerConfig = (options?: {
/**
* The config value to load for the API key.
*/
readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
/**
* The config value to load for the API URL.
*/
readonly apiUrl?: Config.Config<string> | undefined
/**
* The config value to load for the site referrer URL.
*/
readonly siteReferrer?: Config.Config<string> | undefined
/**
* The config value to load for the site title.
*/
readonly siteTitle?: Config.Config<string> | undefined
/**
* Optional transformer for the HTTP client.
*/
readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
}): Layer.Layer<OpenRouterClient, Config.ConfigError, HttpClient.HttpClient> =>
Layer.effect(
OpenRouterClient,
Effect.gen(function*() {
const apiKey = Predicate.isNotUndefined(options?.apiKey)
? yield* options.apiKey
: undefined
const apiUrl = Predicate.isNotUndefined(options?.apiUrl)
? yield* options.apiUrl
: undefined
const siteReferrer = Predicate.isNotUndefined(options?.siteReferrer)
? yield* options.siteReferrer
: undefined
const siteTitle = Predicate.isNotUndefined(options?.siteTitle)
? yield* options.siteTitle
: undefined
return yield* make({
apiKey,
apiUrl,
siteReferrer,
siteTitle,
transformClient: options?.transformClient
})
})
)
// =============================================================================
// Internal Utilities
// =============================================================================
const ChatStreamingResponseChunkDataFromString = Schema.fromJsonString(Generated.ChatStreamingResponseChunk.fields.data)
const decodeChatStreamingResponseChunkData = Schema.decodeUnknownEffect(ChatStreamingResponseChunkDataFromString)
const decodeChatCompletionSseData = (
data: string
): Effect.Effect<ChatStreamingResponseChunkData | "[DONE]", Schema.SchemaError> =>
data === "[DONE]"
? Effect.succeed(data)
: decodeChatStreamingResponseChunkData(data)

View File

@@ -0,0 +1,99 @@
/**
* The `OpenRouterConfig` module lets a workflow temporarily customize the HTTP
* client used by generated OpenRouter request methods. `OpenRouterClient` reads
* this scoped transform when generated client operations execute, so callers can
* add middleware or instrumentation without rebuilding the client layer.
*
* @since 4.0.0
*/
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import type { HttpClient } from "effect/unstable/http/HttpClient"
/**
* Context service for scoped OpenRouter provider configuration used by client
* operations.
*
* **When to use**
*
* Use as the context service tag when manually providing or reading scoped
* OpenRouter provider configuration in an Effect context.
*
* @see {@link withClientTransform} for scoping an HTTP client transformation
*
* @category services
* @since 4.0.0
*/
export class OpenRouterConfig extends Context.Service<
OpenRouterConfig,
OpenRouterConfig.Service
>()("@effect/ai-openrouter/OpenRouterConfig") {
/**
* Gets the configured OpenRouter service from the current context when present.
*
* @since 4.0.0
*/
static readonly getOrUndefined: Effect.Effect<typeof OpenRouterConfig.Service | undefined> = Effect.map(
Effect.context<never>(),
(services) => services.mapUnsafe.get(OpenRouterConfig.key)
)
}
/**
* Types associated with the `OpenRouterConfig` context service.
*
* @since 4.0.0
*/
export declare namespace OpenRouterConfig {
/**
* Configuration values read by OpenRouter provider operations when resolving
* the generated HTTP client.
*
* @category models
* @since 4.0.0
*/
export interface Service {
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined
}
}
/**
* Provides a scoped transform for the OpenRouter HTTP client used by provider
* operations.
*
* **When to use**
*
* Use when you need temporary OpenRouter HTTP client customization for a
* single effect or workflow without rebuilding the client layer.
*
* **Details**
*
* Supports both data-first and data-last forms. The transform is stored in the
* scoped `OpenRouterConfig` service and read by generated OpenRouter request
* operations while running the supplied effect.
*
* **Gotchas**
*
* If a transform is already present in the scoped config, this helper replaces
* it. Compose transforms manually when both should apply. Streaming chat
* completion requests are sent directly by `OpenRouterClient.make` and do not
* read this scoped transform.
*
* @category configuration
* @since 4.0.0
*/
export const withClientTransform: {
(transform: (client: HttpClient) => HttpClient): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<A, E, R>(self: Effect.Effect<A, E, R>, transform: (client: HttpClient) => HttpClient): Effect.Effect<A, E, R>
} = dual<
(transform: (client: HttpClient) => HttpClient) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>,
<A, E, R>(self: Effect.Effect<A, E, R>, transform: (client: HttpClient) => HttpClient) => Effect.Effect<A, E, R>
>(
2,
(self, transformClient) =>
Effect.flatMap(
OpenRouterConfig.getOrUndefined,
(config) => Effect.provideService(self, OpenRouterConfig, { ...config, transformClient })
)
)

View File

@@ -0,0 +1,225 @@
/**
* OpenRouter error metadata augmentation.
*
* Provides OpenRouter-specific metadata fields for AI error types through
* module augmentation, enabling typed access to OpenRouter error details.
*
* @since 4.0.0
*/
/**
* OpenRouter-specific error metadata fields.
*
* @category models
* @since 4.0.0
*/
export type OpenRouterErrorMetadata = {
/**
* The error code returned by the API.
*/
readonly errorCode: string | number | null
/**
* The error type returned by the API.
*/
readonly errorType: string | null
/**
* The unique request ID for debugging.
*/
readonly requestId: string | null
}
/**
* OpenRouter-specific rate limit metadata fields.
*
* @category models
* @since 4.0.0
*/
export type OpenRouterRateLimitMetadata = OpenRouterErrorMetadata & {
readonly limit: string | null
readonly remaining: number | null
readonly resetRequests: string | null
readonly resetTokens: string | null
}
declare module "effect/unstable/ai/AiError" {
/**
* OpenRouter metadata attached to `RateLimitError` values.
*
* **Details**
*
* Captures OpenRouter error details together with rate limit header
* information from responses where the provider rejected the request because
* a limit was reached.
*
* @category configuration
* @since 4.0.0
*/
export interface RateLimitErrorMetadata {
/**
* OpenRouter-specific details for the rate limit response.
*/
readonly openrouter?: OpenRouterRateLimitMetadata | null
}
/**
* OpenRouter metadata attached to `QuotaExhaustedError` values.
*
* **Details**
*
* Preserves provider error details for failures caused by exhausted account,
* billing, or usage quota.
*
* @category configuration
* @since 4.0.0
*/
export interface QuotaExhaustedErrorMetadata {
/**
* OpenRouter-specific details for the quota exhaustion response.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `AuthenticationError` values.
*
* **Details**
*
* Preserves provider error details for failed API key, authorization, or
* permission checks.
*
* @category configuration
* @since 4.0.0
*/
export interface AuthenticationErrorMetadata {
/**
* OpenRouter-specific details for the authentication failure.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `ContentPolicyError` values.
*
* **Details**
*
* Preserves provider error details when OpenRouter rejects input or output
* because it violates a content policy.
*
* @category configuration
* @since 4.0.0
*/
export interface ContentPolicyErrorMetadata {
/**
* OpenRouter-specific details for the content policy response.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `InvalidRequestError` values.
*
* **Details**
*
* Preserves provider error details for malformed requests, unsupported
* parameters, or other request validation failures reported by OpenRouter.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidRequestErrorMetadata {
/**
* OpenRouter-specific details for the invalid request response.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `InternalProviderError` values.
*
* **Details**
*
* Preserves provider error details for OpenRouter-side failures such as
* transient server errors or overload responses.
*
* @category configuration
* @since 4.0.0
*/
export interface InternalProviderErrorMetadata {
/**
* OpenRouter-specific details for the internal provider response.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `InvalidOutputError` values.
*
* **Details**
*
* Preserves provider error details when an OpenRouter response cannot be
* parsed or validated as the expected output.
*
* @category configuration
* @since 4.0.0
*/
export interface InvalidOutputErrorMetadata {
/**
* OpenRouter-specific details for the invalid output response.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `StructuredOutputError` values.
*
* **Details**
*
* Preserves provider error details when OpenRouter returns content that does
* not satisfy the requested structured output schema.
*
* @category configuration
* @since 4.0.0
*/
export interface StructuredOutputErrorMetadata {
/**
* OpenRouter-specific details for the structured output failure.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `UnsupportedSchemaError` values.
*
* **Details**
*
* Preserves provider error details when an unsupported schema failure is
* associated with an OpenRouter response.
*
* @category configuration
* @since 4.0.0
*/
export interface UnsupportedSchemaErrorMetadata {
/**
* OpenRouter-specific details for the unsupported schema failure.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
/**
* OpenRouter metadata attached to `UnknownError` values.
*
* **Details**
*
* Preserves provider error details for OpenRouter failures that do not map
* cleanly to a more specific AI error category.
*
* @category configuration
* @since 4.0.0
*/
export interface UnknownErrorMetadata {
/**
* OpenRouter-specific details for the unclassified provider failure.
*/
readonly openrouter?: OpenRouterErrorMetadata | null
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
/**
* @since 4.0.0
*/
// @barrel: Auto-generated exports. Do not edit manually.
/**
* @since 4.0.0
*/
export * as Generated from "./Generated.ts"
/**
* @since 4.0.0
*/
export * as OpenRouterClient from "./OpenRouterClient.ts"
/**
* @since 4.0.0
*/
export * as OpenRouterConfig from "./OpenRouterConfig.ts"
/**
* @since 4.0.0
*/
export * as OpenRouterError from "./OpenRouterError.ts"
/**
* @since 4.0.0
*/
export * as OpenRouterLanguageModel from "./OpenRouterLanguageModel.ts"

View File

@@ -0,0 +1,375 @@
import * as Duration from "effect/Duration"
import * as Effect from "effect/Effect"
import { dual } from "effect/Function"
import * as Number from "effect/Number"
import * as Option from "effect/Option"
import * as Predicate from "effect/Predicate"
import * as Redactable from "effect/Redactable"
import * as Schema from "effect/Schema"
import * as AiError from "effect/unstable/ai/AiError"
import type * as Response from "effect/unstable/ai/Response"
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
import type * as Generated from "../Generated.ts"
import type { OpenRouterErrorMetadata } from "../OpenRouterError.ts"
// =============================================================================
// OpenRouter Error Body Schema
// =============================================================================
/** @internal */
export const OpenRouterErrorBody = Schema.Struct({
error: Schema.Struct({
message: Schema.String,
type: Schema.optional(Schema.NullOr(Schema.String)),
code: Schema.optional(Schema.NullOr(Schema.Union([Schema.String, Schema.Number.check(Schema.isFinite())])))
})
})
/** @internal */
export type OpenRouterClientErrorBody = {
readonly error: {
readonly code: string | number | null
readonly message: string
readonly param?: string | null
readonly type?: string | null
}
}
// =============================================================================
// Error Mappers
// =============================================================================
/** @internal */
export const mapSchemaError = dual<
(method: string) => (error: Schema.SchemaError) => AiError.AiError,
(error: Schema.SchemaError, method: string) => AiError.AiError
>(2, (error, method) =>
AiError.make({
module: "OpenRouterClient",
method,
reason: AiError.InvalidOutputError.fromSchemaError(error)
}))
/** @internal */
export const mapClientError = dual<
(method: string) => (error: Generated.OpenRouterClientError<string, OpenRouterClientErrorBody>) => AiError.AiError,
(error: Generated.OpenRouterClientError<string, OpenRouterClientErrorBody>, method: string) => AiError.AiError
>(2, (error, method) => {
const { request, response, cause } = error
const status = response.status
const headers = response.headers as Record<string, string>
const metadata: OpenRouterErrorMetadata = {
errorCode: cause.error.code ?? null,
errorType: cause.error.type ?? null,
requestId: headers["x-request-id"] ?? null
}
const http = buildHttpContext({ request, response, body: JSON.stringify(cause) })
const reason = mapStatusCodeToReason({
status,
headers,
message: cause.error.message,
metadata,
http
})
return AiError.make({ module: "OpenRouterClient", method, reason })
})
/** @internal */
export const mapHttpClientError = dual<
(method: string) => (error: HttpClientError.HttpClientError) => Effect.Effect<never, AiError.AiError>,
(error: HttpClientError.HttpClientError, method: string) => Effect.Effect<never, AiError.AiError>
>(2, (error, method) => {
const reason = error.reason
switch (reason._tag) {
case "TransportError": {
return Effect.fail(AiError.make({
module: "OpenRouterClient",
method,
reason: new AiError.NetworkError({
reason: "TransportError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "EncodeError": {
return Effect.fail(AiError.make({
module: "OpenRouterClient",
method,
reason: new AiError.NetworkError({
reason: "EncodeError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "InvalidUrlError": {
return Effect.fail(AiError.make({
module: "OpenRouterClient",
method,
reason: new AiError.NetworkError({
reason: "InvalidUrlError",
description: reason.description,
request: buildHttpRequestDetails(reason.request)
})
}))
}
case "StatusCodeError": {
return mapStatusCodeError(reason, method)
}
case "DecodeError": {
return Effect.fail(AiError.make({
module: "OpenRouterClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Failed to decode response"
})
}))
}
case "EmptyBodyError": {
return Effect.fail(AiError.make({
module: "OpenRouterClient",
method,
reason: new AiError.InvalidOutputError({
description: reason.description ?? "Response body was empty"
})
}))
}
}
})
/** @internal */
const mapStatusCodeError = Effect.fnUntraced(function*(
error: HttpClientError.StatusCodeError,
method: string
) {
const { request, response, description } = error
const status = response.status
const headers = response.headers as Record<string, string>
const requestId = headers["x-request-id"]
let body: string | undefined = description
if (!description || !description.startsWith("{")) {
const responseBody = yield* Effect.option(response.text)
if (Option.isSome(responseBody) && responseBody.value) {
body = responseBody.value
}
}
let json: unknown = undefined
// @effect-diagnostics effect/tryCatchInEffectGen:off
try {
json = Predicate.isNotUndefined(body) ? JSON.parse(body) : undefined
} catch {
json = undefined
}
const decoded = Schema.decodeUnknownOption(OpenRouterErrorBody)(json)
const reason = mapStatusCodeToReason({
status,
headers,
message: Option.isSome(decoded) ? decoded.value.error.message : undefined,
http: buildHttpContext({ request, response, body }),
metadata: {
errorCode: Option.isSome(decoded) ? decoded.value.error.code ?? null : null,
errorType: Option.isSome(decoded) ? decoded.value.error.type ?? null : null,
requestId: requestId ?? null
}
})
return yield* AiError.make({ module: "OpenRouterClient", method, reason })
})
// =============================================================================
// Rate Limits
// =============================================================================
/** @internal */
export const parseRateLimitHeaders = (headers: Record<string, string>) => {
const retryAfterRaw = headers["retry-after"]
let retryAfter: Duration.Duration | undefined
if (Predicate.isNotUndefined(retryAfterRaw)) {
const parsed = Number.parse(retryAfterRaw)
if (Option.isSome(parsed)) {
retryAfter = Duration.seconds(parsed.value)
}
}
const remainingRaw = headers["x-ratelimit-remaining-requests"]
const remaining = Predicate.isNotUndefined(remainingRaw)
? Option.getOrNull(Number.parse(remainingRaw))
: null
return {
retryAfter,
limit: headers["x-ratelimit-limit-requests"] ?? null,
remaining,
resetRequests: headers["x-ratelimit-reset-requests"] ?? null,
resetTokens: headers["x-ratelimit-reset-tokens"] ?? null
}
}
// =============================================================================
// HTTP Context
// =============================================================================
/** @internal */
export const buildHttpRequestDetails = (
request: HttpClientRequest.HttpClientRequest
): typeof Response.HttpRequestDetails.Type => ({
method: request.method,
url: request.url,
urlParams: Array.from(request.urlParams),
hash: Option.getOrUndefined(request.hash),
headers: Redactable.redact(request.headers) as Record<string, string>
})
/** @internal */
export const buildHttpContext = (params: {
readonly request: HttpClientRequest.HttpClientRequest
readonly response?: HttpClientResponse.HttpClientResponse
readonly body?: string | undefined
}): typeof AiError.HttpContext.Type => ({
request: buildHttpRequestDetails(params.request),
response: Predicate.isNotUndefined(params.response)
? {
status: params.response.status,
headers: Redactable.redact(params.response.headers) as Record<string, string>
}
: undefined,
body: params.body
})
// =============================================================================
// HTTP Status Code
// =============================================================================
const buildInvalidRequestDescription = (params: {
readonly status: number
readonly message: string | undefined
readonly method: string
readonly url: string
readonly errorCode: string | number | null
readonly errorType: string | null
readonly requestId: string | null
readonly body: string | undefined
}): string => {
const parts: Array<string> = []
if (params.message) {
parts.push(params.message)
} else {
parts.push(`HTTP ${params.status}`)
}
parts.push(`(${params.method} ${params.url})`)
if (params.errorCode) {
parts.push(`[code: ${params.errorCode}]`)
} else if (params.errorType) {
parts.push(`[type: ${params.errorType}]`)
}
if (params.requestId) {
parts.push(`[requestId: ${params.requestId}]`)
}
if (!params.message && params.body) {
const truncated = params.body.length > 200
? params.body.slice(0, 200) + "..."
: params.body
parts.push(`Response: ${truncated}`)
}
return parts.join(" ")
}
/** @internal */
export const mapStatusCodeToReason = ({ status, headers, message, metadata, http }: {
readonly status: number
readonly headers: Record<string, string>
readonly message: string | undefined
readonly metadata: OpenRouterErrorMetadata
readonly http: typeof AiError.HttpContext.Type
}): AiError.AiErrorReason => {
const invalidRequestDescription = buildInvalidRequestDescription({
status,
message,
method: http.request.method,
url: http.request.url,
errorCode: metadata.errorCode,
errorType: metadata.errorType,
requestId: metadata.requestId,
body: http.body
})
switch (status) {
case 400:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openrouter: metadata },
http
})
case 401:
return new AiError.AuthenticationError({
kind: "InvalidKey",
metadata: { openrouter: metadata },
http
})
case 403:
return new AiError.AuthenticationError({
kind: "InsufficientPermissions",
metadata: { openrouter: metadata },
http
})
case 404:
case 409:
case 422:
return new AiError.InvalidRequestError({
description: invalidRequestDescription,
metadata: { openrouter: metadata },
http
})
case 429: {
if (
metadata.errorCode === "insufficient_quota" ||
metadata.errorType === "insufficient_quota"
) {
return new AiError.QuotaExhaustedError({
metadata: { openrouter: metadata },
http
})
}
const { retryAfter, ...rateLimitMetadata } = parseRateLimitHeaders(headers)
return new AiError.RateLimitError({
retryAfter,
metadata: {
openrouter: {
...metadata,
...rateLimitMetadata
}
},
http
})
}
case 529:
return new AiError.InternalProviderError({
description: message ?? "OpenRouter API is overloaded",
metadata: { openrouter: metadata },
http
})
default:
if (status >= 500) {
return new AiError.InternalProviderError({
description: message ?? "Server error",
metadata: { openrouter: metadata },
http
})
}
return new AiError.UnknownError({
description: message,
metadata: { openrouter: metadata },
http
})
}
}

View File

@@ -0,0 +1,92 @@
import * as Predicate from "effect/Predicate"
import type * as Response from "effect/unstable/ai/Response"
import type { ReasoningDetails } from "../OpenRouterLanguageModel.ts"
const finishReasonMap: Record<string, Response.FinishReason> = {
content_filter: "content-filter",
error: "error",
function_call: "tool-calls",
length: "length",
tool_calls: "tool-calls",
stop: "stop"
}
/** @internal */
export const resolveFinishReason = (
finishReason: string | null | undefined
): Response.FinishReason =>
Predicate.isNotNullish(finishReason)
? finishReasonMap[finishReason]
: "other"
/**
* Tracks ReasoningDetailUnion entries and deduplicates them based
* on a derived canonical key.
*
* This is used when converting messages to ensure the API request only
* contains unique reasoning details, preventing "Duplicate item found with id"
* errors in multi-turn conversations.
*
* The canonical key logic matches the OpenRouter API's deduplication exactly
* (see openrouter-web/packages/llm-interfaces/reasonings/duplicate-tracker.ts):
* - Summary: key = summary field
* - Encrypted: key = id field (if truthy) or data field
* - Text: key = text field (if truthy) or signature field (if truthy)
*
* @internal
*/
export class ReasoningDetailsDuplicateTracker {
readonly #seenKeys = new Set<string>()
/**
* Attempts to track a detail.
*
* or `false` if it was skipped (no valid key) or already seen (duplicate).
*/
upsert(detail: ReasoningDetails[number]): boolean {
const key = this.getCanonicalKey(detail)
if (Predicate.isNull(key)) {
return false
}
if (this.#seenKeys.has(key)) {
return false
}
this.#seenKeys.add(key)
return true
}
private getCanonicalKey(detail: ReasoningDetails[number]): string | null {
// This logic matches the OpenRouter API's deduplication exactly.
// See: openrouter-web/packages/llm-interfaces/reasonings/duplicate-tracker.ts
switch (detail.type) {
case "reasoning.summary": {
return detail.summary
}
case "reasoning.encrypted": {
return Predicate.isNotNullish(detail.id) ? detail.id : detail.data
}
case "reasoning.text": {
if (Predicate.isNotNullish(detail.text)) {
return detail.text
}
if (Predicate.isNotNullish(detail.signature)) {
return detail.signature
}
return null
}
default: {
// Handle unknown types gracefully
return null
}
}
}
}

View File

@@ -0,0 +1,265 @@
import { Generated, OpenRouterClient, OpenRouterLanguageModel } from "@effect/ai-openrouter"
import { assert, describe, it } from "@effect/vitest"
import { deepStrictEqual, strictEqual } from "@effect/vitest/utils"
import { Array, Context, Effect, Layer, Redacted, Ref, Schema } from "effect"
import { LanguageModel, Prompt } from "effect/unstable/ai"
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
describe("OpenRouterLanguageModel", () => {
describe("generateText", () => {
describe("message preparation", () => {
describe("audio file parts", () => {
it.effect("converts audio bytes to input_audio", () =>
Effect.gen(function*() {
const audioData = new Uint8Array([0x49, 0x44, 0x33, 0x04]) // ID3v2 magic bytes
yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: [
Prompt.filePart({
mediaType: "audio/mpeg",
data: audioData
})
]
}])
}).pipe(Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")))
const requests = yield* MockHttpClient.requests
const body = yield* getRequestBody(requests[0])
const userMessage = body.messages.find((message: any) => message.role === "user")
deepStrictEqual(userMessage.content, [{
type: "input_audio",
input_audio: {
data: "SUQzBA==",
format: "mp3"
}
}])
}).pipe(Effect.provide(makeTestLayer())))
it.effect("converts base64 data url audio to input_audio", () =>
Effect.gen(function*() {
yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: [
Prompt.filePart({
mediaType: "audio/wav",
data: "data:audio/wav;base64,UklGRg=="
})
]
}])
}).pipe(Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")))
const requests = yield* MockHttpClient.requests
const body = yield* getRequestBody(requests[0])
const userMessage = body.messages.find((message: any) => message.role === "user")
deepStrictEqual(userMessage.content, [{
type: "input_audio",
input_audio: {
data: "UklGRg==",
format: "wav"
}
}])
}).pipe(Effect.provide(makeTestLayer())))
it.effect("maps audio media types to OpenRouter audio formats", () =>
Effect.gen(function*() {
const mediaTypes: ReadonlyArray<readonly [mediaType: string, format: string]> = [
["audio/aac", "aac"],
["audio/x-aiff", "aiff"],
["audio/flac", "flac"],
["audio/L16", "pcm16"],
["audio/mp4", "m4a"],
["audio/mp3", "mp3"],
["audio/ogg", "ogg"],
["audio/x-wav", "wav"]
]
yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: mediaTypes.map(([mediaType]) =>
Prompt.filePart({
mediaType,
data: new Uint8Array([0x00])
})
)
}])
}).pipe(Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")))
const requests = yield* MockHttpClient.requests
const body = yield* getRequestBody(requests[0])
const userMessage = body.messages.find((message: any) => message.role === "user")
deepStrictEqual(
userMessage.content.map((item: any) => item.input_audio.format),
mediaTypes.map(([, format]) => format)
)
}).pipe(Effect.provide(makeTestLayer())))
it.effect("fails on unsupported audio media types", () =>
Effect.gen(function*() {
const error = yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: [
Prompt.filePart({
mediaType: "audio/webm",
data: new Uint8Array([0x00])
})
]
}])
}).pipe(
Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")),
Effect.flip
)
strictEqual(error.reason._tag, "InvalidUserInputError")
assert.include(error.message, "audio/webm")
}).pipe(Effect.provide(makeTestLayer())))
it.effect("fails on audio URLs", () =>
Effect.gen(function*() {
const error = yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: [
Prompt.filePart({
mediaType: "audio/mpeg",
data: new URL("https://example.com/audio.mp3")
})
]
}])
}).pipe(
Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")),
Effect.flip
)
strictEqual(error.reason._tag, "InvalidUserInputError")
}).pipe(Effect.provide(makeTestLayer())))
it.effect("converts non-audio files to file blocks", () =>
Effect.gen(function*() {
const pdfData = new Uint8Array([0x25, 0x50, 0x44, 0x46]) // %PDF
yield* LanguageModel.generateText({
prompt: Prompt.make([{
role: "user",
content: [
Prompt.filePart({
mediaType: "application/pdf",
fileName: "document.pdf",
data: pdfData
})
]
}])
}).pipe(Effect.provide(OpenRouterLanguageModel.model("google/gemini-2.5-flash")))
const requests = yield* MockHttpClient.requests
const body = yield* getRequestBody(requests[0])
const userMessage = body.messages.find((message: any) => message.role === "user")
deepStrictEqual(userMessage.content, [{
type: "file",
file: {
filename: "document.pdf",
file_data: "data:application/pdf;base64,JVBERg=="
}
}])
}).pipe(Effect.provide(makeTestLayer())))
})
})
})
})
// =============================================================================
// Test Infrastructure
// =============================================================================
class MockOpenRouterResponse extends Context.Service<MockOpenRouterResponse, {
readonly status: number
readonly body: typeof Generated.SendChatCompletionRequest200.Type
readonly headers?: Record<string, string> | undefined
}>()("MockOpenRouterResponse") {}
class MockHttpClient extends Context.Service<MockHttpClient, {
readonly requests: Effect.Effect<ReadonlyArray<HttpClientRequest.HttpClientRequest>>
}>()("MockHttpClient") {
static requests = Effect.service(MockHttpClient).pipe(
Effect.flatMap((client) => client.requests)
)
}
const encodeResponse = Schema.encodeEffect(Generated.SendChatCompletionRequest200)
const makeHttpClient = Effect.gen(function*() {
const capturedRequests = yield* Ref.make<ReadonlyArray<HttpClientRequest.HttpClientRequest>>([])
const response = yield* MockOpenRouterResponse
const body = yield* Effect.orDie(encodeResponse(response.body))
const httpClient = HttpClient.makeWith(
Effect.fnUntraced(function*(requestEffect) {
const request = yield* requestEffect
yield* Ref.update(capturedRequests, Array.append(request))
return HttpClientResponse.fromWeb(
request,
new Response(JSON.stringify(body), {
headers: response.headers ?? {},
status: response.status
})
)
}),
Effect.succeed as HttpClient.HttpClient.Preprocess<HttpClientError.HttpClientError, never>
)
return Context.make(HttpClient.HttpClient, httpClient).pipe(
Context.add(MockHttpClient, MockHttpClient.of({ requests: Ref.get(capturedRequests) }))
)
})
const HttpClientLayer = Layer.effectContext(makeHttpClient)
const makeDefaultResponse = (
overrides: Partial<typeof Generated.SendChatCompletionRequest200.Type> = {}
): typeof Generated.SendChatCompletionRequest200.Type => ({
id: "gen-test123",
choices: [{
finish_reason: "stop",
index: 0,
message: {
role: "assistant",
content: "Hello!"
}
}],
created: 1234567890,
model: "google/gemini-2.5-flash",
object: "chat.completion",
...overrides
})
const makeTestLayer = (options: {
readonly body?: Partial<typeof Generated.SendChatCompletionRequest200.Type>
readonly status?: number
readonly headers?: Record<string, string>
} = {}) =>
OpenRouterClient.layer({ apiKey: Redacted.make("sk-test-key") }).pipe(
Layer.provideMerge(HttpClientLayer),
Layer.provide(Layer.succeed(MockOpenRouterResponse, {
body: makeDefaultResponse(options.body),
status: options.status ?? 200,
headers: options.headers ?? {}
}))
)
const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
Effect.gen(function*() {
const body = request.body
if (body._tag === "Uint8Array") {
const text = new TextDecoder().decode(body.body)
return JSON.parse(text)
}
return yield* Effect.die(new Error("Expected Uint8Array body"))
})

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src"],
"references": [
{ "path": "../../effect" },
{ "path": "../anthropic" },
{ "path": "../openai" }
]
}

View File

@@ -0,0 +1,6 @@
import { mergeConfig, type ViteUserConfig } from "vitest/config"
import shared from "../../../vitest.shared.ts"
const config: ViteUserConfig = {}
export default mergeConfig(shared, config)