@fractaal/pi-ai 0.85.4 → 0.86.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (712) hide show
  1. package/README.md +322 -46
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +403 -140
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/anthropic-messages.lazy.d.ts.map +1 -1
  6. package/dist/api/azure-openai-config.d.ts +15 -0
  7. package/dist/api/azure-openai-config.d.ts.map +1 -0
  8. package/dist/api/azure-openai-config.js +76 -0
  9. package/dist/api/azure-openai-config.js.map +1 -0
  10. package/dist/api/azure-openai-responses.d.ts +5 -6
  11. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  12. package/dist/api/azure-openai-responses.js +42 -86
  13. package/dist/api/azure-openai-responses.js.map +1 -1
  14. package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -1
  15. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  16. package/dist/api/bedrock-converse-stream.js +233 -24
  17. package/dist/api/bedrock-converse-stream.js.map +1 -1
  18. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
  19. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
  20. package/dist/api/cloudflare-ai-binding.d.ts +77 -0
  21. package/dist/api/cloudflare-ai-binding.d.ts.map +1 -0
  22. package/dist/api/cloudflare-ai-binding.js +72 -0
  23. package/dist/api/cloudflare-ai-binding.js.map +1 -0
  24. package/dist/api/cloudflare-workers-ai-system-one.d.ts +4 -0
  25. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -0
  26. package/dist/api/cloudflare-workers-ai-system-one.js +48 -0
  27. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -0
  28. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts +3 -0
  29. package/dist/api/cloudflare-workers-ai-system-one.lazy.d.ts.map +1 -0
  30. package/dist/api/cloudflare-workers-ai-system-one.lazy.js +4 -0
  31. package/dist/api/cloudflare-workers-ai-system-one.lazy.js.map +1 -0
  32. package/dist/api/cloudflare.d.ts +2 -0
  33. package/dist/api/cloudflare.d.ts.map +1 -1
  34. package/dist/api/cloudflare.js +2 -0
  35. package/dist/api/cloudflare.js.map +1 -1
  36. package/dist/api/constrained-sampling.d.ts +10 -1
  37. package/dist/api/constrained-sampling.d.ts.map +1 -1
  38. package/dist/api/constrained-sampling.js +137 -4
  39. package/dist/api/constrained-sampling.js.map +1 -1
  40. package/dist/api/github-copilot-headers.d.ts.map +1 -1
  41. package/dist/api/github-copilot-headers.js.map +1 -1
  42. package/dist/api/google-generative-ai.d.ts +2 -2
  43. package/dist/api/google-generative-ai.d.ts.map +1 -1
  44. package/dist/api/google-generative-ai.js +39 -82
  45. package/dist/api/google-generative-ai.js.map +1 -1
  46. package/dist/api/google-generative-ai.lazy.d.ts.map +1 -1
  47. package/dist/api/google-shared.d.ts +27 -5
  48. package/dist/api/google-shared.d.ts.map +1 -1
  49. package/dist/api/google-shared.js +126 -18
  50. package/dist/api/google-shared.js.map +1 -1
  51. package/dist/api/google-vertex.d.ts +2 -2
  52. package/dist/api/google-vertex.d.ts.map +1 -1
  53. package/dist/api/google-vertex.js +42 -73
  54. package/dist/api/google-vertex.js.map +1 -1
  55. package/dist/api/google-vertex.lazy.d.ts.map +1 -1
  56. package/dist/api/lazy.d.ts +5 -1
  57. package/dist/api/lazy.d.ts.map +1 -1
  58. package/dist/api/lazy.js +19 -7
  59. package/dist/api/lazy.js.map +1 -1
  60. package/dist/api/llama-cpp-classify.d.ts +33 -0
  61. package/dist/api/llama-cpp-classify.d.ts.map +1 -0
  62. package/dist/api/llama-cpp-classify.js +365 -0
  63. package/dist/api/llama-cpp-classify.js.map +1 -0
  64. package/dist/api/llama-cpp-classify.lazy.d.ts +3 -0
  65. package/dist/api/llama-cpp-classify.lazy.d.ts.map +1 -0
  66. package/dist/api/llama-cpp-classify.lazy.js +4 -0
  67. package/dist/api/llama-cpp-classify.lazy.js.map +1 -0
  68. package/dist/api/mistral-conversations.d.ts +2 -2
  69. package/dist/api/mistral-conversations.d.ts.map +1 -1
  70. package/dist/api/mistral-conversations.js +250 -69
  71. package/dist/api/mistral-conversations.js.map +1 -1
  72. package/dist/api/mistral-conversations.lazy.d.ts.map +1 -1
  73. package/dist/api/openai-codex-compaction-history.d.ts +2 -2
  74. package/dist/api/openai-codex-compaction-history.d.ts.map +1 -1
  75. package/dist/api/openai-codex-compaction-history.js +1 -2
  76. package/dist/api/openai-codex-compaction-history.js.map +1 -1
  77. package/dist/api/openai-codex-responses.d.ts +1 -1
  78. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  79. package/dist/api/openai-codex-responses.js +107 -60
  80. package/dist/api/openai-codex-responses.js.map +1 -1
  81. package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -1
  82. package/dist/api/openai-codex-responses.lazy.js.map +1 -1
  83. package/dist/api/openai-completions.d.ts +11 -4
  84. package/dist/api/openai-completions.d.ts.map +1 -1
  85. package/dist/api/openai-completions.js +250 -134
  86. package/dist/api/openai-completions.js.map +1 -1
  87. package/dist/api/openai-completions.lazy.d.ts.map +1 -1
  88. package/dist/api/openai-prompt-cache.d.ts.map +1 -1
  89. package/dist/api/openai-prompt-cache.js.map +1 -1
  90. package/dist/api/openai-responses-shared.d.ts +8 -4
  91. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  92. package/dist/api/openai-responses-shared.js +114 -62
  93. package/dist/api/openai-responses-shared.js.map +1 -1
  94. package/dist/api/openai-responses.d.ts.map +1 -1
  95. package/dist/api/openai-responses.js +55 -23
  96. package/dist/api/openai-responses.js.map +1 -1
  97. package/dist/api/openai-responses.lazy.d.ts.map +1 -1
  98. package/dist/api/openrouter-images.d.ts +2 -1
  99. package/dist/api/openrouter-images.d.ts.map +1 -1
  100. package/dist/api/openrouter-images.js +1 -0
  101. package/dist/api/openrouter-images.js.map +1 -1
  102. package/dist/api/openrouter-images.lazy.d.ts.map +1 -1
  103. package/dist/api/openrouter-images.lazy.js.map +1 -1
  104. package/dist/api/pi-messages.d.ts +5 -3
  105. package/dist/api/pi-messages.d.ts.map +1 -1
  106. package/dist/api/pi-messages.js +10 -3
  107. package/dist/api/pi-messages.js.map +1 -1
  108. package/dist/api/pi-messages.lazy.d.ts.map +1 -1
  109. package/dist/api/simple-options.d.ts +10 -3
  110. package/dist/api/simple-options.d.ts.map +1 -1
  111. package/dist/api/simple-options.js +31 -11
  112. package/dist/api/simple-options.js.map +1 -1
  113. package/dist/api/system-one-shared.d.ts +23 -0
  114. package/dist/api/system-one-shared.d.ts.map +1 -0
  115. package/dist/api/system-one-shared.js +183 -0
  116. package/dist/api/system-one-shared.js.map +1 -0
  117. package/dist/api/transform-messages.d.ts.map +1 -1
  118. package/dist/api/transform-messages.js +21 -7
  119. package/dist/api/transform-messages.js.map +1 -1
  120. package/dist/api/typesafe-system-one.d.ts +4 -0
  121. package/dist/api/typesafe-system-one.d.ts.map +1 -0
  122. package/dist/api/typesafe-system-one.js +19 -0
  123. package/dist/api/typesafe-system-one.js.map +1 -0
  124. package/dist/api/typesafe-system-one.lazy.d.ts +3 -0
  125. package/dist/api/typesafe-system-one.lazy.d.ts.map +1 -0
  126. package/dist/api/typesafe-system-one.lazy.js +4 -0
  127. package/dist/api/typesafe-system-one.lazy.js.map +1 -0
  128. package/dist/auth/context.d.ts.map +1 -1
  129. package/dist/auth/context.js.map +1 -1
  130. package/dist/auth/credential-store.d.ts +6 -6
  131. package/dist/auth/credential-store.d.ts.map +1 -1
  132. package/dist/auth/credential-store.js +22 -11
  133. package/dist/auth/credential-store.js.map +1 -1
  134. package/dist/auth/helpers.d.ts +1 -0
  135. package/dist/auth/helpers.d.ts.map +1 -1
  136. package/dist/auth/helpers.js +8 -3
  137. package/dist/auth/helpers.js.map +1 -1
  138. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  139. package/dist/auth/oauth/anthropic.js +78 -131
  140. package/dist/auth/oauth/anthropic.js.map +1 -1
  141. package/dist/auth/oauth/callback-server.d.ts +55 -0
  142. package/dist/auth/oauth/callback-server.d.ts.map +1 -0
  143. package/dist/auth/oauth/callback-server.js +146 -0
  144. package/dist/auth/oauth/callback-server.js.map +1 -0
  145. package/dist/auth/oauth/device-code.d.ts +2 -1
  146. package/dist/auth/oauth/device-code.d.ts.map +1 -1
  147. package/dist/auth/oauth/device-code.js +5 -5
  148. package/dist/auth/oauth/device-code.js.map +1 -1
  149. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  150. package/dist/auth/oauth/github-copilot.js +121 -41
  151. package/dist/auth/oauth/github-copilot.js.map +1 -1
  152. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  153. package/dist/auth/oauth/kimi-coding.js +5 -6
  154. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  155. package/dist/auth/oauth/load.d.ts +4 -0
  156. package/dist/auth/oauth/load.d.ts.map +1 -1
  157. package/dist/auth/oauth/load.js +10 -0
  158. package/dist/auth/oauth/load.js.map +1 -1
  159. package/dist/auth/oauth/meta.d.ts +17 -0
  160. package/dist/auth/oauth/meta.d.ts.map +1 -0
  161. package/dist/auth/oauth/meta.js +190 -0
  162. package/dist/auth/oauth/meta.js.map +1 -0
  163. package/dist/auth/oauth/openai-chatgpt.d.ts +9 -0
  164. package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -0
  165. package/dist/auth/oauth/openai-chatgpt.js +263 -0
  166. package/dist/auth/oauth/openai-chatgpt.js.map +1 -0
  167. package/dist/auth/oauth/openai-codex-errors.d.ts.map +1 -1
  168. package/dist/auth/oauth/openai-codex-errors.js.map +1 -1
  169. package/dist/auth/oauth/openai-codex.d.ts +1 -1
  170. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  171. package/dist/auth/oauth/openai-codex.js +23 -152
  172. package/dist/auth/oauth/openai-codex.js.map +1 -1
  173. package/dist/auth/oauth/openrouter.d.ts +1 -1
  174. package/dist/auth/oauth/openrouter.d.ts.map +1 -1
  175. package/dist/auth/oauth/openrouter.js +25 -144
  176. package/dist/auth/oauth/openrouter.js.map +1 -1
  177. package/dist/auth/oauth/pkce.d.ts.map +1 -1
  178. package/dist/auth/oauth/pkce.js.map +1 -1
  179. package/dist/auth/oauth/radius.d.ts +1 -1
  180. package/dist/auth/oauth/radius.d.ts.map +1 -1
  181. package/dist/auth/oauth/radius.js +26 -93
  182. package/dist/auth/oauth/radius.js.map +1 -1
  183. package/dist/auth/oauth/xai.d.ts.map +1 -1
  184. package/dist/auth/oauth/xai.js +3 -2
  185. package/dist/auth/oauth/xai.js.map +1 -1
  186. package/dist/auth/resolve.d.ts +19 -9
  187. package/dist/auth/resolve.d.ts.map +1 -1
  188. package/dist/auth/resolve.js +72 -53
  189. package/dist/auth/resolve.js.map +1 -1
  190. package/dist/auth/types.d.ts +29 -8
  191. package/dist/auth/types.d.ts.map +1 -1
  192. package/dist/auth/types.js.map +1 -1
  193. package/dist/bedrock-provider.d.ts.map +1 -1
  194. package/dist/bun-oauth.d.ts.map +1 -1
  195. package/dist/bun-oauth.js +4 -0
  196. package/dist/bun-oauth.js.map +1 -1
  197. package/dist/cli.d.ts.map +1 -1
  198. package/dist/cli.js +4 -1
  199. package/dist/cli.js.map +1 -1
  200. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  201. package/dist/compat.d.ts +3 -3
  202. package/dist/compat.d.ts.map +1 -1
  203. package/dist/compat.js +9 -6
  204. package/dist/compat.js.map +1 -1
  205. package/dist/env-api-keys.d.ts +5 -0
  206. package/dist/env-api-keys.d.ts.map +1 -1
  207. package/dist/env-api-keys.js +10 -1
  208. package/dist/env-api-keys.js.map +1 -1
  209. package/dist/image-models.d.ts +19 -8
  210. package/dist/image-models.d.ts.map +1 -1
  211. package/dist/image-models.js +14 -13
  212. package/dist/image-models.js.map +1 -1
  213. package/dist/images-api-registry.d.ts +7 -7
  214. package/dist/images-api-registry.d.ts.map +1 -1
  215. package/dist/images-api-registry.js.map +1 -1
  216. package/dist/images.d.ts +7 -2
  217. package/dist/images.d.ts.map +1 -1
  218. package/dist/images.js +5 -0
  219. package/dist/images.js.map +1 -1
  220. package/dist/index.d.ts +4 -3
  221. package/dist/index.d.ts.map +1 -1
  222. package/dist/index.js +3 -2
  223. package/dist/index.js.map +1 -1
  224. package/dist/legacy-api-aliases.d.ts.map +1 -1
  225. package/dist/model-catalog.d.ts +25 -9
  226. package/dist/model-catalog.d.ts.map +1 -1
  227. package/dist/model-catalog.js +14 -2
  228. package/dist/model-catalog.js.map +1 -1
  229. package/dist/models-store.d.ts +12 -14
  230. package/dist/models-store.d.ts.map +1 -1
  231. package/dist/models-store.js +6 -3
  232. package/dist/models-store.js.map +1 -1
  233. package/dist/models.d.ts +138 -47
  234. package/dist/models.d.ts.map +1 -1
  235. package/dist/models.generated.d.ts +136 -38
  236. package/dist/models.generated.d.ts.map +1 -1
  237. package/dist/models.generated.js +136 -38
  238. package/dist/models.generated.js.map +1 -1
  239. package/dist/models.js +390 -104
  240. package/dist/models.js.map +1 -1
  241. package/dist/oauth.d.ts.map +1 -1
  242. package/dist/providers/all.d.ts +19 -13
  243. package/dist/providers/all.d.ts.map +1 -1
  244. package/dist/providers/all.js +33 -23
  245. package/dist/providers/all.js.map +1 -1
  246. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  247. package/dist/providers/amazon-bedrock.js +15 -7
  248. package/dist/providers/amazon-bedrock.js.map +1 -1
  249. package/dist/providers/amazon-bedrock.models.d.ts +4 -2
  250. package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  251. package/dist/providers/amazon-bedrock.models.js +4 -2
  252. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  253. package/dist/providers/ant-ling.d.ts.map +1 -1
  254. package/dist/providers/ant-ling.js.map +1 -1
  255. package/dist/providers/ant-ling.models.d.ts +4 -2
  256. package/dist/providers/ant-ling.models.d.ts.map +1 -1
  257. package/dist/providers/ant-ling.models.js +4 -2
  258. package/dist/providers/ant-ling.models.js.map +1 -1
  259. package/dist/providers/anthropic.d.ts.map +1 -1
  260. package/dist/providers/anthropic.js +39 -8
  261. package/dist/providers/anthropic.js.map +1 -1
  262. package/dist/providers/anthropic.models.d.ts +4 -2
  263. package/dist/providers/anthropic.models.d.ts.map +1 -1
  264. package/dist/providers/anthropic.models.js +4 -2
  265. package/dist/providers/anthropic.models.js.map +1 -1
  266. package/dist/providers/azure.d.ts +3 -0
  267. package/dist/providers/azure.d.ts.map +1 -0
  268. package/dist/providers/azure.js +46 -0
  269. package/dist/providers/azure.js.map +1 -0
  270. package/dist/providers/azure.models.d.ts +6 -0
  271. package/dist/providers/azure.models.d.ts.map +1 -0
  272. package/dist/providers/azure.models.js +8 -0
  273. package/dist/providers/azure.models.js.map +1 -0
  274. package/dist/providers/baseten.d.ts +3 -0
  275. package/dist/providers/baseten.d.ts.map +1 -0
  276. package/dist/providers/baseten.js +15 -0
  277. package/dist/providers/baseten.js.map +1 -0
  278. package/dist/providers/baseten.models.d.ts +6 -0
  279. package/dist/providers/baseten.models.d.ts.map +1 -0
  280. package/dist/providers/baseten.models.js +8 -0
  281. package/dist/providers/baseten.models.js.map +1 -0
  282. package/dist/providers/cerebras.d.ts.map +1 -1
  283. package/dist/providers/cerebras.js.map +1 -1
  284. package/dist/providers/cerebras.models.d.ts +4 -2
  285. package/dist/providers/cerebras.models.d.ts.map +1 -1
  286. package/dist/providers/cerebras.models.js +4 -2
  287. package/dist/providers/cerebras.models.js.map +1 -1
  288. package/dist/providers/cloudflare-ai-gateway.d.ts +3 -1
  289. package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -1
  290. package/dist/providers/cloudflare-ai-gateway.js +4 -0
  291. package/dist/providers/cloudflare-ai-gateway.js.map +1 -1
  292. package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -2
  293. package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  294. package/dist/providers/cloudflare-ai-gateway.models.js +4 -2
  295. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  296. package/dist/providers/cloudflare-auth.d.ts.map +1 -1
  297. package/dist/providers/cloudflare-auth.js +15 -10
  298. package/dist/providers/cloudflare-auth.js.map +1 -1
  299. package/dist/providers/cloudflare-stream.d.ts +6 -2
  300. package/dist/providers/cloudflare-stream.d.ts.map +1 -1
  301. package/dist/providers/cloudflare-stream.js +6 -0
  302. package/dist/providers/cloudflare-stream.js.map +1 -1
  303. package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -1
  304. package/dist/providers/cloudflare-workers-ai.js +10 -3
  305. package/dist/providers/cloudflare-workers-ai.js.map +1 -1
  306. package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -2
  307. package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -1
  308. package/dist/providers/cloudflare-workers-ai.models.js +4 -2
  309. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  310. package/dist/providers/data/.manifest.json +1 -1
  311. package/dist/providers/data/amazon-bedrock.json +1 -1
  312. package/dist/providers/data/ant-ling.json +1 -1
  313. package/dist/providers/data/anthropic.json +1 -1
  314. package/dist/providers/data/azure.json +1 -0
  315. package/dist/providers/data/baseten.json +1 -0
  316. package/dist/providers/data/cerebras.json +1 -1
  317. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  318. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  319. package/dist/providers/data/deepseek.json +1 -1
  320. package/dist/providers/data/fireworks.json +1 -1
  321. package/dist/providers/data/github-copilot.json +1 -1
  322. package/dist/providers/data/google-vertex.json +1 -1
  323. package/dist/providers/data/google.json +1 -1
  324. package/dist/providers/data/groq.json +1 -1
  325. package/dist/providers/data/huggingface.json +1 -1
  326. package/dist/providers/data/kimi-coding.json +1 -1
  327. package/dist/providers/data/meta.json +1 -0
  328. package/dist/providers/data/minimax-cn.json +1 -1
  329. package/dist/providers/data/minimax.json +1 -1
  330. package/dist/providers/data/mistral.json +1 -1
  331. package/dist/providers/data/moonshotai-cn.json +1 -1
  332. package/dist/providers/data/moonshotai.json +1 -1
  333. package/dist/providers/data/nvidia.json +1 -1
  334. package/dist/providers/data/openai-codex.json +1 -1
  335. package/dist/providers/data/openai.json +1 -1
  336. package/dist/providers/data/opencode-go.json +1 -1
  337. package/dist/providers/data/opencode.json +1 -1
  338. package/dist/providers/data/openrouter.json +1 -1
  339. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  340. package/dist/providers/data/qwen-token-plan-individual.json +1 -0
  341. package/dist/providers/data/qwen-token-plan.json +1 -1
  342. package/dist/providers/data/radius.json +1 -0
  343. package/dist/providers/data/together.json +1 -1
  344. package/dist/providers/data/typesafe.json +1 -0
  345. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  346. package/dist/providers/data/xai.json +1 -1
  347. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  348. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  349. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  350. package/dist/providers/data/xiaomi.json +1 -1
  351. package/dist/providers/data/zai-coding-cn.json +1 -1
  352. package/dist/providers/data/zai.json +1 -1
  353. package/dist/providers/deepseek.d.ts.map +1 -1
  354. package/dist/providers/deepseek.js.map +1 -1
  355. package/dist/providers/deepseek.models.d.ts +4 -2
  356. package/dist/providers/deepseek.models.d.ts.map +1 -1
  357. package/dist/providers/deepseek.models.js +4 -2
  358. package/dist/providers/deepseek.models.js.map +1 -1
  359. package/dist/providers/faux.d.ts +19 -13
  360. package/dist/providers/faux.d.ts.map +1 -1
  361. package/dist/providers/faux.js +109 -18
  362. package/dist/providers/faux.js.map +1 -1
  363. package/dist/providers/fireworks.d.ts.map +1 -1
  364. package/dist/providers/fireworks.js.map +1 -1
  365. package/dist/providers/fireworks.models.d.ts +4 -2
  366. package/dist/providers/fireworks.models.d.ts.map +1 -1
  367. package/dist/providers/fireworks.models.js +4 -2
  368. package/dist/providers/fireworks.models.js.map +1 -1
  369. package/dist/providers/github-copilot.d.ts.map +1 -1
  370. package/dist/providers/github-copilot.js +1 -1
  371. package/dist/providers/github-copilot.js.map +1 -1
  372. package/dist/providers/github-copilot.models.d.ts +4 -2
  373. package/dist/providers/github-copilot.models.d.ts.map +1 -1
  374. package/dist/providers/github-copilot.models.js +4 -2
  375. package/dist/providers/github-copilot.models.js.map +1 -1
  376. package/dist/providers/google-vertex.d.ts.map +1 -1
  377. package/dist/providers/google-vertex.js +15 -7
  378. package/dist/providers/google-vertex.js.map +1 -1
  379. package/dist/providers/google-vertex.models.d.ts +4 -2
  380. package/dist/providers/google-vertex.models.d.ts.map +1 -1
  381. package/dist/providers/google-vertex.models.js +4 -2
  382. package/dist/providers/google-vertex.models.js.map +1 -1
  383. package/dist/providers/google.d.ts.map +1 -1
  384. package/dist/providers/google.js.map +1 -1
  385. package/dist/providers/google.models.d.ts +4 -2
  386. package/dist/providers/google.models.d.ts.map +1 -1
  387. package/dist/providers/google.models.js +4 -2
  388. package/dist/providers/google.models.js.map +1 -1
  389. package/dist/providers/groq.d.ts.map +1 -1
  390. package/dist/providers/groq.js.map +1 -1
  391. package/dist/providers/groq.models.d.ts +4 -2
  392. package/dist/providers/groq.models.d.ts.map +1 -1
  393. package/dist/providers/groq.models.js +4 -2
  394. package/dist/providers/groq.models.js.map +1 -1
  395. package/dist/providers/huggingface.d.ts.map +1 -1
  396. package/dist/providers/huggingface.js.map +1 -1
  397. package/dist/providers/huggingface.models.d.ts +4 -2
  398. package/dist/providers/huggingface.models.d.ts.map +1 -1
  399. package/dist/providers/huggingface.models.js +4 -2
  400. package/dist/providers/huggingface.models.js.map +1 -1
  401. package/dist/providers/images/register-builtins.d.ts +1 -1
  402. package/dist/providers/images/register-builtins.d.ts.map +1 -1
  403. package/dist/providers/images/register-builtins.js.map +1 -1
  404. package/dist/providers/kimi-coding.d.ts.map +1 -1
  405. package/dist/providers/kimi-coding.js +1 -0
  406. package/dist/providers/kimi-coding.js.map +1 -1
  407. package/dist/providers/kimi-coding.models.d.ts +4 -2
  408. package/dist/providers/kimi-coding.models.d.ts.map +1 -1
  409. package/dist/providers/kimi-coding.models.js +4 -2
  410. package/dist/providers/kimi-coding.models.js.map +1 -1
  411. package/dist/providers/meta.d.ts +3 -0
  412. package/dist/providers/meta.d.ts.map +1 -0
  413. package/dist/providers/meta.js +24 -0
  414. package/dist/providers/meta.js.map +1 -0
  415. package/dist/providers/meta.models.d.ts +6 -0
  416. package/dist/providers/meta.models.d.ts.map +1 -0
  417. package/dist/providers/meta.models.js +8 -0
  418. package/dist/providers/meta.models.js.map +1 -0
  419. package/dist/providers/minimax-cn.d.ts.map +1 -1
  420. package/dist/providers/minimax-cn.js.map +1 -1
  421. package/dist/providers/minimax-cn.models.d.ts +4 -2
  422. package/dist/providers/minimax-cn.models.d.ts.map +1 -1
  423. package/dist/providers/minimax-cn.models.js +4 -2
  424. package/dist/providers/minimax-cn.models.js.map +1 -1
  425. package/dist/providers/minimax.d.ts.map +1 -1
  426. package/dist/providers/minimax.js.map +1 -1
  427. package/dist/providers/minimax.models.d.ts +4 -2
  428. package/dist/providers/minimax.models.d.ts.map +1 -1
  429. package/dist/providers/minimax.models.js +4 -2
  430. package/dist/providers/minimax.models.js.map +1 -1
  431. package/dist/providers/mistral.d.ts.map +1 -1
  432. package/dist/providers/mistral.js.map +1 -1
  433. package/dist/providers/mistral.models.d.ts +4 -2
  434. package/dist/providers/mistral.models.d.ts.map +1 -1
  435. package/dist/providers/mistral.models.js +4 -2
  436. package/dist/providers/mistral.models.js.map +1 -1
  437. package/dist/providers/moonshotai-cn.d.ts.map +1 -1
  438. package/dist/providers/moonshotai-cn.js.map +1 -1
  439. package/dist/providers/moonshotai-cn.models.d.ts +4 -2
  440. package/dist/providers/moonshotai-cn.models.d.ts.map +1 -1
  441. package/dist/providers/moonshotai-cn.models.js +4 -2
  442. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  443. package/dist/providers/moonshotai.d.ts.map +1 -1
  444. package/dist/providers/moonshotai.js.map +1 -1
  445. package/dist/providers/moonshotai.models.d.ts +4 -2
  446. package/dist/providers/moonshotai.models.d.ts.map +1 -1
  447. package/dist/providers/moonshotai.models.js +4 -2
  448. package/dist/providers/moonshotai.models.js.map +1 -1
  449. package/dist/providers/nvidia.d.ts.map +1 -1
  450. package/dist/providers/nvidia.js.map +1 -1
  451. package/dist/providers/nvidia.models.d.ts +4 -2
  452. package/dist/providers/nvidia.models.d.ts.map +1 -1
  453. package/dist/providers/nvidia.models.js +4 -2
  454. package/dist/providers/nvidia.models.js.map +1 -1
  455. package/dist/providers/openai-codex-catalog.d.ts +1 -0
  456. package/dist/providers/openai-codex-catalog.d.ts.map +1 -1
  457. package/dist/providers/openai-codex-catalog.js +19 -10
  458. package/dist/providers/openai-codex-catalog.js.map +1 -1
  459. package/dist/providers/openai-codex.d.ts.map +1 -1
  460. package/dist/providers/openai-codex.js +5 -1
  461. package/dist/providers/openai-codex.js.map +1 -1
  462. package/dist/providers/openai-codex.models.d.ts +4 -2
  463. package/dist/providers/openai-codex.models.d.ts.map +1 -1
  464. package/dist/providers/openai-codex.models.js +4 -2
  465. package/dist/providers/openai-codex.models.js.map +1 -1
  466. package/dist/providers/openai.d.ts.map +1 -1
  467. package/dist/providers/openai.js +11 -2
  468. package/dist/providers/openai.js.map +1 -1
  469. package/dist/providers/openai.models.d.ts +4 -2
  470. package/dist/providers/openai.models.d.ts.map +1 -1
  471. package/dist/providers/openai.models.js +4 -2
  472. package/dist/providers/openai.models.js.map +1 -1
  473. package/dist/providers/opencode-go.d.ts.map +1 -1
  474. package/dist/providers/opencode-go.js +5 -4
  475. package/dist/providers/opencode-go.js.map +1 -1
  476. package/dist/providers/opencode-go.models.d.ts +4 -2
  477. package/dist/providers/opencode-go.models.d.ts.map +1 -1
  478. package/dist/providers/opencode-go.models.js +4 -2
  479. package/dist/providers/opencode-go.models.js.map +1 -1
  480. package/dist/providers/opencode-headers.d.ts +4 -0
  481. package/dist/providers/opencode-headers.d.ts.map +1 -0
  482. package/dist/providers/opencode-headers.js +22 -0
  483. package/dist/providers/opencode-headers.js.map +1 -0
  484. package/dist/providers/opencode.d.ts +3 -1
  485. package/dist/providers/opencode.d.ts.map +1 -1
  486. package/dist/providers/opencode.js +10 -6
  487. package/dist/providers/opencode.js.map +1 -1
  488. package/dist/providers/opencode.models.d.ts +4 -2
  489. package/dist/providers/opencode.models.d.ts.map +1 -1
  490. package/dist/providers/opencode.models.js +4 -2
  491. package/dist/providers/opencode.models.js.map +1 -1
  492. package/dist/providers/openrouter.d.ts +1 -1
  493. package/dist/providers/openrouter.d.ts.map +1 -1
  494. package/dist/providers/openrouter.js +16 -3
  495. package/dist/providers/openrouter.js.map +1 -1
  496. package/dist/providers/openrouter.models.d.ts +4 -2
  497. package/dist/providers/openrouter.models.d.ts.map +1 -1
  498. package/dist/providers/openrouter.models.js +4 -2
  499. package/dist/providers/openrouter.models.js.map +1 -1
  500. package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -1
  501. package/dist/providers/qwen-token-plan-cn.js.map +1 -1
  502. package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -2
  503. package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -1
  504. package/dist/providers/qwen-token-plan-cn.models.js +4 -2
  505. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  506. package/dist/providers/qwen-token-plan-individual.d.ts +3 -0
  507. package/dist/providers/qwen-token-plan-individual.d.ts.map +1 -0
  508. package/dist/providers/qwen-token-plan-individual.js +15 -0
  509. package/dist/providers/qwen-token-plan-individual.js.map +1 -0
  510. package/dist/providers/qwen-token-plan-individual.models.d.ts +6 -0
  511. package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -0
  512. package/dist/providers/qwen-token-plan-individual.models.js +8 -0
  513. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -0
  514. package/dist/providers/qwen-token-plan.d.ts.map +1 -1
  515. package/dist/providers/qwen-token-plan.js.map +1 -1
  516. package/dist/providers/qwen-token-plan.models.d.ts +4 -2
  517. package/dist/providers/qwen-token-plan.models.d.ts.map +1 -1
  518. package/dist/providers/qwen-token-plan.models.js +4 -2
  519. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  520. package/dist/providers/radius-config.d.ts.map +1 -1
  521. package/dist/providers/radius-config.js.map +1 -1
  522. package/dist/providers/radius.d.ts.map +1 -1
  523. package/dist/providers/radius.js +53 -29
  524. package/dist/providers/radius.js.map +1 -1
  525. package/dist/providers/radius.models.d.ts +6 -0
  526. package/dist/providers/radius.models.d.ts.map +1 -0
  527. package/dist/providers/radius.models.js +8 -0
  528. package/dist/providers/radius.models.js.map +1 -0
  529. package/dist/providers/together.d.ts.map +1 -1
  530. package/dist/providers/together.js.map +1 -1
  531. package/dist/providers/together.models.d.ts +4 -2
  532. package/dist/providers/together.models.d.ts.map +1 -1
  533. package/dist/providers/together.models.js +4 -2
  534. package/dist/providers/together.models.js.map +1 -1
  535. package/dist/providers/typesafe.d.ts +3 -0
  536. package/dist/providers/typesafe.d.ts.map +1 -0
  537. package/dist/providers/typesafe.js +18 -0
  538. package/dist/providers/typesafe.js.map +1 -0
  539. package/dist/providers/typesafe.models.d.ts +6 -0
  540. package/dist/providers/typesafe.models.d.ts.map +1 -0
  541. package/dist/providers/typesafe.models.js +8 -0
  542. package/dist/providers/typesafe.models.js.map +1 -0
  543. package/dist/providers/vercel-ai-gateway.d.ts.map +1 -1
  544. package/dist/providers/vercel-ai-gateway.js +5 -2
  545. package/dist/providers/vercel-ai-gateway.js.map +1 -1
  546. package/dist/providers/vercel-ai-gateway.models.d.ts +4 -2
  547. package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  548. package/dist/providers/vercel-ai-gateway.models.js +4 -2
  549. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  550. package/dist/providers/xai.d.ts +1 -1
  551. package/dist/providers/xai.d.ts.map +1 -1
  552. package/dist/providers/xai.js +2 -5
  553. package/dist/providers/xai.js.map +1 -1
  554. package/dist/providers/xai.models.d.ts +4 -2
  555. package/dist/providers/xai.models.d.ts.map +1 -1
  556. package/dist/providers/xai.models.js +4 -2
  557. package/dist/providers/xai.models.js.map +1 -1
  558. package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -1
  559. package/dist/providers/xiaomi-token-plan-ams.js.map +1 -1
  560. package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -2
  561. package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
  562. package/dist/providers/xiaomi-token-plan-ams.models.js +4 -2
  563. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  564. package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -1
  565. package/dist/providers/xiaomi-token-plan-cn.js.map +1 -1
  566. package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -2
  567. package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
  568. package/dist/providers/xiaomi-token-plan-cn.models.js +4 -2
  569. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  570. package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -1
  571. package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -1
  572. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -2
  573. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
  574. package/dist/providers/xiaomi-token-plan-sgp.models.js +4 -2
  575. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  576. package/dist/providers/xiaomi.d.ts.map +1 -1
  577. package/dist/providers/xiaomi.js.map +1 -1
  578. package/dist/providers/xiaomi.models.d.ts +4 -2
  579. package/dist/providers/xiaomi.models.d.ts.map +1 -1
  580. package/dist/providers/xiaomi.models.js +4 -2
  581. package/dist/providers/xiaomi.models.js.map +1 -1
  582. package/dist/providers/zai-coding-cn.d.ts.map +1 -1
  583. package/dist/providers/zai-coding-cn.js.map +1 -1
  584. package/dist/providers/zai-coding-cn.models.d.ts +4 -2
  585. package/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
  586. package/dist/providers/zai-coding-cn.models.js +4 -2
  587. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  588. package/dist/providers/zai.d.ts.map +1 -1
  589. package/dist/providers/zai.js.map +1 -1
  590. package/dist/providers/zai.models.d.ts +4 -2
  591. package/dist/providers/zai.models.d.ts.map +1 -1
  592. package/dist/providers/zai.models.js +4 -2
  593. package/dist/providers/zai.models.js.map +1 -1
  594. package/dist/session-resources.d.ts.map +1 -1
  595. package/dist/session-resources.js.map +1 -1
  596. package/dist/types.d.ts +414 -126
  597. package/dist/types.d.ts.map +1 -1
  598. package/dist/types.js.map +1 -1
  599. package/dist/utils/abort-signals.d.ts.map +1 -1
  600. package/dist/utils/abort-signals.js.map +1 -1
  601. package/dist/utils/abort.d.ts +8 -0
  602. package/dist/utils/abort.d.ts.map +1 -0
  603. package/dist/utils/abort.js +49 -0
  604. package/dist/utils/abort.js.map +1 -0
  605. package/dist/utils/assistant-message-frame.d.ts +77 -0
  606. package/dist/utils/assistant-message-frame.d.ts.map +1 -0
  607. package/dist/utils/assistant-message-frame.js +425 -0
  608. package/dist/utils/assistant-message-frame.js.map +1 -0
  609. package/dist/utils/diagnostics.d.ts +3 -2
  610. package/dist/utils/diagnostics.d.ts.map +1 -1
  611. package/dist/utils/diagnostics.js.map +1 -1
  612. package/dist/utils/error-body.d.ts.map +1 -1
  613. package/dist/utils/error-body.js +23 -4
  614. package/dist/utils/error-body.js.map +1 -1
  615. package/dist/utils/estimate.d.ts +2 -2
  616. package/dist/utils/estimate.d.ts.map +1 -1
  617. package/dist/utils/estimate.js +8 -29
  618. package/dist/utils/estimate.js.map +1 -1
  619. package/dist/utils/event-stream.d.ts.map +1 -1
  620. package/dist/utils/event-stream.js +25 -7
  621. package/dist/utils/event-stream.js.map +1 -1
  622. package/dist/utils/hash.d.ts.map +1 -1
  623. package/dist/utils/hash.js.map +1 -1
  624. package/dist/utils/headers.d.ts +1 -1
  625. package/dist/utils/headers.d.ts.map +1 -1
  626. package/dist/utils/headers.js +10 -8
  627. package/dist/utils/headers.js.map +1 -1
  628. package/dist/utils/json-parse.d.ts.map +1 -1
  629. package/dist/utils/json-parse.js.map +1 -1
  630. package/dist/utils/model-operations.d.ts +11 -0
  631. package/dist/utils/model-operations.d.ts.map +1 -0
  632. package/dist/utils/model-operations.js +47 -0
  633. package/dist/utils/model-operations.js.map +1 -0
  634. package/dist/utils/models-error.d.ts +8 -0
  635. package/dist/utils/models-error.d.ts.map +1 -0
  636. package/dist/utils/models-error.js +19 -0
  637. package/dist/utils/models-error.js.map +1 -0
  638. package/dist/utils/node-http-proxy.d.ts.map +1 -1
  639. package/dist/utils/node-http-proxy.js +53 -12
  640. package/dist/utils/node-http-proxy.js.map +1 -1
  641. package/dist/utils/oauth-page.d.ts.map +1 -0
  642. package/dist/{auth/oauth → utils}/oauth-page.js +1 -1
  643. package/dist/utils/oauth-page.js.map +1 -0
  644. package/dist/utils/overflow.d.ts +11 -1
  645. package/dist/utils/overflow.d.ts.map +1 -1
  646. package/dist/utils/overflow.js +24 -6
  647. package/dist/utils/overflow.js.map +1 -1
  648. package/dist/utils/pi-user-agent.d.ts +2 -0
  649. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  650. package/dist/utils/pi-user-agent.js +12 -0
  651. package/dist/utils/pi-user-agent.js.map +1 -0
  652. package/dist/utils/provider-env.d.ts.map +1 -1
  653. package/dist/utils/provider-env.js.map +1 -1
  654. package/dist/utils/provider-retry.d.ts.map +1 -1
  655. package/dist/utils/provider-retry.js +3 -2
  656. package/dist/utils/provider-retry.js.map +1 -1
  657. package/dist/utils/retry.d.ts +6 -1
  658. package/dist/utils/retry.d.ts.map +1 -1
  659. package/dist/utils/retry.js +23 -2
  660. package/dist/utils/retry.js.map +1 -1
  661. package/dist/utils/sanitize-unicode.d.ts.map +1 -1
  662. package/dist/utils/sanitize-unicode.js.map +1 -1
  663. package/dist/utils/sleep.d.ts +2 -0
  664. package/dist/utils/sleep.d.ts.map +1 -0
  665. package/dist/utils/sleep.js +15 -0
  666. package/dist/utils/sleep.js.map +1 -0
  667. package/dist/utils/text.d.ts +9 -1
  668. package/dist/utils/text.d.ts.map +1 -1
  669. package/dist/utils/text.js +26 -0
  670. package/dist/utils/text.js.map +1 -1
  671. package/dist/utils/transcript.d.ts +87 -0
  672. package/dist/utils/transcript.d.ts.map +1 -0
  673. package/dist/utils/transcript.js +207 -0
  674. package/dist/utils/transcript.js.map +1 -0
  675. package/dist/utils/typebox-helpers.d.ts.map +1 -1
  676. package/dist/utils/typebox-helpers.js.map +1 -1
  677. package/dist/utils/uuid.d.ts +2 -2
  678. package/dist/utils/uuid.d.ts.map +1 -1
  679. package/dist/utils/uuid.js +35 -39
  680. package/dist/utils/uuid.js.map +1 -1
  681. package/dist/utils/validation.d.ts.map +1 -1
  682. package/dist/utils/validation.js +40 -0
  683. package/dist/utils/validation.js.map +1 -1
  684. package/package.json +21 -15
  685. package/dist/auth/oauth/oauth-page.d.ts.map +0 -1
  686. package/dist/auth/oauth/oauth-page.js.map +0 -1
  687. package/dist/image-models.generated.d.ts +0 -605
  688. package/dist/image-models.generated.d.ts.map +0 -1
  689. package/dist/image-models.generated.js +0 -607
  690. package/dist/image-models.generated.js.map +0 -1
  691. package/dist/images-models.d.ts +0 -95
  692. package/dist/images-models.d.ts.map +0 -1
  693. package/dist/images-models.js +0 -142
  694. package/dist/images-models.js.map +0 -1
  695. package/dist/providers/azure-openai-responses.d.ts +0 -3
  696. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  697. package/dist/providers/azure-openai-responses.js +0 -14
  698. package/dist/providers/azure-openai-responses.js.map +0 -1
  699. package/dist/providers/azure-openai-responses.models.d.ts +0 -4
  700. package/dist/providers/azure-openai-responses.models.d.ts.map +0 -1
  701. package/dist/providers/azure-openai-responses.models.js +0 -6
  702. package/dist/providers/azure-openai-responses.models.js.map +0 -1
  703. package/dist/providers/data/azure-openai-responses.json +0 -1
  704. package/dist/providers/openrouter-images.d.ts +0 -3
  705. package/dist/providers/openrouter-images.d.ts.map +0 -1
  706. package/dist/providers/openrouter-images.js +0 -22
  707. package/dist/providers/openrouter-images.js.map +0 -1
  708. package/dist/utils/deferred-tools.d.ts +0 -9
  709. package/dist/utils/deferred-tools.d.ts.map +0 -1
  710. package/dist/utils/deferred-tools.js +0 -36
  711. package/dist/utils/deferred-tools.js.map +0 -1
  712. /package/dist/{auth/oauth → utils}/oauth-page.d.ts +0 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Unified LLM API with provider collections, automatic auth resolution, token and cost tracking, and simple context persistence and hand-off to other models mid-session.
4
4
 
5
- **Note**: This library only includes models that support tool calling (function calling), as this is essential for agentic workflows.
5
+ **Note**: The chat catalog only includes models that support tool calling (function calling), as this is essential for agentic workflows. Image and classifier catalogs use their operation-specific capabilities.
6
6
 
7
7
  ## Table of Contents
8
8
 
@@ -26,8 +26,10 @@ Unified LLM API with provider collections, automatic auth resolution, token and
26
26
  - [Streaming Tool Calls with Partial JSON](#streaming-tool-calls-with-partial-json)
27
27
  - [Validating Tool Arguments](#validating-tool-arguments)
28
28
  - [Complete Event Reference](#complete-event-reference)
29
+ - [Compact Assistant Message Frames](#compact-assistant-message-frames)
29
30
  - [Image Input](#image-input)
30
31
  - [Image Generation](#image-generation)
32
+ - [Classification](#classification)
31
33
  - [Thinking/Reasoning](#thinkingreasoning)
32
34
  - [Unified Interface](#unified-interface-streamsimplecompletesimple)
33
35
  - [Provider-Specific Options](#provider-specific-options-streamcomplete)
@@ -37,12 +39,14 @@ Unified LLM API with provider collections, automatic auth resolution, token and
37
39
  - [Aborting Requests](#aborting-requests)
38
40
  - [Continuing After Abort](#continuing-after-abort)
39
41
  - [Debugging Provider Payloads](#debugging-provider-payloads)
42
+ - [Observing Provider Stream Events](#observing-provider-stream-events)
40
43
  - [Custom Providers](#custom-providers)
41
44
  - [createProvider()](#createprovider)
42
45
  - [Calling API Implementations Directly](#calling-api-implementations-directly)
43
46
  - [OpenAI Compatibility Settings](#openai-compatibility-settings)
44
47
  - [Faux Provider for Tests](#faux-provider-for-tests)
45
48
  - [Cross-Provider Handoffs](#cross-provider-handoffs)
49
+ - [System Messages](#system-messages)
46
50
  - [Context Serialization](#context-serialization)
47
51
  - [Browser Usage](#browser-usage)
48
52
  - [Bundling and Tree Shaking](#bundling-and-tree-shaking)
@@ -59,7 +63,9 @@ Unified LLM API with provider collections, automatic auth resolution, token and
59
63
  - **OpenAI**
60
64
  - **Ant Ling**
61
65
  - **Azure OpenAI (Responses)**
62
- - **OpenAI Codex** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
66
+ - **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
67
+ - **Radius** (API key or OAuth, with a dynamically refreshed gateway catalog)
68
+ - **TypeSafe** (System One classifier API)
63
69
  - **DeepSeek**
64
70
  - **NVIDIA NIM**
65
71
  - **Anthropic**
@@ -76,6 +82,7 @@ Unified LLM API with provider collections, automatic auth resolution, token and
76
82
  - **ZAI Coding Plan (Global)** (with separate China provider)
77
83
  - **MiniMax** (with separate China provider)
78
84
  - **Together AI**
85
+ - **Baseten**
79
86
  - **Hugging Face**
80
87
  - **Moonshot AI** (with separate China provider)
81
88
  - **GitHub Copilot** (requires OAuth, see below)
@@ -84,6 +91,8 @@ Unified LLM API with provider collections, automatic auth resolution, token and
84
91
  - **OpenCode Go**
85
92
  - **Fireworks** (uses OpenAI- and Anthropic-compatible APIs)
86
93
  - **Kimi For Coding** (Moonshot AI subscription endpoint, uses Anthropic-compatible API)
94
+ - **Meta** (Model API, uses OpenAI Responses-compatible API)
95
+ - **Qwen Token Plan** (separate Individual and existing catalogs, with a separate China provider)
87
96
  - **Xiaomi MiMo** (defaults to API billing endpoint, with separate Token Plan providers for `cn`/`ams`/`sgp` regions)
88
97
  - **Any OpenAI-compatible API**: Ollama, vLLM, LM Studio, etc.
89
98
 
@@ -269,7 +278,7 @@ Reads are synchronous and return the last-known lists:
269
278
  const providers = models.getProviders(); // registered Provider objects
270
279
  const provider = models.getProvider('anthropic'); // one provider
271
280
 
272
- const all = models.getModels(); // every model across providers
281
+ const all = models.getModels(); // every chat model across providers
273
282
  const anthropicModels = models.getModels('anthropic');
274
283
  const model = models.getModel('anthropic', 'claude-sonnet-4-5');
275
284
 
@@ -282,7 +291,31 @@ for (const m of anthropicModels) {
282
291
  }
283
292
  ```
284
293
 
285
- Dynamically listed models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
294
+ The unqualified reads `getModels()`/`getModel()`/`getAvailable()` return chat models (`Model<Api>`) usable with `stream()`. The `*OfType` reads return one model type, and `getAllModels()`/`getAllAvailable()` return every type as `AnyModel`:
295
+
296
+ ```typescript
297
+ const images = models.getModelsOfType('image', 'openrouter'); // ImageModel[]
298
+ const flux = models.getModelOfType('image', 'openrouter', 'black-forest-labs/flux.2-pro');
299
+ const jev = models.getModelOfType('classifier', 'typesafe', 'jev-latest');
300
+ const availableImages = await models.getAvailableOfType('image');
301
+ const everything = models.getAllModels(); // AnyModel[]
302
+ ```
303
+
304
+ The model's `type` decides which operation accepts it: chat models stream, `type: "image"` models generate images, and `type: "classifier"` models classify structured state. `type` is optional on chat models, so a model without `type` is a chat model. Do not compare `type` directly; narrow mixed lists with `isModelType()` or read the effective type with `getModelType()`:
305
+
306
+ ```typescript
307
+ import { isModelType } from '@earendil-works/pi-ai';
308
+
309
+ for (const model of models.getAllModels()) {
310
+ if (isModelType(model, 'image')) {
311
+ // model: ImageModel<ImageApi>
312
+ }
313
+ }
314
+ ```
315
+
316
+ IDs are unique within each provider and type; one upstream model may have separate entries for different operations. On a provider, `getModels()` returns chat models and the optional `getAllModels()` returns every type; providers with only chat models can omit it.
317
+
318
+ Dynamically listed chat models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
286
319
 
287
320
  ```typescript
288
321
  import { hasApi } from '@earendil-works/pi-ai';
@@ -299,11 +332,26 @@ if (m && hasApi(m, 'anthropic-messages')) {
299
332
  For tooling that wants the generated built-in catalog with full literal typing (provider and model IDs auto-complete), independent of any collection:
300
333
 
301
334
  ```typescript
302
- import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from '@earendil-works/pi-ai/providers/all';
335
+ import {
336
+ getAllBuiltinModels,
337
+ getBuiltinClassifierModel,
338
+ getBuiltinClassifierModels,
339
+ getBuiltinImageModel,
340
+ getBuiltinImageModels,
341
+ getBuiltinModel,
342
+ getBuiltinModels,
343
+ getBuiltinProviders,
344
+ } from '@earendil-works/pi-ai/providers/all';
303
345
 
304
346
  const model = getBuiltinModel('openai', 'gpt-4o-mini'); // typed Model<'openai-responses'>
347
+ const radius = getBuiltinModel('radius', 'balanced'); // typed Model<'pi-messages'>
348
+ const flux = getBuiltinImageModel('openrouter', 'black-forest-labs/flux.2-pro');
349
+ const jev = getBuiltinClassifierModel('typesafe', 'jev-latest');
305
350
  const providers = getBuiltinProviders();
306
- const anthropic = getBuiltinModels('anthropic');
351
+ const openrouterChat = getBuiltinModels('openrouter'); // Model[]
352
+ const openrouterImages = getBuiltinImageModels('openrouter'); // ImageModel[]
353
+ const typesafeClassifiers = getBuiltinClassifierModels('typesafe'); // ClassifierModel[]
354
+ const openrouterAll = getAllBuiltinModels('openrouter'); // AnyModel[]
307
355
  ```
308
356
 
309
357
  ### Dynamic Providers
@@ -312,12 +360,12 @@ Providers may have dynamic model lists (a llama.cpp server, a live OpenRouter li
312
360
 
313
361
  ```typescript
314
362
  // getModels() returns the last-known list (empty before the first refresh)
315
- await models.refresh('llamacpp'); // fetch one provider's list; rejects on failure
316
- await models.refresh(); // refresh all providers concurrently, best-effort
363
+ await models.refresh({ providers: ['llamacpp'] }); // refresh one provider
364
+ await models.refresh(); // refresh all providers concurrently, best-effort
317
365
  const fresh = models.getModel('llamacpp', 'qwen3-30b');
318
366
  ```
319
367
 
320
- Static built-in providers are no-ops for `refresh()`. See [createProvider()](#createprovider) for building a dynamic provider.
368
+ Static built-in providers are no-ops for `refresh()`. Radius is both static and dynamic: it ships the public `radius.pi.dev` catalog for synchronous API lookup, then overlays cached and freshly fetched `/v1/config` models when refreshed with configured auth. See [createProvider()](#createprovider) for building a dynamic provider.
321
369
 
322
370
  ## Auth
323
371
 
@@ -351,6 +399,8 @@ if (modelAuth) {
351
399
 
352
400
  Both overloads resolve credentials, refresh expired OAuth when necessary, and may return an auth-derived `apiKey`, `headers`, or `baseUrl`. `getAuth()` resolves `undefined` for unconfigured providers and rejects with `ModelsError` when something is actually broken (`"oauth"`: token refresh failed, credential preserved for re-login; `"auth"`: key resolution or credential store failure). Request paths surface the same failures as stream errors.
353
401
 
402
+ `getAuth()`, `checkAuth()`, `getAvailable()`, login, and logout accept optional caller cancellation through their existing options or interaction objects and remain unbounded when no signal is supplied. Provider `login`, `ApiKeyAuth.check`, `ApiKeyAuth.resolve`, and `OAuthAuth.refresh` implementations always receive a concrete signal and must honor it for blocking work.
403
+
354
404
  ### Transforming Request Headers
355
405
 
356
406
  `Models.stream()`, `complete()`, `streamSimple()`, and `completeSimple()` accept a Models-only `transformHeaders` option. It runs once after provider auth, `model.headers`, and explicit `options.headers` have been merged, but before provider dispatch:
@@ -387,7 +437,7 @@ const models = createModels({ credentials: myFileBackedStore });
387
437
  // const models = builtinModels({ credentials: myFileBackedStore });
388
438
  ```
389
439
 
390
- The contract is small: `read(providerId)`, `list()` for non-secret `{ providerId, type }` metadata, `modify(providerId, fn)` (the only write path — a serialized read-modify-write), and `delete(providerId)`. Enumeration must not resolve secrets or execute configured key commands. OAuth token refresh runs inside `modify`, so concurrent requests and processes cannot double-refresh a rotated token. A stored credential *owns* its provider: environment variables are only consulted when nothing is stored, and a failed refresh never silently falls back to an env key.
440
+ The contract is small: `read(providerId)`, `list()` for non-secret `{ providerId, type }` metadata, `modify(providerId, fn)` (the only write path — a serialized read-modify-write), and `delete(providerId)`. Each operation accepts optional cancellation options. Enumeration must not resolve secrets or execute configured key commands. OAuth token refresh runs inside `modify`, so concurrent requests and processes cannot double-refresh a rotated token. A stored credential *owns* its provider: environment variables are only consulted when nothing is stored, and a failed refresh never silently falls back to an env key.
391
441
 
392
442
  API-key credentials use the same discriminator as pi's `auth.json` and can carry provider-scoped env/config values:
393
443
 
@@ -412,6 +462,8 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
412
462
  | Ant Ling | `ANT_LING_API_KEY` |
413
463
  | Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` (e.g. `https://{resource}.ai.azure.com`) or `AZURE_OPENAI_RESOURCE_NAME`. Supports `*.openai.azure.com`, `*.cognitiveservices.azure.com` and `*.ai.azure.com`; root endpoints auto-normalize to `/openai/v1`. Optional: `AZURE_OPENAI_API_VERSION` (default `v1`), `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`. |
414
464
  | Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
465
+ | Radius | `RADIUS_API_KEY` |
466
+ | TypeSafe | `TYPESAFE_API_KEY` |
415
467
  | DeepSeek | `DEEPSEEK_API_KEY` |
416
468
  | NVIDIA NIM | `NVIDIA_API_KEY` |
417
469
  | Google | `GEMINI_API_KEY` |
@@ -424,6 +476,7 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
424
476
  | xAI | `XAI_API_KEY` |
425
477
  | Fireworks | `FIREWORKS_API_KEY` |
426
478
  | Together AI | `TOGETHER_API_KEY` |
479
+ | Baseten | `BASETEN_API_KEY` |
427
480
  | OpenRouter | `OPENROUTER_API_KEY` |
428
481
  | Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
429
482
  | ZAI Coding Plan (Global) | `ZAI_API_KEY` |
@@ -434,7 +487,9 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
434
487
  | Hugging Face | `HF_TOKEN` |
435
488
  | OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
436
489
  | Kimi For Coding | `KIMI_API_KEY` |
437
- | Qwen Token Plan | `QWEN_TOKEN_PLAN_API_KEY` |
490
+ | Meta | `META_API_KEY` |
491
+ | Qwen Token Plan (existing catalog) | `QWEN_TOKEN_PLAN_API_KEY` |
492
+ | Qwen Token Plan (Individual) | `QWEN_TOKEN_PLAN_API_KEY` |
438
493
  | Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` |
439
494
  | Xiaomi MiMo (API billing) | `XIAOMI_API_KEY` |
440
495
  | Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` |
@@ -442,6 +497,11 @@ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` ex
442
497
  | Xiaomi MiMo Token Plan (Singapore) | `XIAOMI_TOKEN_PLAN_SGP_API_KEY` |
443
498
  | GitHub Copilot | `COPILOT_GITHUB_TOKEN` |
444
499
 
500
+ `qwen-token-plan-individual` and `qwen-token-plan` share the international endpoint and
501
+ `QWEN_TOKEN_PLAN_API_KEY`. The Individual provider exposes only the models documented for Individual
502
+ subscriptions, while the existing provider retains its broader catalog for backward compatibility.
503
+ Stored credentials remain provider-scoped, so save the key under the provider ID you register.
504
+
445
505
  Amazon Bedrock resolves ambient AWS credentials (`AWS_PROFILE`, access key pairs, `AWS_BEARER_TOKEN_BEDROCK`, ECS task roles, web identity tokens); its provider-owned login flow supports bearer tokens, AWS profiles, and the existing credential chain. Vertex AI resolves either an explicit key or gcloud Application Default Credentials plus project/location, with a provider-owned login flow for API keys, ADC, and service-account files.
446
506
 
447
507
  ## Tools
@@ -640,6 +700,10 @@ for await (const event of s) {
640
700
 
641
701
  ### Complete Event Reference
642
702
 
703
+ Successful generation follows `start → updates* → done`. A failure after generation starts follows `start → updates* → error`. Request setup may fail before generation starts, in which case the stream contains only `error`; `done` and update events are invalid before `start`. Direct API `streamSimple()` calls throw synchronously when request auth is missing.
704
+
705
+ Every non-terminal event's `partial` is the shared live response-so-far helper. It is intentionally not an event-time snapshot: providers may mutate the same message and content blocks as generation advances, including while older events wait in the stream queue. Inspect it when handling an event instead of retaining it as historical state. Text and ordinary thinking blocks are empty when their `*_start` event is emitted and grow only through matching `*_delta` events until the authoritative `*_end`; redacted thinking may be complete at start and emit no deltas. Tool-call arguments at `toolcall_start` are provider-specific; `toolcall_delta` carries subsequent JSON updates.
706
+
643
707
  All streaming events emitted during assistant message generation:
644
708
 
645
709
  | Event Type | Description | Key Properties |
@@ -653,12 +717,40 @@ All streaming events emitted during assistant message generation:
653
717
  | `thinking_end` | Thinking block complete | `content`: Full thinking, `contentIndex`: Position |
654
718
  | `toolcall_start` | Tool call begins | `contentIndex`: Position in content array |
655
719
  | `toolcall_delta` | Tool arguments streaming | `delta`: JSON chunk, `partial.content[contentIndex].arguments`: Partial parsed args |
656
- | `toolcall_end` | Tool call complete | `toolCall`: Complete validated tool call with `id`, `name`, `arguments` |
720
+ | `toolcall_end` | Tool call complete | `toolCall`: Complete, but not schema-validated, tool call with `id`, `name`, `arguments` |
657
721
  | `done` | Stream complete | `reason`: Stop reason ("stop", "length", "toolUse"), `message`: Final assistant message |
658
722
  | `error` | Error occurred | `reason`: Error type ("error" or "aborted"), `error`: AssistantMessage with partial content |
659
723
 
660
724
  Streaming events for different content blocks are not guaranteed to be contiguous. Providers may emit deltas for text, thinking, and tool calls in the same upstream chunk, and pi may surface corresponding events interleaved, for example `text_start`, `text_delta`, `toolcall_start`, `text_delta`, `toolcall_delta`. Consumers must use `contentIndex` to associate each delta/end event with its block and must not assume that a block's `*_start`/`*_delta`/`*_end` sequence is uninterrupted by events for other blocks.
661
725
 
726
+ ### Compact Assistant Message Frames
727
+
728
+ `AssistantMessageFrameEncoder` converts one stream into compact, persistable `AssistantMessageFrame` values. Create one encoder per stream and feed it every event in order. The encoder understands that `partial` is live: a block-start event consumed after the provider has already queued later deltas snapshots the current block once, and covered queued text/thinking deltas produce no duplicate frame. It retains only per-open-block counters plus, temporarily, the raw prefix needed to synchronize an already-advanced tool call. It never clones the growing full partial per token.
729
+
730
+ The start frame contains message metadata with empty content. Text and thinking frames store each generated character at most once before the authoritative end frame. Tool calls that were already advanced when their start event was consumed use one compact JSON checkpoint before ordinary deltas resume. Terminal `done` and `error` events produce no frame because final message settlement is separate. A pre-generation `error` therefore produces no frames.
731
+
732
+ `reduceAssistantMessageFrames()` is the canonical pure reducer. It reconstructs text, thinking, and tool-call arguments, including interleaved blocks identified by `contentIndex`, and rejects malformed sequences. It performs a single pass over the iterable and returns `undefined` when there is no start frame. End frames replace blocks with the provider's authoritative completed content and metadata. The reducer does not validate tool arguments against a TypeBox schema; call `validateToolCall` before execution.
733
+
734
+ ```typescript
735
+ import {
736
+ AssistantMessageFrameEncoder,
737
+ reduceAssistantMessageFrames,
738
+ type AssistantMessageFrame,
739
+ } from '@earendil-works/pi-ai';
740
+
741
+ const encoder = new AssistantMessageFrameEncoder();
742
+ const frames: AssistantMessageFrame[] = [];
743
+ for await (const event of s) {
744
+ const frame = encoder.encode(event);
745
+ if (frame) frames.push(frame);
746
+ }
747
+
748
+ const reconstructedPartial = reduceAssistantMessageFrames(frames);
749
+ const finalMessage = await s.result(); // Persist terminal settlement separately.
750
+ ```
751
+
752
+ An encoder rejects duplicate starts, updates before start, `done` before start, events after a terminal event, duplicate block starts, and block-kind mismatches. An `error` before start is valid and returns no frame.
753
+
662
754
  ## Image Input
663
755
 
664
756
  Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
@@ -697,20 +789,19 @@ for (const block of response.content) {
697
789
 
698
790
  ## Image Generation
699
791
 
700
- Image generation uses a separate API surface from text/chat generation, mirroring the chat-side design: an `ImagesModels` collection holds `ImagesProvider`s, reads are sync, and auth resolves through the owning provider. Image generation is a one-shot API: `generateImages()` waits for the provider response and returns the final `AssistantImages` result — do not use the chat/stream APIs for it.
792
+ Image models live in the same `Models` collection and on the same `Provider` as chat models, so one credential per provider covers both. They are typed `ImageModel` with `type: "image"` and are used through `generateImages()`, a one-shot API that waits for the provider response and returns the final `AssistantImages` result. Do not use the chat/stream APIs for them; `stream()` rejects image models.
701
793
 
702
794
  ### Basic Image Generation
703
795
 
704
796
  ```typescript
705
- import { builtinImagesModels } from '@earendil-works/pi-ai/providers/all';
797
+ import { builtinModels } from '@earendil-works/pi-ai/providers/all';
706
798
 
707
- // Every built-in image-generation provider; accepts the same options as createModels()
708
- const imagesModels = builtinImagesModels();
799
+ const models = builtinModels();
709
800
 
710
- const model = imagesModels.getModel('openrouter', 'google/gemini-2.5-flash-image')!;
801
+ const model = models.getModelOfType('image', 'openrouter', 'google/gemini-2.5-flash-image')!;
711
802
 
712
803
  // Auth resolves through the provider (OPENROUTER_API_KEY here); explicit apiKey wins
713
- const result = await imagesModels.generateImages(model, {
804
+ const result = await models.generateImages(model, {
714
805
  input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
715
806
  });
716
807
 
@@ -724,7 +815,33 @@ for (const block of result.output) {
724
815
  }
725
816
  ```
726
817
 
727
- Like the chat side, you can build the collection from parts: `createImagesModels({ credentials?, authContext? })`, the `openrouterImagesProvider()` factory from `@earendil-works/pi-ai/providers/openrouter-images`, and `createImagesProvider({ id, auth, models, refreshModels?, api })` for custom image providers (with `imagesModels.refresh(provider?)` for dynamic lists). Failures never reject — they return an `AssistantImages` with `stopReason: "error"`. The collection's provider-scoped `getAuth(providerId)` works exactly like the chat-side one.
818
+ `generateImages()` accepts only `ImageModel` values. If an upstream model supports both chat and image generation, the catalog contains separate entries with the same provider and ID: `getModel()` returns its chat operation and `getModelOfType('image', ...)` returns its image operation. Failures never reject; they return an `AssistantImages` with `stopReason: "error"`, including unknown providers, unconfigured auth, and providers without an image implementation.
819
+
820
+ A provider declares image support with the `images` option of [`createProvider()`](#createprovider): a map from `model.api` to an implementation with `generateImages()`. Image models go into the same `models` list as chat models. `api` becomes optional when `images` is present, so an image-only provider is just a provider without chat models:
821
+
822
+ ```typescript
823
+ import { createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
824
+
825
+ const pixels = createProvider({
826
+ id: 'pixels',
827
+ auth: { apiKey: envApiKeyAuth('Pixels API key', ['PIXELS_API_KEY']) },
828
+ models: [{
829
+ type: 'image',
830
+ id: 'flux-pro',
831
+ name: 'FLUX Pro',
832
+ api: 'pixels-images',
833
+ provider: 'pixels',
834
+ baseUrl: 'https://api.pixels.test/v1',
835
+ input: ['text'],
836
+ output: ['image'],
837
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
838
+ }],
839
+ images: {
840
+ 'pixels-images': { generateImages: async (model, context, options) => { /* ... */ } },
841
+ },
842
+ });
843
+ models.setProvider(pixels);
844
+ ```
728
845
 
729
846
  The old global API (`getImageModel()` / `getImageModels()` / `getImageProviders()` / `generateImages()`) remains available on the [compat entrypoint](#migrating-from-the-old-global-api):
730
847
 
@@ -745,7 +862,7 @@ Some models also support image input:
745
862
  import { readFileSync } from 'fs';
746
863
 
747
864
  const imageBuffer = readFileSync('input.png');
748
- const result = await imagesModels.generateImages(model, {
865
+ const result = await models.generateImages(model, {
749
866
  input: [
750
867
  { type: 'text', text: 'Create a variation of this image with a blue background.' },
751
868
  { type: 'image', data: imageBuffer.toString('base64'), mimeType: 'image/png' }
@@ -756,14 +873,13 @@ const result = await imagesModels.generateImages(model, {
756
873
  Check capabilities on the model metadata:
757
874
 
758
875
  ```typescript
759
- console.log(model.input); // ['text', 'image']
760
- console.log(model.output); // ['image'] or ['image', 'text']
876
+ console.log(model.input); // ['text'] or ['text', 'image']
877
+ console.log(model.output); // ['image'] or ['image', 'text']
761
878
  ```
762
879
 
763
880
  ### Notes and Limitations
764
881
 
765
- - Image models live in `ImagesModels` collections, chat models in `Models` collections; the two are separate surfaces.
766
- - Use `generateImages()`, not the chat/stream APIs.
882
+ - Image models and chat models share `Models` and `Provider`; list them with `getModelsOfType('image')` and run them with `generateImages()`, never the chat/stream APIs.
767
883
  - Image-generation models do not participate in tool calling.
768
884
  - Outputs are returned in `AssistantImages.output` and can include both base64-encoded `ImageContent` blocks and `TextContent` blocks.
769
885
  - Some models return only images, others return images plus text. Check `model.output`.
@@ -772,6 +888,97 @@ console.log(model.output); // ['image'] or ['image', 'text']
772
888
  - If you want a model to analyze images in a conversation or call tools, use the regular chat APIs with a model that supports image input.
773
889
  - At the moment, image generation is available through only one provider, OpenRouter.
774
890
 
891
+ ## Classification
892
+
893
+ Classifier models consume structured JSON state and answer one or more typed questions. They do not use chat or image-generation APIs. TypeSafe's Jev model is available from these built-in providers:
894
+
895
+ | Provider | Model IDs | Auth |
896
+ | --- | --- | --- |
897
+ | `typesafe` | `jev-latest` | `TYPESAFE_API_KEY` |
898
+ | `openrouter` | `typesafe/jev-1.13`, `~typesafe/jev-latest` | `OPENROUTER_API_KEY` or OpenRouter OAuth |
899
+ | `cloudflare-workers-ai` | `typesafe/jev`, `@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash` | `CLOUDFLARE_API_KEY` and `CLOUDFLARE_ACCOUNT_ID` |
900
+ | `vercel-ai-gateway` | `typesafe-ai/jev` | `AI_GATEWAY_API_KEY` |
901
+ | `opencode` | `jev-1.13`, `jev-1.13-free` | `OPENCODE_API_KEY` |
902
+
903
+ ```typescript
904
+ import { builtinModels } from '@earendil-works/pi-ai/providers/all';
905
+
906
+ const models = builtinModels();
907
+ const model = models.getModelOfType('classifier', 'typesafe', 'jev-latest')!;
908
+ const result = await models.classify(model, {
909
+ state: { message: 'The change works perfectly, thanks.' },
910
+ questions: {
911
+ category: {
912
+ type: 'choice',
913
+ instructions: 'Classify the message.',
914
+ criteria: {
915
+ approval: 'The user approves of the result',
916
+ correction: 'The user requests a correction'
917
+ }
918
+ },
919
+ satisfaction: {
920
+ type: 'score',
921
+ instructions: 'Score user satisfaction.',
922
+ criteria: ['dissatisfied', 'neutral', 'satisfied']
923
+ },
924
+ approved: {
925
+ type: 'bool',
926
+ instructions: 'Does the user approve?',
927
+ criteria: { true: 'Approval', false: 'No approval' }
928
+ }
929
+ }
930
+ });
931
+
932
+ console.log(result.answers);
933
+ ```
934
+
935
+ The public contract uses `bool` questions and `{ type: "bool", probability }` answers. The TypeSafe adapter translates those to and from its `noul` wire representation. Like image generation, `classify()` resolves to a result with `stopReason: "error"` instead of rejecting for provider, authentication, or response errors.
936
+
937
+ When the service reports token counts, `result.usage` carries them with their cost at the model's catalog price, the same `Usage` shape as chat messages. All System One services report token counts; a request that was answered with malformed answers keeps its usage. Local classifiers such as `llama-cpp-classify` report no usage.
938
+
939
+ `ClassifierOptions.temperature` divides the answer logits by the given value before they are normalized; values above 1 soften the distribution. APIs that cannot apply it, such as System One, ignore it.
940
+
941
+ ### Chat models on llama.cpp
942
+
943
+ The `llama-cpp-classify` API turns a chat model served by llama.cpp's `llama-server` into a classifier. Each question becomes one chat prompt: the state, every question of the request, the state again, and the question with its answers under single-token labels (letters for a choice, `Yes`/`No` for a bool, digits for a score). The prompt up to the final question is shared by all questions of a request, so the server's prompt cache evaluates the state once per request. The server returns the log-probabilities of the next token, and the answer is the softmax over the label tokens. Choices support up to 62 options and scores up to 10 levels. The model's `baseUrl` is the server URL; a trailing `/v1` is ignored. In router mode, the model ID selects the model.
944
+
945
+ ```typescript
946
+ import { createProvider } from '@earendil-works/pi-ai';
947
+ import { llamaCppClassifyApi } from '@earendil-works/pi-ai/api/llama-cpp-classify.lazy';
948
+
949
+ const provider = createProvider({
950
+ id: 'local-llama',
951
+ auth: { apiKey: { name: 'llama.cpp', resolve: async () => ({ auth: {} }) } },
952
+ models: [{
953
+ type: 'classifier',
954
+ id: 'qwen3-4b',
955
+ name: 'Qwen3 4B',
956
+ api: 'llama-cpp-classify',
957
+ provider: 'local-llama',
958
+ baseUrl: 'http://127.0.0.1:8080',
959
+ input: ['text'],
960
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
961
+ contextWindow: 32768
962
+ }],
963
+ classifiers: { 'llama-cpp-classify': llamaCppClassifyApi() }
964
+ });
965
+ ```
966
+
967
+ Raw label probabilities are usually overconfident; pass `temperature` above 1 to soften them.
968
+
969
+ Custom providers register classifier models and implementations by API ID:
970
+
971
+ ```typescript
972
+ createProvider({
973
+ id: 'classifier-service',
974
+ auth,
975
+ models: [model],
976
+ classifiers: {
977
+ 'classifier-api': { classify: async (model, context, options) => result }
978
+ }
979
+ });
980
+ ```
981
+
775
982
  ## Thinking/Reasoning
776
983
 
777
984
  Many models support thinking/reasoning capabilities where they can show their internal thought process. You can check if a model supports reasoning via the `reasoning` property. If you pass reasoning options to a non-reasoning model, they are silently ignored.
@@ -783,7 +990,7 @@ Many models support thinking/reasoning capabilities where they can show their in
783
990
  const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
784
991
  // or models.getModel('openai', 'gpt-5-mini');
785
992
  // or models.getModel('google', 'gemini-2.5-flash');
786
- // or models.getModel('xai', 'grok-4.5');
993
+ // or models.getModel('xai', 'grok-4.7');
787
994
 
788
995
  // Check if model supports reasoning
789
996
  if (model.reasoning) {
@@ -883,7 +1090,7 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
883
1090
 
884
1091
  ## Error Handling
885
1092
 
886
- Request failures never throw out of the stream functions: when a request ends with an error (including aborts and tool call validation errors), the streaming API emits an error event and the final message carries the details:
1093
+ Request failures after a stream is returned never throw: when a request ends with an error (including aborts and tool call validation errors), the streaming API emits an error event and the final message carries the details. Setup failures may emit `error` without `start`; failures after generation begins emit `start`, any observed updates, then `error`. Direct API `streamSimple()` calls throw synchronously when request auth is missing:
887
1094
 
888
1095
  ```typescript
889
1096
  // In streaming
@@ -905,7 +1112,7 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
905
1112
  }
906
1113
  ```
907
1114
 
908
- Auth failures (no key configured, OAuth refresh failed, unknown provider) surface the same way: as a stream error with `stopReason: "error"`.
1115
+ When using a provider collection, auth failures (OAuth refresh failed, unknown provider) surface as a stream error with `stopReason: "error"`. Direct API `streamSimple()` calls instead throw synchronously when their required auth is absent.
909
1116
 
910
1117
  ### Aborting Requests
911
1118
 
@@ -980,11 +1187,30 @@ const response = await models.complete(model, context, {
980
1187
 
981
1188
  The callback is supported by `stream`, `complete`, `streamSimple`, and `completeSimple`.
982
1189
 
1190
+ ### Observing Provider Stream Events
1191
+
1192
+ Use `onProviderStreamEvent` to inspect provider-specific fields that Pi does not include in `AssistantMessage`. The callback receives the parsed event available to the adapter before Pi normalizes it. Treat the event as read-only because mutations can affect normalization. This is not guaranteed to be the original HTTP bytes or SSE frame.
1193
+
1194
+ ```typescript
1195
+ const openRouterModel = models.getModel('openrouter', 'openrouter/auto')!;
1196
+ const response = await models.complete(openRouterModel, context, {
1197
+ headers: { "X-OpenRouter-Metadata": "enabled" },
1198
+ onProviderStreamEvent: (data) => {
1199
+ const chunk = data as Record<string, unknown>;
1200
+ if (chunk.openrouter_metadata) {
1201
+ console.log(chunk.openrouter_metadata);
1202
+ }
1203
+ },
1204
+ });
1205
+ ```
1206
+
1207
+ Callbacks are awaited in stream order, so slow callbacks delay stream consumption and thrown errors fail the request. SDK-backed adapters can expose only fields retained by their SDK.
1208
+
983
1209
  ## Custom Providers
984
1210
 
985
1211
  ### createProvider()
986
1212
 
987
- `createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation. Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
1213
+ `createProvider()` builds a provider from parts: identity, auth, a model list, and an API implementation (`api` for chat models, `images` for image generation, `classifiers` for classification; at least one is required, see [Image Generation](#image-generation)). Use it for local inference servers, proxies, or any OpenAI/Anthropic-compatible endpoint:
988
1214
 
989
1215
  ```typescript
990
1216
  import { createModels, createProvider, envApiKeyAuth, type Model } from '@earendil-works/pi-ai';
@@ -1066,7 +1292,7 @@ const tenantGateway = createProvider({
1066
1292
  });
1067
1293
  ```
1068
1294
 
1069
- Dynamic model lists use `fetchModels`. `Models.refresh()` refreshes every configured dynamic provider, passing its effective API-key or refreshed OAuth credential. A `ModelsStore` persists dynamic catalogs; both stores default to in-memory implementations.
1295
+ Dynamic model lists use `fetchModels`, which can return models of every type. `Models.refresh()` refreshes every configured dynamic provider, passing its effective API-key or refreshed OAuth credential. A `ModelsStore` persists dynamic catalogs; both stores default to in-memory implementations. Its `read`, `write`, and `delete` operations accept optional cancellation, and `Models` binds those waits to the provider refresh signal.
1070
1296
 
1071
1297
  ```typescript
1072
1298
  const models = createModels({ credentials, modelsStore });
@@ -1084,7 +1310,11 @@ if (result.aborted) console.log('refresh cancelled');
1084
1310
  for (const [provider, error] of result.errors) console.error(provider, error);
1085
1311
  ```
1086
1312
 
1087
- Use `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access, or `models.refresh({ force: true })` to bypass provider freshness checks. Model reads stay synchronous and return the last restored or refreshed list.
1313
+ `Models.refresh()` is unbounded when its optional signal is omitted. Providers always receive a concrete `RefreshModelsContext.signal` and must honor it for network requests and other blocking work. When a caller supplies a signal, `Models.refresh()` returns promptly with `aborted: true` after cancellation even if a custom provider fails to cooperate; the provider must still honor the signal to stop its underlying work.
1314
+
1315
+ Use `models.refresh({ providers: ['openrouter'] })` to restrict work to selected providers, `models.refresh({ allowNetwork: false })` to restore persisted catalogs without network access, or `models.refresh({ force: true })` to bypass provider freshness checks. Model reads stay synchronous and return the last restored or refreshed list.
1316
+
1317
+ `createProvider()` handles dynamic publication and persistence automatically. Handwritten `Provider.refreshModels()` implementations receive the read-only `context.stored` snapshot and publish through `context.publish({ persist?, update? })`. Omit `persist` to leave storage unchanged, pass a `ModelsStoreEntry` to write it, or pass `persist: null` to delete it. `ModelsStoreEntry.models` contains models of every type. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
1088
1318
 
1089
1319
  Custom models can carry `headers` (e.g. proxies behind bot detection) and `compat` flags. `Models.getAuth(model)` includes those model headers, and stream methods merge them before explicit request headers and `transformHeaders`. See [OpenAI Compatibility Settings](#openai-compatibility-settings).
1090
1320
 
@@ -1120,12 +1350,13 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
1120
1350
 
1121
1351
  ### Calling API Implementations Directly
1122
1352
 
1123
- The API implementations are importable on their own. Each module exports exactly `stream` and `streamSimple` with that API's full option typing. Direct calls bypass provider auth — pass `apiKey` explicitly:
1353
+ The API implementations are importable on their own. Each module exports exactly `stream` and `streamSimple` with that API's full option typing. Direct calls bypass provider auth and context normalization — pass `apiKey` explicitly and wrap the context in `normalizeContext()`:
1124
1354
 
1125
1355
  ```typescript
1356
+ import { normalizeContext } from '@earendil-works/pi-ai';
1126
1357
  import { stream } from '@earendil-works/pi-ai/api/anthropic-messages';
1127
1358
 
1128
- const s = stream(claudeModel, context, {
1359
+ const s = stream(claudeModel, normalizeContext(context), {
1129
1360
  apiKey: process.env.ANTHROPIC_API_KEY,
1130
1361
  thinkingEnabled: true,
1131
1362
  thinkingBudgetTokens: 2048,
@@ -1158,17 +1389,22 @@ interface OpenAICompletionsCompat {
1158
1389
  supportsDeveloperRole?: boolean; // Whether provider supports `developer` role vs `system` (default: true)
1159
1390
  supportsReasoningEffort?: boolean; // Whether provider supports `reasoning_effort` (default: true)
1160
1391
  supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
1161
- supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: true)
1392
+ supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: false; enabled in metadata for capable built-in models)
1162
1393
  supportsOpenAIGrammarTools?: boolean; // Whether to emit OpenAI custom Lark/regex grammar tools; false falls back to normal function tools (default: false; the generated catalog enables it for capable models)
1163
- sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: false)
1394
+ supportsMidConvoSystemMessages?: boolean; // Whether the model accepts system messages after the conversation started; false folds them into the leading prompt (default: false; the generated catalog enables it for verified models)
1395
+ supportsMidConvoToolAdditions?: boolean; // Whether system messages can add tools mid-conversation via Kimi-style `tools` system messages; requires supportsMidConvoSystemMessages (default: false)
1396
+ sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: true for OpenRouter, false otherwise)
1164
1397
  sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Format for session affinity: 'openai' uses `prompt_cache_key`, `session_id`, `x-client-request-id`, and `x-session-affinity`; 'openai-nosession' uses `prompt_cache_key`, `x-client-request-id`, and `x-session-affinity`; 'openrouter' uses `x-session-id` (default: auto-detected)
1165
1398
  maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
1166
1399
  requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
1167
1400
  requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
1168
1401
  requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false)
1169
1402
  requiresReasoningContentOnAssistantMessages?: boolean; // Whether all replayed assistant messages must include empty reasoning_content when reasoning is enabled (default: auto-detected for DeepSeek)
1170
- thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking' | 'ant-ling'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking, 'ant-ling' uses reasoning: { effort } only for mapped efforts (default: openai)
1171
- chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for pi-controlled thinking values
1403
+ thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'baseten' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking' | 'ant-ling'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'baseten' uses configurable chat_template_args plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking, 'ant-ling' uses reasoning: { effort } only for mapped efforts (default: openai)
1404
+ chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort' | 'thinking.budget'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for pi-controlled thinking values
1405
+ chatTemplateArgs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort' | 'thinking.budget'; omitWhenOff?: boolean }>; // chat_template_args values for thinkingFormat: 'baseten'; use $var for pi-controlled thinking values
1406
+ thinkingTokenBudgetField?: 'thinking_token_budget' | 'thinking_budget' | 'thinking_budget_tokens'; // Top-level field that caps reasoning tokens from thinkingBudgets (vLLM / Qwen / llama.cpp). Off by default.
1407
+ supportsThinkingTokenBudget?: boolean; // Alias for thinkingTokenBudgetField: 'thinking_token_budget' (vLLM). Prefer thinkingTokenBudgetField. Default: false.
1172
1408
  cacheControlFormat?: 'anthropic'; // Anthropic-style cache_control on system prompt, last tool, and last user/assistant text content
1173
1409
  openRouterRouting?: OpenRouterRouting; // OpenRouter routing preferences (default: {})
1174
1410
  vercelGatewayRouting?: VercelGatewayRouting; // Vercel AI Gateway routing preferences (default: {})
@@ -1183,6 +1419,8 @@ interface OpenAIResponsesCompat {
1183
1419
  }
1184
1420
  ```
1185
1421
 
1422
+ OpenRouter requests send `x-session-id` from `sessionId` when prompt caching is enabled. Chat Completions and Anthropic Messages both auto-detect OpenRouter endpoints unless `sendSessionAffinityHeaders` is explicitly false. On Anthropic-compatible models, `sessionAffinityFormat: "openrouter"` selects `x-session-id`; when unset, the existing `x-session-affinity` format is used. Explicit request headers take precedence over generated headers.
1423
+
1186
1424
  If `compat` is not set, the library falls back to URL-based detection. If `compat` is partially set, unspecified fields use the detected defaults. This is useful for:
1187
1425
 
1188
1426
  - **LiteLLM proxies**: May not support `store` field
@@ -1318,6 +1556,39 @@ const geminiResponse = await models.complete(gemini, context);
1318
1556
 
1319
1557
  All providers can handle messages from other providers — text, tool calls and results (including images), thinking blocks (transformed to tagged text), and aborted messages with partial content. This enables flexible workflows: start with a fast model, switch to a more capable one for complex reasoning, or maintain continuity across provider outages.
1320
1558
 
1559
+ ## System Messages
1560
+
1561
+ `Context.systemPrompt` and `Context.tools` are shorthand for a leading system message. The public entry points (`Models.stream()`, `streamSimple()`, `complete()`, `completeSimple()`) accept a `Context` and call `normalizeContext()` once; everything below them, including `Provider.stream()`, `ProviderStreams`, and the API implementation modules, receives the resulting `TranscriptContext`, which only has `messages`. The transcript can also carry system messages later in the conversation to change the prompt or the tool set without rewriting the history:
1562
+
1563
+ ```typescript
1564
+ interface SystemMessage {
1565
+ role: "system";
1566
+ content: string | TextContent[]; // leading: base prompt; later: added instructions
1567
+ sections?: Record<string, string | null>; // named prompt sections; later messages patch by name, null removes
1568
+ toolsAdded?: Tool[]; // tools that become available here
1569
+ toolsRemoved?: ToolReference[]; // tools that stop being available here
1570
+ timestamp: number;
1571
+ }
1572
+ ```
1573
+
1574
+ Sections are opaque text rendered verbatim after `content`, joined by blank lines. Keep each one self-delimiting (a tag, a heading) so the model can relate an update to the original. Replaying every system message in order yields the current prompt and tools; the replay helpers take the message list:
1575
+
1576
+ ```typescript
1577
+ import { getCurrentSystemPrompt, getCurrentTools } from "@earendil-works/pi-ai";
1578
+
1579
+ const messages: Message[] = [
1580
+ { role: "system", content: "You are helpful.", sections: { rules: "<rules>Be brief.</rules>" }, toolsAdded: [readTool], timestamp: 1 },
1581
+ { role: "user", content: "hi", timestamp: 2 },
1582
+ { role: "system", content: "", sections: { rules: "<rules>Be thorough.</rules>" }, toolsRemoved: [{ name: "read" }], timestamp: 3 },
1583
+ ];
1584
+ getCurrentSystemPrompt(messages); // "You are helpful.\n\n<rules>Be thorough.</rules>"
1585
+ getCurrentTools(messages); // []
1586
+ ```
1587
+
1588
+ A custom `Provider` or `ProviderStreams` implementation reads the prompt and tools the same way from `context.messages`; `context.systemPrompt` and `context.tools` do not exist at that layer.
1589
+
1590
+ Models that accept system messages mid-conversation (`supportsMidConvoSystemMessages` in the model's compat settings, set by the generated catalog for verified models) receive each later system message in place, so the cached prefix stays intact; section changes are framed by name for the model. Every other model receives `collapseSystemMessages(transcript)`: the replayed prompt and current tools as the leading system message, with later system messages dropped. Anthropic models that also set `supportsMidConvoToolChanges` send tool changes as native blocks (`inline-tools-2026-09-15` beta): the top-level tool list holds the initial tools plus a stable deferred placeholder and never changes, later tools are defined by value in `tool_addition` blocks (a new definition under an existing name replaces the old one), and removals are `tool_removal` references, so tool changes do not invalidate the prompt cache. That needs at least one initial tool; otherwise the current tool list is sent at the top level with the system text only. OpenAI Responses models with `supportsAdditionalTools` or `supportsToolSearch` anchor additive tool changes at their message; everything else sends the current tool list at the top level.
1591
+
1321
1592
  ## Context Serialization
1322
1593
 
1323
1594
  The `Context` object can be easily serialized and deserialized using standard JSON methods, making it simple to persist conversations, implement chat history, or transfer contexts between services:
@@ -1382,10 +1653,10 @@ Browser compatibility notes:
1382
1653
 
1383
1654
  ## Bundling and Tree Shaking
1384
1655
 
1385
- For small bundles, import only the providers you need:
1656
+ For small bundles and low-overhead unbundled scripts, import the model runtime and only the providers you need:
1386
1657
 
1387
1658
  ```typescript
1388
- import { createModels } from '@earendil-works/pi-ai';
1659
+ import { createModels } from '@earendil-works/pi-ai/models';
1389
1660
  import { openaiProvider } from '@earendil-works/pi-ai/providers/openai';
1390
1661
 
1391
1662
  const models = createModels();
@@ -1394,7 +1665,8 @@ models.setProvider(openaiProvider());
1394
1665
 
1395
1666
  Rules:
1396
1667
 
1397
- - `@earendil-works/pi-ai` is the core entrypoint and does not import built-in catalogs, provider factories, or SDK implementations.
1668
+ - `@earendil-works/pi-ai/models` exports the model runtime (`createModels`, `createProvider`, model helpers, and their types) without TypeBox, built-in catalogs, or SDK implementations. Other types can still use `import type` from the root.
1669
+ - `@earendil-works/pi-ai` is the core entrypoint and does not import built-in catalogs, real provider factories, or SDK implementations, but it eagerly imports TypeBox and schema validation. Unbundled Node scripts do not tree-shake its unused exports; prefer `./models`, `./providers/faux`, and specific `./utils/*` subpaths when those are all you need.
1398
1670
  - `@earendil-works/pi-ai/providers/<provider>` imports that provider's catalog and lazy API wrapper only.
1399
1671
  - `@earendil-works/pi-ai/providers/all` imports every built-in provider factory and all catalogs. Use it only when you want the full built-in set.
1400
1672
  - With code splitting, provider SDKs stay in lazy chunks and load on first request.
@@ -1458,11 +1730,12 @@ Use this when one process needs different provider settings per request, or when
1458
1730
  Several providers support OAuth authentication instead of static API keys:
1459
1731
 
1460
1732
  - **Anthropic** (Claude Pro/Max subscription)
1461
- - **OpenAI Codex** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
1733
+ - **OpenAI** (Sign in with ChatGPT: uses the ChatGPT subscription with the OpenAI API)
1734
+ - **OpenAI Codex (legacy)** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
1462
1735
  - **GitHub Copilot** (Copilot subscription)
1463
1736
  - **OpenRouter** (OAuth PKCE that mints a user-controlled API key)
1464
1737
 
1465
- Each of these providers carries an `OAuthAuth` on `provider.auth.oauth` with three operations: `login(interaction)` uses the provider-neutral `AuthInteraction.prompt()`/`notify()` protocol and returns a credential, `refresh(credential)` refreshes expiring credentials when applicable, and `toAuth(credential)` derives request auth (GitHub Copilot's per-account base URL comes from here). Refresh is automatic: `models.getAuth(providerId)` and request paths refresh expired tokens under a credential-store lock, so concurrent requests and processes cannot double-refresh. OpenRouter's OAuth flow instead returns a permanent API key, so its refresh operation is a no-op.
1738
+ Each of these providers carries an `OAuthAuth` on `provider.auth.oauth` with three operations: `login(interaction)` uses the provider-neutral `AuthInteraction.prompt()`/`notify()` protocol and returns a credential, `refresh(credential, signal)` refreshes expiring credentials when applicable, and `toAuth(credential)` derives request auth (GitHub Copilot's per-account base URL comes from here). Provider login interactions and refresh calls always carry a concrete abort signal. Refresh is automatic: `models.getAuth(providerId)` and request paths refresh expired tokens under a credential-store lock, so concurrent requests and processes cannot double-refresh. OpenRouter's OAuth flow instead returns a permanent API key, so its refresh operation is a no-op.
1466
1739
 
1467
1740
  ```typescript
1468
1741
  import { createModels } from '@earendil-works/pi-ai';
@@ -1538,7 +1811,7 @@ Built-in login and refresh flows are private provider implementations. Use provi
1538
1811
 
1539
1812
  Provider notes:
1540
1813
 
1541
- **OpenAI Codex**: Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity.
1814
+ **OpenAI Codex (legacy)**: Superseded by Sign in with ChatGPT on the OpenAI provider. Requires a ChatGPT Plus or Pro subscription. Provides access to GPT-5.x Codex models with extended context windows and reasoning capabilities. The library automatically handles session-based prompt caching when `sessionId` is provided in stream options unless `cacheRetention` is `"none"`. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId` and cache retention enabled, connections are reused per session and expire after 5 minutes of inactivity. Call `cleanupSessionResources(sessionId)` when finished so the pooled connection does not keep the process alive.
1542
1815
 
1543
1816
  **Azure OpenAI (Responses)**: Uses the Responses API only. Set `AZURE_OPENAI_API_KEY` and either `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME`. `AZURE_OPENAI_BASE_URL` supports both `https://<resource>.openai.azure.com` and `https://<resource>.cognitiveservices.azure.com`; root endpoints are normalized to `.../openai/v1` automatically. Use `AZURE_OPENAI_API_VERSION` (defaults to `v1`) to override the API version if needed. Deployment names are treated as model IDs by default, override with `azureDeploymentName` or `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` using comma-separated `model-id=deployment` pairs (for example `gpt-4o-mini=my-deployment,gpt-4o=prod`). Legacy deployment-based URLs are intentionally unsupported.
1544
1817
 
@@ -1567,6 +1840,9 @@ Compat is a strict superset of the root entrypoint, so a file can switch its imp
1567
1840
  | `getEnvApiKey('openai')` | `await models.getAuth(model.provider)` |
1568
1841
  | `streamAnthropic(model, ctx, opts)` | `stream` from `@earendil-works/pi-ai/api/anthropic-messages`, or a provider in a collection |
1569
1842
  | `registerFauxProvider()` | `fauxProvider()` + `models.setProvider()` |
1843
+ | `getImageModel('openrouter', id)` / `generateImages(model, ctx, { apiKey })` | `models.getModelOfType('image', 'openrouter', id)` / `models.generateImages(model, ctx)` |
1844
+
1845
+ The separate `ImagesModels`/`ImagesProvider` collection that existed briefly (`createImagesModels()`, `createImagesProvider()`, `openrouterImagesProvider()`, `builtinImagesModels()`) is gone: image models now live on the regular provider. Replace `builtinImagesModels()` with `builtinModels()`, `imagesModels.getModel()` with `models.getModelOfType('image', ...)`, and `createImagesProvider({ models, api })` with `createProvider({ models, images })`. The old plural image type names are removed; use `ImageModel` and `ImageApi`, and add `type: "image"` to image model literals.
1570
1846
 
1571
1847
  ## Development
1572
1848
 
@@ -1585,17 +1861,17 @@ Adding a new LLM provider requires changes across multiple files. The layered la
1585
1861
  Create a new API implementation file (for example `bedrock-converse-stream.ts`) that exports exactly `stream` and `streamSimple`, plus:
1586
1862
 
1587
1863
  - An options interface extending `StreamOptions` (for example `BedrockOptions`)
1588
- - Message conversion functions to transform `Context` to provider format
1864
+ - Message conversion functions to transform the `TranscriptContext` messages to provider format; read the prompt and tools from the transcript with `getInitialSystemMessage()`, `getCurrentTools()`, and `resolveTranscript()`
1589
1865
  - Tool conversion if the provider supports tools
1590
1866
  - Response parsing to emit standardized events (`text`, `tool_call`, `thinking`, `usage`, `stop`)
1591
1867
 
1592
1868
  Add a lazy wrapper `src/api/<api-id>.lazy.ts` (`<name>Api()` via `lazyApi()`) so providers can reference the implementation without importing its SDK. Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@earendil-works/pi-ai`.
1593
1869
 
1594
- #### 3. Model Generation (`scripts/generate-models.ts`, `scripts/generate-image-models.ts`)
1870
+ #### 3. Model Generation (`scripts/generate-models.ts`)
1595
1871
 
1596
1872
  - Add logic to fetch and parse models from the provider's source (e.g., models.dev API)
1597
- - Map chat/tool-capable provider model data to the standardized `Model` interface via `scripts/generate-models.ts`; hydration groups the ignored `src/providers/data/<id>.json` values by API, while stable `src/providers/<id>.models.ts` wrappers derive exact model/API types directly from those JSON keys
1598
- - Map image-generation provider model data to the standardized `ImagesModel` interface via `scripts/generate-image-models.ts`
1873
+ - Map chat/tool-capable provider data to `Model`, image-generation data to `ImageModel`, and models.dev `type: "decision"` entries to `ClassifierModel`; hydration groups the ignored `src/providers/data/<id>.json` values by API while stable `src/providers/<id>.models.ts` wrappers derive exact model/API types directly from those JSON keys
1874
+ - Keep model ids unique within each provider and model type; emit separate entries when an upstream model supports multiple operations
1599
1875
  - Handle provider-specific quirks (pricing format, capability flags, model ID transformations)
1600
1876
 
1601
1877
  #### 4. Provider Factory (`src/providers/<id>.ts`)