@sammorrowdrums/mcpi-ai 0.70.8 → 0.85.0-bootstrap.0

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 (795) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +845 -486
  3. package/dist/api/anthropic-messages.d.ts +71 -0
  4. package/dist/api/anthropic-messages.d.ts.map +1 -0
  5. package/dist/api/anthropic-messages.js +1060 -0
  6. package/dist/api/anthropic-messages.js.map +1 -0
  7. package/dist/api/anthropic-messages.lazy.d.ts +3 -0
  8. package/dist/api/anthropic-messages.lazy.d.ts.map +1 -0
  9. package/dist/api/anthropic-messages.lazy.js +3 -0
  10. package/dist/api/anthropic-messages.lazy.js.map +1 -0
  11. package/dist/api/azure-openai-responses.d.ts +15 -0
  12. package/dist/api/azure-openai-responses.d.ts.map +1 -0
  13. package/dist/api/azure-openai-responses.js +242 -0
  14. package/dist/api/azure-openai-responses.js.map +1 -0
  15. package/dist/api/azure-openai-responses.lazy.d.ts +3 -0
  16. package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -0
  17. package/dist/api/azure-openai-responses.lazy.js +3 -0
  18. package/dist/api/azure-openai-responses.lazy.js.map +1 -0
  19. package/dist/api/bedrock-converse-stream.d.ts +38 -0
  20. package/dist/api/bedrock-converse-stream.d.ts.map +1 -0
  21. package/dist/api/bedrock-converse-stream.js +967 -0
  22. package/dist/api/bedrock-converse-stream.js.map +1 -0
  23. package/dist/api/bedrock-converse-stream.lazy.d.ts +9 -0
  24. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -0
  25. package/dist/api/bedrock-converse-stream.lazy.js +30 -0
  26. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -0
  27. package/dist/api/cloudflare-gateway-binding.d.ts +69 -0
  28. package/dist/api/cloudflare-gateway-binding.d.ts.map +1 -0
  29. package/dist/api/cloudflare-gateway-binding.js +159 -0
  30. package/dist/api/cloudflare-gateway-binding.js.map +1 -0
  31. package/dist/api/cloudflare.d.ts +9 -0
  32. package/dist/api/cloudflare.d.ts.map +1 -0
  33. package/dist/api/cloudflare.js +9 -0
  34. package/dist/api/cloudflare.js.map +1 -0
  35. package/dist/api/constrained-sampling.d.ts +20 -0
  36. package/dist/api/constrained-sampling.d.ts.map +1 -0
  37. package/dist/api/constrained-sampling.js +222 -0
  38. package/dist/api/constrained-sampling.js.map +1 -0
  39. package/dist/{providers → api}/github-copilot-headers.d.ts +1 -1
  40. package/dist/api/github-copilot-headers.d.ts.map +1 -0
  41. package/dist/api/github-copilot-headers.js.map +1 -0
  42. package/dist/api/google-generative-ai.d.ts +13 -0
  43. package/dist/api/google-generative-ai.d.ts.map +1 -0
  44. package/dist/api/google-generative-ai.js +417 -0
  45. package/dist/api/google-generative-ai.js.map +1 -0
  46. package/dist/api/google-generative-ai.lazy.d.ts +3 -0
  47. package/dist/api/google-generative-ai.lazy.d.ts.map +1 -0
  48. package/dist/api/google-generative-ai.lazy.js +3 -0
  49. package/dist/api/google-generative-ai.lazy.js.map +1 -0
  50. package/dist/{providers → api}/google-shared.d.ts +23 -7
  51. package/dist/api/google-shared.d.ts.map +1 -0
  52. package/dist/{providers → api}/google-shared.js +76 -30
  53. package/dist/api/google-shared.js.map +1 -0
  54. package/dist/api/google-vertex.d.ts +15 -0
  55. package/dist/api/google-vertex.d.ts.map +1 -0
  56. package/dist/api/google-vertex.js +466 -0
  57. package/dist/api/google-vertex.js.map +1 -0
  58. package/dist/api/google-vertex.lazy.d.ts +3 -0
  59. package/dist/api/google-vertex.lazy.d.ts.map +1 -0
  60. package/dist/api/google-vertex.lazy.js +3 -0
  61. package/dist/api/google-vertex.lazy.js.map +1 -0
  62. package/dist/api/lazy.d.ts +19 -0
  63. package/dist/api/lazy.d.ts.map +1 -0
  64. package/dist/api/lazy.js +70 -0
  65. package/dist/api/lazy.js.map +1 -0
  66. package/dist/api/mistral-conversations.d.ts +25 -0
  67. package/dist/api/mistral-conversations.d.ts.map +1 -0
  68. package/dist/api/mistral-conversations.js +736 -0
  69. package/dist/api/mistral-conversations.js.map +1 -0
  70. package/dist/api/mistral-conversations.lazy.d.ts +3 -0
  71. package/dist/api/mistral-conversations.lazy.d.ts.map +1 -0
  72. package/dist/api/mistral-conversations.lazy.js +3 -0
  73. package/dist/api/mistral-conversations.lazy.js.map +1 -0
  74. package/dist/api/openai-codex-responses.d.ts +31 -0
  75. package/dist/api/openai-codex-responses.d.ts.map +1 -0
  76. package/dist/api/openai-codex-responses.js +1294 -0
  77. package/dist/api/openai-codex-responses.js.map +1 -0
  78. package/dist/api/openai-codex-responses.lazy.d.ts +3 -0
  79. package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -0
  80. package/dist/api/openai-codex-responses.lazy.js +3 -0
  81. package/dist/api/openai-codex-responses.lazy.js.map +1 -0
  82. package/dist/api/openai-completions.d.ts +22 -0
  83. package/dist/api/openai-completions.d.ts.map +1 -0
  84. package/dist/api/openai-completions.js +1279 -0
  85. package/dist/api/openai-completions.js.map +1 -0
  86. package/dist/api/openai-completions.lazy.d.ts +3 -0
  87. package/dist/api/openai-completions.lazy.d.ts.map +1 -0
  88. package/dist/api/openai-completions.lazy.js +3 -0
  89. package/dist/api/openai-completions.lazy.js.map +1 -0
  90. package/dist/api/openai-prompt-cache.d.ts +3 -0
  91. package/dist/api/openai-prompt-cache.d.ts.map +1 -0
  92. package/dist/api/openai-prompt-cache.js +10 -0
  93. package/dist/api/openai-prompt-cache.js.map +1 -0
  94. package/dist/{providers → api}/openai-responses-shared.d.ts +11 -3
  95. package/dist/api/openai-responses-shared.d.ts.map +1 -0
  96. package/dist/api/openai-responses-shared.js +686 -0
  97. package/dist/api/openai-responses-shared.js.map +1 -0
  98. package/dist/{providers → api}/openai-responses.d.ts +5 -4
  99. package/dist/api/openai-responses.d.ts.map +1 -0
  100. package/dist/api/openai-responses.js +283 -0
  101. package/dist/api/openai-responses.js.map +1 -0
  102. package/dist/api/openai-responses.lazy.d.ts +3 -0
  103. package/dist/api/openai-responses.lazy.d.ts.map +1 -0
  104. package/dist/api/openai-responses.lazy.js +3 -0
  105. package/dist/api/openai-responses.lazy.js.map +1 -0
  106. package/dist/api/openrouter-images.d.ts +3 -0
  107. package/dist/api/openrouter-images.d.ts.map +1 -0
  108. package/dist/api/openrouter-images.js +132 -0
  109. package/dist/api/openrouter-images.js.map +1 -0
  110. package/dist/api/openrouter-images.lazy.d.ts +3 -0
  111. package/dist/api/openrouter-images.lazy.d.ts.map +1 -0
  112. package/dist/api/openrouter-images.lazy.js +4 -0
  113. package/dist/api/openrouter-images.lazy.js.map +1 -0
  114. package/dist/api/pi-messages.d.ts +97 -0
  115. package/dist/api/pi-messages.d.ts.map +1 -0
  116. package/dist/api/pi-messages.js +307 -0
  117. package/dist/api/pi-messages.js.map +1 -0
  118. package/dist/api/pi-messages.lazy.d.ts +3 -0
  119. package/dist/api/pi-messages.lazy.d.ts.map +1 -0
  120. package/dist/api/pi-messages.lazy.js +3 -0
  121. package/dist/api/pi-messages.lazy.js.map +1 -0
  122. package/dist/api/simple-options.d.ts +11 -0
  123. package/dist/api/simple-options.d.ts.map +1 -0
  124. package/dist/api/simple-options.js +59 -0
  125. package/dist/api/simple-options.js.map +1 -0
  126. package/dist/{providers → api}/transform-messages.d.ts +1 -1
  127. package/dist/api/transform-messages.d.ts.map +1 -0
  128. package/dist/{providers → api}/transform-messages.js +4 -1
  129. package/dist/api/transform-messages.js.map +1 -0
  130. package/dist/auth/context.d.ts +7 -0
  131. package/dist/auth/context.d.ts.map +1 -0
  132. package/dist/auth/context.js +42 -0
  133. package/dist/auth/context.js.map +1 -0
  134. package/dist/auth/credential-store.d.ts +17 -0
  135. package/dist/auth/credential-store.d.ts.map +1 -0
  136. package/dist/auth/credential-store.js +51 -0
  137. package/dist/auth/credential-store.js.map +1 -0
  138. package/dist/auth/helpers.d.ts +22 -0
  139. package/dist/auth/helpers.d.ts.map +1 -0
  140. package/dist/auth/helpers.js +53 -0
  141. package/dist/auth/helpers.js.map +1 -0
  142. package/dist/auth/oauth/anthropic.d.ts +9 -0
  143. package/dist/auth/oauth/anthropic.d.ts.map +1 -0
  144. package/dist/{utils → auth}/oauth/anthropic.js +66 -93
  145. package/dist/auth/oauth/anthropic.js.map +1 -0
  146. package/dist/auth/oauth/device-code.d.ts +23 -0
  147. package/dist/auth/oauth/device-code.d.ts.map +1 -0
  148. package/dist/auth/oauth/device-code.js +69 -0
  149. package/dist/auth/oauth/device-code.js.map +1 -0
  150. package/dist/auth/oauth/github-copilot.d.ts +6 -0
  151. package/dist/auth/oauth/github-copilot.d.ts.map +1 -0
  152. package/dist/auth/oauth/github-copilot.js +328 -0
  153. package/dist/auth/oauth/github-copilot.js.map +1 -0
  154. package/dist/auth/oauth/kimi-coding.d.ts +10 -0
  155. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -0
  156. package/dist/auth/oauth/kimi-coding.js +261 -0
  157. package/dist/auth/oauth/kimi-coding.js.map +1 -0
  158. package/dist/auth/oauth/load.d.ts +27 -0
  159. package/dist/auth/oauth/load.d.ts.map +1 -0
  160. package/dist/auth/oauth/load.js +59 -0
  161. package/dist/auth/oauth/load.js.map +1 -0
  162. package/dist/auth/oauth/oauth-page.d.ts.map +1 -0
  163. package/dist/auth/oauth/oauth-page.js.map +1 -0
  164. package/dist/auth/oauth/openai-codex.d.ts +9 -0
  165. package/dist/auth/oauth/openai-codex.d.ts.map +1 -0
  166. package/dist/auth/oauth/openai-codex.js +450 -0
  167. package/dist/auth/oauth/openai-codex.js.map +1 -0
  168. package/dist/auth/oauth/openrouter.d.ts +15 -0
  169. package/dist/auth/oauth/openrouter.d.ts.map +1 -0
  170. package/dist/auth/oauth/openrouter.js +277 -0
  171. package/dist/auth/oauth/openrouter.js.map +1 -0
  172. package/dist/auth/oauth/pkce.d.ts.map +1 -0
  173. package/dist/auth/oauth/pkce.js.map +1 -0
  174. package/dist/auth/oauth/radius.d.ts +17 -0
  175. package/dist/auth/oauth/radius.d.ts.map +1 -0
  176. package/dist/auth/oauth/radius.js +321 -0
  177. package/dist/auth/oauth/radius.js.map +1 -0
  178. package/dist/auth/oauth/xai.d.ts +6 -0
  179. package/dist/auth/oauth/xai.d.ts.map +1 -0
  180. package/dist/auth/oauth/xai.js +190 -0
  181. package/dist/auth/oauth/xai.js.map +1 -0
  182. package/dist/auth/resolve.d.ts +27 -0
  183. package/dist/auth/resolve.d.ts.map +1 -0
  184. package/dist/auth/resolve.js +132 -0
  185. package/dist/auth/resolve.js.map +1 -0
  186. package/dist/auth/types.d.ts +231 -0
  187. package/dist/auth/types.d.ts.map +1 -0
  188. package/dist/auth/types.js.map +1 -0
  189. package/dist/bedrock-provider.d.ts +2 -2
  190. package/dist/bedrock-provider.d.ts.map +1 -1
  191. package/dist/bedrock-provider.js +3 -3
  192. package/dist/bedrock-provider.js.map +1 -1
  193. package/dist/bun-oauth.d.ts +3 -0
  194. package/dist/bun-oauth.d.ts.map +1 -0
  195. package/dist/bun-oauth.js +21 -0
  196. package/dist/bun-oauth.js.map +1 -0
  197. package/dist/cli.d.ts.map +1 -1
  198. package/dist/cli.js +60 -63
  199. package/dist/cli.js.map +1 -1
  200. package/dist/compat/extension-oauth-types.d.ts +39 -0
  201. package/dist/compat/extension-oauth-types.d.ts.map +1 -0
  202. package/dist/compat/extension-oauth-types.js +2 -0
  203. package/dist/compat/extension-oauth-types.js.map +1 -0
  204. package/dist/compat.d.ts +67 -0
  205. package/dist/compat.d.ts.map +1 -0
  206. package/dist/compat.js +199 -0
  207. package/dist/compat.js.map +1 -0
  208. package/dist/env-api-keys.d.ts +8 -5
  209. package/dist/env-api-keys.d.ts.map +1 -1
  210. package/dist/env-api-keys.js +60 -22
  211. package/dist/env-api-keys.js.map +1 -1
  212. package/dist/image-models.d.ts +10 -0
  213. package/dist/image-models.d.ts.map +1 -0
  214. package/dist/image-models.generated.d.ts +740 -0
  215. package/dist/image-models.generated.d.ts.map +1 -0
  216. package/dist/image-models.generated.js +742 -0
  217. package/dist/image-models.generated.js.map +1 -0
  218. package/dist/image-models.js +23 -0
  219. package/dist/image-models.js.map +1 -0
  220. package/dist/images-api-registry.d.ts +14 -0
  221. package/dist/images-api-registry.d.ts.map +1 -0
  222. package/dist/images-api-registry.js +22 -0
  223. package/dist/images-api-registry.js.map +1 -0
  224. package/dist/images-models.d.ts +95 -0
  225. package/dist/images-models.d.ts.map +1 -0
  226. package/dist/images-models.js +143 -0
  227. package/dist/images-models.js.map +1 -0
  228. package/dist/images.d.ts +4 -0
  229. package/dist/images.d.ts.map +1 -0
  230. package/dist/images.js +14 -0
  231. package/dist/images.js.map +1 -0
  232. package/dist/index.d.ts +32 -23
  233. package/dist/index.d.ts.map +1 -1
  234. package/dist/index.js +12 -4
  235. package/dist/index.js.map +1 -1
  236. package/dist/legacy-api-aliases.d.ts +42 -0
  237. package/dist/legacy-api-aliases.d.ts.map +1 -0
  238. package/dist/legacy-api-aliases.js +49 -0
  239. package/dist/legacy-api-aliases.js.map +1 -0
  240. package/dist/model-catalog.d.ts +17 -0
  241. package/dist/model-catalog.d.ts.map +1 -0
  242. package/dist/model-catalog.js +4 -0
  243. package/dist/model-catalog.js.map +1 -0
  244. package/dist/models-store.d.ts +29 -0
  245. package/dist/models-store.d.ts.map +1 -0
  246. package/dist/models-store.js +17 -0
  247. package/dist/models-store.js.map +1 -0
  248. package/dist/models.d.ts +191 -15
  249. package/dist/models.d.ts.map +1 -1
  250. package/dist/models.generated.d.ts +78 -15427
  251. package/dist/models.generated.d.ts.map +1 -1
  252. package/dist/models.generated.js +78 -15235
  253. package/dist/models.generated.js.map +1 -1
  254. package/dist/models.js +565 -38
  255. package/dist/models.js.map +1 -1
  256. package/dist/oauth.d.ts +2 -1
  257. package/dist/oauth.d.ts.map +1 -1
  258. package/dist/oauth.js +1 -1
  259. package/dist/oauth.js.map +1 -1
  260. package/dist/providers/all.d.ts +28 -0
  261. package/dist/providers/all.d.ts.map +1 -0
  262. package/dist/providers/all.js +131 -0
  263. package/dist/providers/all.js.map +1 -0
  264. package/dist/providers/amazon-bedrock.d.ts +2 -37
  265. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  266. package/dist/providers/amazon-bedrock.js +81 -724
  267. package/dist/providers/amazon-bedrock.js.map +1 -1
  268. package/dist/providers/amazon-bedrock.models.d.ts +4 -0
  269. package/dist/providers/amazon-bedrock.models.d.ts.map +1 -0
  270. package/dist/providers/amazon-bedrock.models.js +6 -0
  271. package/dist/providers/amazon-bedrock.models.js.map +1 -0
  272. package/dist/providers/ant-ling.d.ts +3 -0
  273. package/dist/providers/ant-ling.d.ts.map +1 -0
  274. package/dist/providers/ant-ling.js +15 -0
  275. package/dist/providers/ant-ling.js.map +1 -0
  276. package/dist/providers/ant-ling.models.d.ts +4 -0
  277. package/dist/providers/ant-ling.models.d.ts.map +1 -0
  278. package/dist/providers/ant-ling.models.js +6 -0
  279. package/dist/providers/ant-ling.models.js.map +1 -0
  280. package/dist/providers/anthropic.d.ts +2 -53
  281. package/dist/providers/anthropic.d.ts.map +1 -1
  282. package/dist/providers/anthropic.js +48 -940
  283. package/dist/providers/anthropic.js.map +1 -1
  284. package/dist/providers/anthropic.models.d.ts +4 -0
  285. package/dist/providers/anthropic.models.d.ts.map +1 -0
  286. package/dist/providers/anthropic.models.js +6 -0
  287. package/dist/providers/anthropic.models.js.map +1 -0
  288. package/dist/providers/azure-openai-responses.d.ts +2 -14
  289. package/dist/providers/azure-openai-responses.d.ts.map +1 -1
  290. package/dist/providers/azure-openai-responses.js +11 -184
  291. package/dist/providers/azure-openai-responses.js.map +1 -1
  292. package/dist/providers/azure-openai-responses.models.d.ts +4 -0
  293. package/dist/providers/azure-openai-responses.models.d.ts.map +1 -0
  294. package/dist/providers/azure-openai-responses.models.js +6 -0
  295. package/dist/providers/azure-openai-responses.models.js.map +1 -0
  296. package/dist/providers/baseten.d.ts +3 -0
  297. package/dist/providers/baseten.d.ts.map +1 -0
  298. package/dist/providers/baseten.js +15 -0
  299. package/dist/providers/baseten.js.map +1 -0
  300. package/dist/providers/baseten.models.d.ts +4 -0
  301. package/dist/providers/baseten.models.d.ts.map +1 -0
  302. package/dist/providers/baseten.models.js +6 -0
  303. package/dist/providers/baseten.models.js.map +1 -0
  304. package/dist/providers/cerebras.d.ts +3 -0
  305. package/dist/providers/cerebras.d.ts.map +1 -0
  306. package/dist/providers/cerebras.js +15 -0
  307. package/dist/providers/cerebras.js.map +1 -0
  308. package/dist/providers/cerebras.models.d.ts +4 -0
  309. package/dist/providers/cerebras.models.d.ts.map +1 -0
  310. package/dist/providers/cerebras.models.js +6 -0
  311. package/dist/providers/cerebras.models.js.map +1 -0
  312. package/dist/providers/cloudflare-ai-gateway.d.ts +3 -0
  313. package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -0
  314. package/dist/providers/cloudflare-ai-gateway.js +21 -0
  315. package/dist/providers/cloudflare-ai-gateway.js.map +1 -0
  316. package/dist/providers/cloudflare-ai-gateway.models.d.ts +4 -0
  317. package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -0
  318. package/dist/providers/cloudflare-ai-gateway.models.js +6 -0
  319. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -0
  320. package/dist/providers/cloudflare-auth.d.ts +4 -0
  321. package/dist/providers/cloudflare-auth.d.ts.map +1 -0
  322. package/dist/providers/cloudflare-auth.js +86 -0
  323. package/dist/providers/cloudflare-auth.js.map +1 -0
  324. package/dist/providers/cloudflare-stream.d.ts +8 -0
  325. package/dist/providers/cloudflare-stream.d.ts.map +1 -0
  326. package/dist/providers/cloudflare-stream.js +21 -0
  327. package/dist/providers/cloudflare-stream.js.map +1 -0
  328. package/dist/providers/cloudflare-workers-ai.d.ts +3 -0
  329. package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -0
  330. package/dist/providers/cloudflare-workers-ai.js +15 -0
  331. package/dist/providers/cloudflare-workers-ai.js.map +1 -0
  332. package/dist/providers/cloudflare-workers-ai.models.d.ts +4 -0
  333. package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -0
  334. package/dist/providers/cloudflare-workers-ai.models.js +6 -0
  335. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -0
  336. package/dist/providers/data/.manifest.json +1 -0
  337. package/dist/providers/data/amazon-bedrock.json +1 -0
  338. package/dist/providers/data/ant-ling.json +1 -0
  339. package/dist/providers/data/anthropic.json +1 -0
  340. package/dist/providers/data/azure-openai-responses.json +1 -0
  341. package/dist/providers/data/baseten.json +1 -0
  342. package/dist/providers/data/cerebras.json +1 -0
  343. package/dist/providers/data/cloudflare-ai-gateway.json +1 -0
  344. package/dist/providers/data/cloudflare-workers-ai.json +1 -0
  345. package/dist/providers/data/deepseek.json +1 -0
  346. package/dist/providers/data/fireworks.json +1 -0
  347. package/dist/providers/data/github-copilot.json +1 -0
  348. package/dist/providers/data/google-vertex.json +1 -0
  349. package/dist/providers/data/google.json +1 -0
  350. package/dist/providers/data/groq.json +1 -0
  351. package/dist/providers/data/huggingface.json +1 -0
  352. package/dist/providers/data/kimi-coding.json +1 -0
  353. package/dist/providers/data/minimax-cn.json +1 -0
  354. package/dist/providers/data/minimax.json +1 -0
  355. package/dist/providers/data/mistral.json +1 -0
  356. package/dist/providers/data/moonshotai-cn.json +1 -0
  357. package/dist/providers/data/moonshotai.json +1 -0
  358. package/dist/providers/data/nvidia.json +1 -0
  359. package/dist/providers/data/openai-codex.json +1 -0
  360. package/dist/providers/data/openai.json +1 -0
  361. package/dist/providers/data/opencode-go.json +1 -0
  362. package/dist/providers/data/opencode.json +1 -0
  363. package/dist/providers/data/openrouter.json +1 -0
  364. package/dist/providers/data/qwen-token-plan-cn.json +1 -0
  365. package/dist/providers/data/qwen-token-plan-individual.json +1 -0
  366. package/dist/providers/data/qwen-token-plan.json +1 -0
  367. package/dist/providers/data/together.json +1 -0
  368. package/dist/providers/data/vercel-ai-gateway.json +1 -0
  369. package/dist/providers/data/xai.json +1 -0
  370. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -0
  371. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -0
  372. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -0
  373. package/dist/providers/data/xiaomi.json +1 -0
  374. package/dist/providers/data/zai-coding-cn.json +1 -0
  375. package/dist/providers/data/zai.json +1 -0
  376. package/dist/providers/deepseek.d.ts +3 -0
  377. package/dist/providers/deepseek.d.ts.map +1 -0
  378. package/dist/providers/deepseek.js +15 -0
  379. package/dist/providers/deepseek.js.map +1 -0
  380. package/dist/providers/deepseek.models.d.ts +4 -0
  381. package/dist/providers/deepseek.models.d.ts.map +1 -0
  382. package/dist/providers/deepseek.models.js +6 -0
  383. package/dist/providers/deepseek.models.js.map +1 -0
  384. package/dist/providers/faux.d.ts +53 -7
  385. package/dist/providers/faux.d.ts.map +1 -1
  386. package/dist/providers/faux.js +131 -11
  387. package/dist/providers/faux.js.map +1 -1
  388. package/dist/providers/fireworks.d.ts +3 -0
  389. package/dist/providers/fireworks.d.ts.map +1 -0
  390. package/dist/providers/fireworks.js +19 -0
  391. package/dist/providers/fireworks.js.map +1 -0
  392. package/dist/providers/fireworks.models.d.ts +4 -0
  393. package/dist/providers/fireworks.models.d.ts.map +1 -0
  394. package/dist/providers/fireworks.models.js +6 -0
  395. package/dist/providers/fireworks.models.js.map +1 -0
  396. package/dist/providers/github-copilot.d.ts +3 -0
  397. package/dist/providers/github-copilot.d.ts.map +1 -0
  398. package/dist/providers/github-copilot.js +35 -0
  399. package/dist/providers/github-copilot.js.map +1 -0
  400. package/dist/providers/github-copilot.models.d.ts +4 -0
  401. package/dist/providers/github-copilot.models.d.ts.map +1 -0
  402. package/dist/providers/github-copilot.models.js +6 -0
  403. package/dist/providers/github-copilot.models.js.map +1 -0
  404. package/dist/providers/google-vertex.d.ts +2 -14
  405. package/dist/providers/google-vertex.d.ts.map +1 -1
  406. package/dist/providers/google-vertex.js +86 -433
  407. package/dist/providers/google-vertex.js.map +1 -1
  408. package/dist/providers/google-vertex.models.d.ts +4 -0
  409. package/dist/providers/google-vertex.models.d.ts.map +1 -0
  410. package/dist/providers/google-vertex.models.js +6 -0
  411. package/dist/providers/google-vertex.models.js.map +1 -0
  412. package/dist/providers/google.d.ts +2 -12
  413. package/dist/providers/google.d.ts.map +1 -1
  414. package/dist/providers/google.js +12 -396
  415. package/dist/providers/google.js.map +1 -1
  416. package/dist/providers/google.models.d.ts +4 -0
  417. package/dist/providers/google.models.d.ts.map +1 -0
  418. package/dist/providers/google.models.js +6 -0
  419. package/dist/providers/google.models.js.map +1 -0
  420. package/dist/providers/groq.d.ts +3 -0
  421. package/dist/providers/groq.d.ts.map +1 -0
  422. package/dist/providers/groq.js +15 -0
  423. package/dist/providers/groq.js.map +1 -0
  424. package/dist/providers/groq.models.d.ts +4 -0
  425. package/dist/providers/groq.models.d.ts.map +1 -0
  426. package/dist/providers/groq.models.js +6 -0
  427. package/dist/providers/groq.models.js.map +1 -0
  428. package/dist/providers/huggingface.d.ts +3 -0
  429. package/dist/providers/huggingface.d.ts.map +1 -0
  430. package/dist/providers/huggingface.js +15 -0
  431. package/dist/providers/huggingface.js.map +1 -0
  432. package/dist/providers/huggingface.models.d.ts +4 -0
  433. package/dist/providers/huggingface.models.d.ts.map +1 -0
  434. package/dist/providers/huggingface.models.js +6 -0
  435. package/dist/providers/huggingface.models.js.map +1 -0
  436. package/dist/providers/images/register-builtins.d.ts +4 -0
  437. package/dist/providers/images/register-builtins.d.ts.map +1 -0
  438. package/dist/providers/images/register-builtins.js +34 -0
  439. package/dist/providers/images/register-builtins.js.map +1 -0
  440. package/dist/providers/kimi-coding.d.ts +3 -0
  441. package/dist/providers/kimi-coding.d.ts.map +1 -0
  442. package/dist/providers/kimi-coding.js +24 -0
  443. package/dist/providers/kimi-coding.js.map +1 -0
  444. package/dist/providers/kimi-coding.models.d.ts +4 -0
  445. package/dist/providers/kimi-coding.models.d.ts.map +1 -0
  446. package/dist/providers/kimi-coding.models.js +6 -0
  447. package/dist/providers/kimi-coding.models.js.map +1 -0
  448. package/dist/providers/minimax-cn.d.ts +3 -0
  449. package/dist/providers/minimax-cn.d.ts.map +1 -0
  450. package/dist/providers/minimax-cn.js +15 -0
  451. package/dist/providers/minimax-cn.js.map +1 -0
  452. package/dist/providers/minimax-cn.models.d.ts +4 -0
  453. package/dist/providers/minimax-cn.models.d.ts.map +1 -0
  454. package/dist/providers/minimax-cn.models.js +6 -0
  455. package/dist/providers/minimax-cn.models.js.map +1 -0
  456. package/dist/providers/minimax.d.ts +3 -0
  457. package/dist/providers/minimax.d.ts.map +1 -0
  458. package/dist/providers/minimax.js +15 -0
  459. package/dist/providers/minimax.js.map +1 -0
  460. package/dist/providers/minimax.models.d.ts +4 -0
  461. package/dist/providers/minimax.models.d.ts.map +1 -0
  462. package/dist/providers/minimax.models.js +6 -0
  463. package/dist/providers/minimax.models.js.map +1 -0
  464. package/dist/providers/mistral.d.ts +2 -24
  465. package/dist/providers/mistral.d.ts.map +1 -1
  466. package/dist/providers/mistral.js +12 -531
  467. package/dist/providers/mistral.js.map +1 -1
  468. package/dist/providers/mistral.models.d.ts +4 -0
  469. package/dist/providers/mistral.models.d.ts.map +1 -0
  470. package/dist/providers/mistral.models.js +6 -0
  471. package/dist/providers/mistral.models.js.map +1 -0
  472. package/dist/providers/moonshotai-cn.d.ts +3 -0
  473. package/dist/providers/moonshotai-cn.d.ts.map +1 -0
  474. package/dist/providers/moonshotai-cn.js +15 -0
  475. package/dist/providers/moonshotai-cn.js.map +1 -0
  476. package/dist/providers/moonshotai-cn.models.d.ts +4 -0
  477. package/dist/providers/moonshotai-cn.models.d.ts.map +1 -0
  478. package/dist/providers/moonshotai-cn.models.js +6 -0
  479. package/dist/providers/moonshotai-cn.models.js.map +1 -0
  480. package/dist/providers/moonshotai.d.ts +3 -0
  481. package/dist/providers/moonshotai.d.ts.map +1 -0
  482. package/dist/providers/moonshotai.js +15 -0
  483. package/dist/providers/moonshotai.js.map +1 -0
  484. package/dist/providers/moonshotai.models.d.ts +4 -0
  485. package/dist/providers/moonshotai.models.d.ts.map +1 -0
  486. package/dist/providers/moonshotai.models.js +6 -0
  487. package/dist/providers/moonshotai.models.js.map +1 -0
  488. package/dist/providers/nvidia.d.ts +3 -0
  489. package/dist/providers/nvidia.d.ts.map +1 -0
  490. package/dist/providers/nvidia.js +15 -0
  491. package/dist/providers/nvidia.js.map +1 -0
  492. package/dist/providers/nvidia.models.d.ts +4 -0
  493. package/dist/providers/nvidia.models.d.ts.map +1 -0
  494. package/dist/providers/nvidia.models.js +6 -0
  495. package/dist/providers/nvidia.models.js.map +1 -0
  496. package/dist/providers/openai-codex.d.ts +3 -0
  497. package/dist/providers/openai-codex.d.ts.map +1 -0
  498. package/dist/providers/openai-codex.js +22 -0
  499. package/dist/providers/openai-codex.js.map +1 -0
  500. package/dist/providers/openai-codex.models.d.ts +4 -0
  501. package/dist/providers/openai-codex.models.d.ts.map +1 -0
  502. package/dist/providers/openai-codex.models.js +6 -0
  503. package/dist/providers/openai-codex.models.js.map +1 -0
  504. package/dist/providers/openai.d.ts +3 -0
  505. package/dist/providers/openai.d.ts.map +1 -0
  506. package/dist/providers/openai.js +15 -0
  507. package/dist/providers/openai.js.map +1 -0
  508. package/dist/providers/openai.models.d.ts +4 -0
  509. package/dist/providers/openai.models.d.ts.map +1 -0
  510. package/dist/providers/openai.models.js +6 -0
  511. package/dist/providers/openai.models.js.map +1 -0
  512. package/dist/providers/opencode-go.d.ts +3 -0
  513. package/dist/providers/opencode-go.d.ts.map +1 -0
  514. package/dist/providers/opencode-go.js +20 -0
  515. package/dist/providers/opencode-go.js.map +1 -0
  516. package/dist/providers/opencode-go.models.d.ts +4 -0
  517. package/dist/providers/opencode-go.models.d.ts.map +1 -0
  518. package/dist/providers/opencode-go.models.js +6 -0
  519. package/dist/providers/opencode-go.models.js.map +1 -0
  520. package/dist/providers/opencode.d.ts +3 -0
  521. package/dist/providers/opencode.d.ts.map +1 -0
  522. package/dist/providers/opencode.js +22 -0
  523. package/dist/providers/opencode.js.map +1 -0
  524. package/dist/providers/opencode.models.d.ts +4 -0
  525. package/dist/providers/opencode.models.d.ts.map +1 -0
  526. package/dist/providers/opencode.models.js +6 -0
  527. package/dist/providers/opencode.models.js.map +1 -0
  528. package/dist/providers/openrouter-images.d.ts +3 -0
  529. package/dist/providers/openrouter-images.d.ts.map +1 -0
  530. package/dist/providers/openrouter-images.js +22 -0
  531. package/dist/providers/openrouter-images.js.map +1 -0
  532. package/dist/providers/openrouter.d.ts +3 -0
  533. package/dist/providers/openrouter.d.ts.map +1 -0
  534. package/dist/providers/openrouter.js +23 -0
  535. package/dist/providers/openrouter.js.map +1 -0
  536. package/dist/providers/openrouter.models.d.ts +4 -0
  537. package/dist/providers/openrouter.models.d.ts.map +1 -0
  538. package/dist/providers/openrouter.models.js +6 -0
  539. package/dist/providers/openrouter.models.js.map +1 -0
  540. package/dist/providers/qwen-token-plan-cn.d.ts +3 -0
  541. package/dist/providers/qwen-token-plan-cn.d.ts.map +1 -0
  542. package/dist/providers/qwen-token-plan-cn.js +15 -0
  543. package/dist/providers/qwen-token-plan-cn.js.map +1 -0
  544. package/dist/providers/qwen-token-plan-cn.models.d.ts +4 -0
  545. package/dist/providers/qwen-token-plan-cn.models.d.ts.map +1 -0
  546. package/dist/providers/qwen-token-plan-cn.models.js +6 -0
  547. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -0
  548. package/dist/providers/qwen-token-plan-individual.d.ts +3 -0
  549. package/dist/providers/qwen-token-plan-individual.d.ts.map +1 -0
  550. package/dist/providers/qwen-token-plan-individual.js +15 -0
  551. package/dist/providers/qwen-token-plan-individual.js.map +1 -0
  552. package/dist/providers/qwen-token-plan-individual.models.d.ts +4 -0
  553. package/dist/providers/qwen-token-plan-individual.models.d.ts.map +1 -0
  554. package/dist/providers/qwen-token-plan-individual.models.js +6 -0
  555. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -0
  556. package/dist/providers/qwen-token-plan.d.ts +3 -0
  557. package/dist/providers/qwen-token-plan.d.ts.map +1 -0
  558. package/dist/providers/qwen-token-plan.js +15 -0
  559. package/dist/providers/qwen-token-plan.js.map +1 -0
  560. package/dist/providers/qwen-token-plan.models.d.ts +4 -0
  561. package/dist/providers/qwen-token-plan.models.d.ts.map +1 -0
  562. package/dist/providers/qwen-token-plan.models.js +6 -0
  563. package/dist/providers/qwen-token-plan.models.js.map +1 -0
  564. package/dist/providers/radius-config.d.ts +26 -0
  565. package/dist/providers/radius-config.d.ts.map +1 -0
  566. package/dist/providers/radius-config.js +63 -0
  567. package/dist/providers/radius-config.js.map +1 -0
  568. package/dist/providers/radius.d.ts +9 -0
  569. package/dist/providers/radius.d.ts.map +1 -0
  570. package/dist/providers/radius.js +64 -0
  571. package/dist/providers/radius.js.map +1 -0
  572. package/dist/providers/together.d.ts +3 -0
  573. package/dist/providers/together.d.ts.map +1 -0
  574. package/dist/providers/together.js +15 -0
  575. package/dist/providers/together.js.map +1 -0
  576. package/dist/providers/together.models.d.ts +4 -0
  577. package/dist/providers/together.models.d.ts.map +1 -0
  578. package/dist/providers/together.models.js +6 -0
  579. package/dist/providers/together.models.js.map +1 -0
  580. package/dist/providers/vercel-ai-gateway.d.ts +3 -0
  581. package/dist/providers/vercel-ai-gateway.d.ts.map +1 -0
  582. package/dist/providers/vercel-ai-gateway.js +15 -0
  583. package/dist/providers/vercel-ai-gateway.js.map +1 -0
  584. package/dist/providers/vercel-ai-gateway.models.d.ts +4 -0
  585. package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -0
  586. package/dist/providers/vercel-ai-gateway.models.js +6 -0
  587. package/dist/providers/vercel-ai-gateway.models.js.map +1 -0
  588. package/dist/providers/xai.d.ts +3 -0
  589. package/dist/providers/xai.d.ts.map +1 -0
  590. package/dist/providers/xai.js +28 -0
  591. package/dist/providers/xai.js.map +1 -0
  592. package/dist/providers/xai.models.d.ts +4 -0
  593. package/dist/providers/xai.models.d.ts.map +1 -0
  594. package/dist/providers/xai.models.js +6 -0
  595. package/dist/providers/xai.models.js.map +1 -0
  596. package/dist/providers/xiaomi-token-plan-ams.d.ts +3 -0
  597. package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -0
  598. package/dist/providers/xiaomi-token-plan-ams.js +15 -0
  599. package/dist/providers/xiaomi-token-plan-ams.js.map +1 -0
  600. package/dist/providers/xiaomi-token-plan-ams.models.d.ts +4 -0
  601. package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -0
  602. package/dist/providers/xiaomi-token-plan-ams.models.js +6 -0
  603. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -0
  604. package/dist/providers/xiaomi-token-plan-cn.d.ts +3 -0
  605. package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -0
  606. package/dist/providers/xiaomi-token-plan-cn.js +15 -0
  607. package/dist/providers/xiaomi-token-plan-cn.js.map +1 -0
  608. package/dist/providers/xiaomi-token-plan-cn.models.d.ts +4 -0
  609. package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -0
  610. package/dist/providers/xiaomi-token-plan-cn.models.js +6 -0
  611. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -0
  612. package/dist/providers/xiaomi-token-plan-sgp.d.ts +3 -0
  613. package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -0
  614. package/dist/providers/xiaomi-token-plan-sgp.js +15 -0
  615. package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -0
  616. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +4 -0
  617. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -0
  618. package/dist/providers/xiaomi-token-plan-sgp.models.js +6 -0
  619. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -0
  620. package/dist/providers/xiaomi.d.ts +3 -0
  621. package/dist/providers/xiaomi.d.ts.map +1 -0
  622. package/dist/providers/xiaomi.js +15 -0
  623. package/dist/providers/xiaomi.js.map +1 -0
  624. package/dist/providers/xiaomi.models.d.ts +4 -0
  625. package/dist/providers/xiaomi.models.d.ts.map +1 -0
  626. package/dist/providers/xiaomi.models.js +6 -0
  627. package/dist/providers/xiaomi.models.js.map +1 -0
  628. package/dist/providers/zai-coding-cn.d.ts +3 -0
  629. package/dist/providers/zai-coding-cn.d.ts.map +1 -0
  630. package/dist/providers/zai-coding-cn.js +15 -0
  631. package/dist/providers/zai-coding-cn.js.map +1 -0
  632. package/dist/providers/zai-coding-cn.models.d.ts +4 -0
  633. package/dist/providers/zai-coding-cn.models.d.ts.map +1 -0
  634. package/dist/providers/zai-coding-cn.models.js +6 -0
  635. package/dist/providers/zai-coding-cn.models.js.map +1 -0
  636. package/dist/providers/zai.d.ts +3 -0
  637. package/dist/providers/zai.d.ts.map +1 -0
  638. package/dist/providers/zai.js +15 -0
  639. package/dist/providers/zai.js.map +1 -0
  640. package/dist/providers/zai.models.d.ts +4 -0
  641. package/dist/providers/zai.models.d.ts.map +1 -0
  642. package/dist/providers/zai.models.js +6 -0
  643. package/dist/providers/zai.models.js.map +1 -0
  644. package/dist/session-resources.d.ts +4 -0
  645. package/dist/session-resources.d.ts.map +1 -0
  646. package/dist/session-resources.js +22 -0
  647. package/dist/session-resources.js.map +1 -0
  648. package/dist/types.d.ts +361 -49
  649. package/dist/types.d.ts.map +1 -1
  650. package/dist/types.js.map +1 -1
  651. package/dist/utils/abort-signals.d.ts +6 -0
  652. package/dist/utils/abort-signals.d.ts.map +1 -0
  653. package/dist/utils/abort-signals.js +34 -0
  654. package/dist/utils/abort-signals.js.map +1 -0
  655. package/dist/utils/abort.d.ts +8 -0
  656. package/dist/utils/abort.d.ts.map +1 -0
  657. package/dist/utils/abort.js +49 -0
  658. package/dist/utils/abort.js.map +1 -0
  659. package/dist/utils/deferred-tools.d.ts +9 -0
  660. package/dist/utils/deferred-tools.d.ts.map +1 -0
  661. package/dist/utils/deferred-tools.js +36 -0
  662. package/dist/utils/deferred-tools.js.map +1 -0
  663. package/dist/utils/diagnostics.d.ts +19 -0
  664. package/dist/utils/diagnostics.d.ts.map +1 -0
  665. package/dist/utils/diagnostics.js +25 -0
  666. package/dist/utils/diagnostics.js.map +1 -0
  667. package/dist/utils/error-body.d.ts +25 -0
  668. package/dist/utils/error-body.d.ts.map +1 -0
  669. package/dist/utils/error-body.js +133 -0
  670. package/dist/utils/error-body.js.map +1 -0
  671. package/dist/utils/estimate.d.ts +17 -0
  672. package/dist/utils/estimate.d.ts.map +1 -0
  673. package/dist/utils/estimate.js +116 -0
  674. package/dist/utils/estimate.js.map +1 -0
  675. package/dist/utils/event-stream.d.ts +3 -3
  676. package/dist/utils/event-stream.d.ts.map +1 -1
  677. package/dist/utils/event-stream.js +2 -2
  678. package/dist/utils/event-stream.js.map +1 -1
  679. package/dist/utils/headers.d.ts +2 -0
  680. package/dist/utils/headers.d.ts.map +1 -1
  681. package/dist/utils/headers.js +10 -0
  682. package/dist/utils/headers.js.map +1 -1
  683. package/dist/utils/node-http-proxy.d.ts +4 -0
  684. package/dist/utils/node-http-proxy.d.ts.map +1 -0
  685. package/dist/utils/node-http-proxy.js +92 -0
  686. package/dist/utils/node-http-proxy.js.map +1 -0
  687. package/dist/utils/overflow.d.ts +19 -4
  688. package/dist/utils/overflow.d.ts.map +1 -1
  689. package/dist/utils/overflow.js +44 -4
  690. package/dist/utils/overflow.js.map +1 -1
  691. package/dist/utils/pi-user-agent.d.ts +2 -0
  692. package/dist/utils/pi-user-agent.d.ts.map +1 -0
  693. package/dist/utils/pi-user-agent.js +12 -0
  694. package/dist/utils/pi-user-agent.js.map +1 -0
  695. package/dist/utils/provider-env.d.ts +7 -0
  696. package/dist/utils/provider-env.d.ts.map +1 -0
  697. package/dist/utils/provider-env.js +44 -0
  698. package/dist/utils/provider-env.js.map +1 -0
  699. package/dist/utils/provider-retry.d.ts +16 -0
  700. package/dist/utils/provider-retry.d.ts.map +1 -0
  701. package/dist/utils/provider-retry.js +94 -0
  702. package/dist/utils/provider-retry.js.map +1 -0
  703. package/dist/utils/retry.d.ts +53 -0
  704. package/dist/utils/retry.d.ts.map +1 -0
  705. package/dist/utils/retry.js +174 -0
  706. package/dist/utils/retry.js.map +1 -0
  707. package/dist/utils/text.d.ts +6 -0
  708. package/dist/utils/text.d.ts.map +1 -0
  709. package/dist/utils/text.js +10 -0
  710. package/dist/utils/text.js.map +1 -0
  711. package/dist/utils/uuid.d.ts +3 -0
  712. package/dist/utils/uuid.d.ts.map +1 -0
  713. package/dist/utils/uuid.js +46 -0
  714. package/dist/utils/uuid.js.map +1 -0
  715. package/dist/utils/validation.d.ts +1 -1
  716. package/dist/utils/validation.d.ts.map +1 -1
  717. package/dist/utils/validation.js +49 -21
  718. package/dist/utils/validation.js.map +1 -1
  719. package/package.json +52 -55
  720. package/dist/api-registry.d.ts +0 -20
  721. package/dist/api-registry.d.ts.map +0 -1
  722. package/dist/api-registry.js +0 -44
  723. package/dist/api-registry.js.map +0 -1
  724. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  725. package/dist/providers/github-copilot-headers.js.map +0 -1
  726. package/dist/providers/google-gemini-cli.d.ts +0 -74
  727. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  728. package/dist/providers/google-gemini-cli.js +0 -779
  729. package/dist/providers/google-gemini-cli.js.map +0 -1
  730. package/dist/providers/google-shared.d.ts.map +0 -1
  731. package/dist/providers/google-shared.js.map +0 -1
  732. package/dist/providers/openai-codex-responses.d.ts +0 -11
  733. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  734. package/dist/providers/openai-codex-responses.js +0 -779
  735. package/dist/providers/openai-codex-responses.js.map +0 -1
  736. package/dist/providers/openai-completions.d.ts +0 -19
  737. package/dist/providers/openai-completions.d.ts.map +0 -1
  738. package/dist/providers/openai-completions.js +0 -876
  739. package/dist/providers/openai-completions.js.map +0 -1
  740. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  741. package/dist/providers/openai-responses-shared.js +0 -483
  742. package/dist/providers/openai-responses-shared.js.map +0 -1
  743. package/dist/providers/openai-responses.d.ts.map +0 -1
  744. package/dist/providers/openai-responses.js +0 -208
  745. package/dist/providers/openai-responses.js.map +0 -1
  746. package/dist/providers/register-builtins.d.ts +0 -38
  747. package/dist/providers/register-builtins.d.ts.map +0 -1
  748. package/dist/providers/register-builtins.js +0 -261
  749. package/dist/providers/register-builtins.js.map +0 -1
  750. package/dist/providers/simple-options.d.ts +0 -8
  751. package/dist/providers/simple-options.d.ts.map +0 -1
  752. package/dist/providers/simple-options.js +0 -36
  753. package/dist/providers/simple-options.js.map +0 -1
  754. package/dist/providers/transform-messages.d.ts.map +0 -1
  755. package/dist/providers/transform-messages.js.map +0 -1
  756. package/dist/stream.d.ts +0 -8
  757. package/dist/stream.d.ts.map +0 -1
  758. package/dist/stream.js +0 -27
  759. package/dist/stream.js.map +0 -1
  760. package/dist/utils/oauth/anthropic.d.ts +0 -25
  761. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  762. package/dist/utils/oauth/anthropic.js.map +0 -1
  763. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  764. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  765. package/dist/utils/oauth/github-copilot.js +0 -292
  766. package/dist/utils/oauth/github-copilot.js.map +0 -1
  767. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  768. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  769. package/dist/utils/oauth/google-antigravity.js +0 -377
  770. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  771. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  772. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  773. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  774. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  775. package/dist/utils/oauth/index.d.ts +0 -61
  776. package/dist/utils/oauth/index.d.ts.map +0 -1
  777. package/dist/utils/oauth/index.js +0 -131
  778. package/dist/utils/oauth/index.js.map +0 -1
  779. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  780. package/dist/utils/oauth/oauth-page.js.map +0 -1
  781. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  782. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  783. package/dist/utils/oauth/openai-codex.js +0 -374
  784. package/dist/utils/oauth/openai-codex.js.map +0 -1
  785. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  786. package/dist/utils/oauth/pkce.js.map +0 -1
  787. package/dist/utils/oauth/types.d.ts +0 -47
  788. package/dist/utils/oauth/types.d.ts.map +0 -1
  789. package/dist/utils/oauth/types.js.map +0 -1
  790. /package/dist/{providers → api}/github-copilot-headers.js +0 -0
  791. /package/dist/{utils → auth}/oauth/oauth-page.d.ts +0 -0
  792. /package/dist/{utils → auth}/oauth/oauth-page.js +0 -0
  793. /package/dist/{utils → auth}/oauth/pkce.d.ts +0 -0
  794. /package/dist/{utils → auth}/oauth/pkce.js +0 -0
  795. /package/dist/{utils/oauth → auth}/types.js +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # @mariozechner/pi-ai
1
+ # @sammorrowdrums/mcpi-ai
2
2
 
3
- Unified LLM API with automatic model discovery, provider configuration, token and cost tracking, and simple context persistence and hand-off to other models mid-session.
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
5
  **Note**: This library only includes models that support tool calling (function calling), as this is essential for agentic workflows.
6
6
 
@@ -9,6 +9,17 @@ Unified LLM API with automatic model discovery, provider configuration, token an
9
9
  - [Supported Providers](#supported-providers)
10
10
  - [Installation](#installation)
11
11
  - [Quick Start](#quick-start)
12
+ - [Providers and Models](#providers-and-models)
13
+ - [Provider Factories](#provider-factories)
14
+ - [All Built-in Providers](#all-built-in-providers)
15
+ - [Querying Models](#querying-models)
16
+ - [Static Catalog Reads](#static-catalog-reads)
17
+ - [Dynamic Providers](#dynamic-providers)
18
+ - [Auth](#auth)
19
+ - [How Auth Resolves](#how-auth-resolves)
20
+ - [Transforming Request Headers](#transforming-request-headers)
21
+ - [Credential Store](#credential-store)
22
+ - [Environment Variables](#environment-variables)
12
23
  - [Tools](#tools)
13
24
  - [Defining Tools](#defining-tools)
14
25
  - [Handling Tool Calls](#handling-tool-calls)
@@ -16,6 +27,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
16
27
  - [Validating Tool Arguments](#validating-tool-arguments)
17
28
  - [Complete Event Reference](#complete-event-reference)
18
29
  - [Image Input](#image-input)
30
+ - [Image Generation](#image-generation)
19
31
  - [Thinking/Reasoning](#thinkingreasoning)
20
32
  - [Unified Interface](#unified-interface-streamsimplecompletesimple)
21
33
  - [Provider-Specific Options](#provider-specific-options-streamcomplete)
@@ -24,67 +36,80 @@ Unified LLM API with automatic model discovery, provider configuration, token an
24
36
  - [Error Handling](#error-handling)
25
37
  - [Aborting Requests](#aborting-requests)
26
38
  - [Continuing After Abort](#continuing-after-abort)
27
- - [APIs, Models, and Providers](#apis-models-and-providers)
28
- - [Providers and Models](#providers-and-models)
29
- - [Querying Providers and Models](#querying-providers-and-models)
30
- - [Custom Models](#custom-models)
39
+ - [Debugging Provider Payloads](#debugging-provider-payloads)
40
+ - [Custom Providers](#custom-providers)
41
+ - [createProvider()](#createprovider)
42
+ - [Calling API Implementations Directly](#calling-api-implementations-directly)
31
43
  - [OpenAI Compatibility Settings](#openai-compatibility-settings)
32
- - [Type Safety](#type-safety)
44
+ - [Faux Provider for Tests](#faux-provider-for-tests)
33
45
  - [Cross-Provider Handoffs](#cross-provider-handoffs)
34
46
  - [Context Serialization](#context-serialization)
35
47
  - [Browser Usage](#browser-usage)
36
- - [Browser Compatibility Notes](#browser-compatibility-notes)
37
- - [Environment Variables](#environment-variables-nodejs-only)
38
- - [Checking Environment Variables](#checking-environment-variables)
48
+ - [Bundling and Tree Shaking](#bundling-and-tree-shaking)
39
49
  - [OAuth Providers](#oauth-providers)
40
50
  - [Vertex AI](#vertex-ai)
41
51
  - [CLI Login](#cli-login)
42
52
  - [Programmatic OAuth](#programmatic-oauth)
43
- - [Login Flow Example](#login-flow-example)
44
- - [Using OAuth Tokens](#using-oauth-tokens)
45
- - [Provider Notes](#provider-notes)
53
+ - [Migrating from the Old Global API](#migrating-from-the-old-global-api)
54
+ - [Development](#development)
46
55
  - [License](#license)
47
56
 
48
57
  ## Supported Providers
49
58
 
50
59
  - **OpenAI**
60
+ - **Ant Ling**
51
61
  - **Azure OpenAI (Responses)**
52
62
  - **OpenAI Codex** (ChatGPT Plus/Pro subscription, requires OAuth, see below)
63
+ - **DeepSeek**
64
+ - **NVIDIA NIM**
53
65
  - **Anthropic**
54
66
  - **Google**
55
67
  - **Vertex AI** (Gemini via Vertex AI)
56
68
  - **Mistral**
57
69
  - **Groq**
58
70
  - **Cerebras**
71
+ - **Cloudflare AI Gateway**
72
+ - **Cloudflare Workers AI**
59
73
  - **xAI**
60
74
  - **OpenRouter**
61
75
  - **Vercel AI Gateway**
62
- - **MiniMax**
76
+ - **ZAI Coding Plan (Global)** (with separate China provider)
77
+ - **MiniMax** (with separate China provider)
78
+ - **Together AI**
79
+ - **Baseten**
80
+ - **Hugging Face**
81
+ - **Moonshot AI** (with separate China provider)
63
82
  - **GitHub Copilot** (requires OAuth, see below)
64
- - **Google Gemini CLI** (requires OAuth, see below)
65
- - **Antigravity** (requires OAuth, see below)
66
83
  - **Amazon Bedrock**
67
84
  - **OpenCode Zen**
68
85
  - **OpenCode Go**
69
- - **Fireworks** (uses Anthropic-compatible API)
70
- - **Kimi For Coding** (Moonshot AI, uses Anthropic-compatible API)
86
+ - **Fireworks** (uses OpenAI- and Anthropic-compatible APIs)
87
+ - **Kimi For Coding** (Moonshot AI subscription endpoint, uses Anthropic-compatible API)
88
+ - **Qwen Token Plan** (separate Individual and existing catalogs, with a separate China provider)
89
+ - **Xiaomi MiMo** (defaults to API billing endpoint, with separate Token Plan providers for `cn`/`ams`/`sgp` regions)
71
90
  - **Any OpenAI-compatible API**: Ollama, vLLM, LM Studio, etc.
72
91
 
73
92
  ## Installation
74
93
 
75
94
  ```bash
76
- npm install @mariozechner/pi-ai
95
+ npm install @sammorrowdrums/mcpi-ai
77
96
  ```
78
97
 
79
- TypeBox exports are re-exported from `@mariozechner/pi-ai`: `Type`, `Static`, and `TSchema`.
98
+ TypeBox exports are re-exported from `@sammorrowdrums/mcpi-ai`: `Type`, `Static`, and `TSchema`.
80
99
 
81
100
  ## Quick Start
82
101
 
102
+ You build a `Models` collection of providers and stream through it. The quickest start registers every built-in provider; apps that care about bundle size register individual providers instead (see [Provider Factories](#provider-factories) and [Bundling and Tree Shaking](#bundling-and-tree-shaking)).
103
+
83
104
  ```typescript
84
- import { Type, getModel, stream, complete, Context, Tool, StringEnum } from '@mariozechner/pi-ai';
105
+ import { Type, type Context, type Tool } from '@sammorrowdrums/mcpi-ai';
106
+ import { builtinModels } from '@sammorrowdrums/mcpi-ai/providers/all';
107
+
108
+ // A Models collection with every built-in provider registered
109
+ const models = builtinModels();
85
110
 
86
- // Fully typed with auto-complete support for both providers and models
87
- const model = getModel('openai', 'gpt-4o-mini');
111
+ // Sync lookup against the collection
112
+ const model = models.getModel('openai', 'gpt-4o-mini')!;
88
113
 
89
114
  // Define tools with TypeBox schemas for type safety and validation
90
115
  const tools: Tool[] = [{
@@ -98,12 +123,13 @@ const tools: Tool[] = [{
98
123
  // Build a conversation context (easily serializable and transferable between models)
99
124
  const context: Context = {
100
125
  systemPrompt: 'You are a helpful assistant.',
101
- messages: [{ role: 'user', content: 'What time is it?' }],
126
+ messages: [{ role: 'user', content: 'What time is it?', timestamp: Date.now() }],
102
127
  tools
103
128
  };
104
129
 
105
- // Option 1: Streaming with all event types
106
- const s = stream(model, context);
130
+ // Option 1: Streaming with all event types.
131
+ // Auth resolves through the provider (OPENAI_API_KEY from the environment here).
132
+ const s = models.stream(model, context);
107
133
 
108
134
  for await (const event of s) {
109
135
  switch (event.type) {
@@ -146,7 +172,7 @@ for await (const event of s) {
146
172
  console.log(`\nFinished: ${event.reason}`);
147
173
  break;
148
174
  case 'error':
149
- console.error(`Error: ${event.error}`);
175
+ console.error(`Error: ${event.error.errorMessage}`);
150
176
  break;
151
177
  }
152
178
  }
@@ -158,7 +184,6 @@ context.messages.push(finalMessage);
158
184
  // Handle tool calls if any
159
185
  const toolCalls = finalMessage.content.filter(b => b.type === 'toolCall');
160
186
  for (const call of toolCalls) {
161
- // Execute the tool
162
187
  const result = call.name === 'get_time'
163
188
  ? new Date().toLocaleString('en-US', {
164
189
  timeZone: call.arguments.timezone || 'UTC',
@@ -180,7 +205,7 @@ for (const call of toolCalls) {
180
205
 
181
206
  // Continue if there were tool calls
182
207
  if (toolCalls.length > 0) {
183
- const continuation = await complete(model, context);
208
+ const continuation = await models.complete(model, context);
184
209
  context.messages.push(continuation);
185
210
  console.log('After tool execution:', continuation.content);
186
211
  }
@@ -189,7 +214,7 @@ console.log(`Total tokens: ${finalMessage.usage.input} in, ${finalMessage.usage.
189
214
  console.log(`Cost: $${finalMessage.usage.cost.total.toFixed(4)}`);
190
215
 
191
216
  // Option 2: Get complete response without streaming
192
- const response = await complete(model, context);
217
+ const response = await models.complete(model, context);
193
218
 
194
219
  for (const block of response.content) {
195
220
  if (block.type === 'text') {
@@ -200,6 +225,236 @@ for (const block of response.content) {
200
225
  }
201
226
  ```
202
227
 
228
+ Snippets in the rest of this README assume a `models` collection set up like this (with the relevant providers registered).
229
+
230
+ ## Providers and Models
231
+
232
+ A **provider** is the runtime unit: it owns its model catalog, its auth (API key resolution, OAuth flows), and its stream behavior. A `Models` collection holds providers and routes every request to the provider that owns the model.
233
+
234
+ Providers internally share **API implementations** (the wire protocols): Anthropic models use `anthropic-messages`, OpenAI uses `openai-responses`, while xAI, Groq, Cerebras, OpenRouter, and most others share `openai-completions`. Mixed-API providers (GitHub Copilot, OpenCode Zen) dispatch per model.
235
+
236
+ ### Provider Factories
237
+
238
+ For apps that only need specific providers, there is one factory per built-in provider, each a subpath import that pulls only that provider's catalog:
239
+
240
+ ```typescript
241
+ import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
242
+ import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
243
+ import { openrouterProvider } from '@sammorrowdrums/mcpi-ai/providers/openrouter';
244
+ import { amazonBedrockProvider } from '@sammorrowdrums/mcpi-ai/providers/amazon-bedrock';
245
+ // ...one module per provider in the Supported Providers list
246
+
247
+ const models = createModels();
248
+ models.setProvider(anthropicProvider());
249
+ models.setProvider(openrouterProvider());
250
+ ```
251
+
252
+ Provider factories import their model catalog and a lazy API wrapper. They do not import other providers. With bundler code splitting, SDK implementations (`@anthropic-ai/sdk`, `openai`, `@google/genai`, etc.) stay in lazy chunks loaded on the first request to a model of that API.
253
+
254
+ ### All Built-in Providers
255
+
256
+ For apps that want everything (as in Quick Start):
257
+
258
+ ```typescript
259
+ import { builtinModels } from '@sammorrowdrums/mcpi-ai/providers/all';
260
+
261
+ const models = builtinModels(); // a Models collection with every built-in provider registered
262
+ ```
263
+
264
+ This imports all catalogs and every built-in provider factory. It is the heavy, explicit entrypoint. `builtinModels()` accepts the same options as `createModels()` (`credentials`, `authContext`); `builtinProviders()` returns the provider array if you want to register them on your own collection.
265
+
266
+ ### Querying Models
267
+
268
+ Reads are synchronous and return the last-known lists:
269
+
270
+ ```typescript
271
+ const providers = models.getProviders(); // registered Provider objects
272
+ const provider = models.getProvider('anthropic'); // one provider
273
+
274
+ const all = models.getModels(); // every model across providers
275
+ const anthropicModels = models.getModels('anthropic');
276
+ const model = models.getModel('anthropic', 'claude-sonnet-4-5');
277
+
278
+ for (const m of anthropicModels) {
279
+ console.log(`${m.id}: ${m.name}`);
280
+ console.log(` API: ${m.api}`);
281
+ console.log(` Context: ${m.contextWindow} tokens`);
282
+ console.log(` Vision: ${m.input.includes('image')}`);
283
+ console.log(` Reasoning: ${m.reasoning}`);
284
+ }
285
+ ```
286
+
287
+ Dynamically listed models are typed `Model<Api>`. Narrow with the `hasApi()` guard when you need API-specific option typing:
288
+
289
+ ```typescript
290
+ import { hasApi } from '@sammorrowdrums/mcpi-ai';
291
+
292
+ const m = models.getModel('anthropic', 'claude-sonnet-4-5');
293
+ if (m && hasApi(m, 'anthropic-messages')) {
294
+ // m: Model<'anthropic-messages'> — stream options fully typed
295
+ models.stream(m, context, { thinkingEnabled: true, thinkingBudgetTokens: 2048 });
296
+ }
297
+ ```
298
+
299
+ ### Static Catalog Reads
300
+
301
+ For tooling that wants the generated built-in catalog with full literal typing (provider and model IDs auto-complete), independent of any collection:
302
+
303
+ ```typescript
304
+ import { getBuiltinModel, getBuiltinModels, getBuiltinProviders } from '@sammorrowdrums/mcpi-ai/providers/all';
305
+
306
+ const model = getBuiltinModel('openai', 'gpt-4o-mini'); // typed Model<'openai-responses'>
307
+ const providers = getBuiltinProviders();
308
+ const anthropic = getBuiltinModels('anthropic');
309
+ ```
310
+
311
+ ### Dynamic Providers
312
+
313
+ Providers may have dynamic model lists (a llama.cpp server, a live OpenRouter listing). Reads stay sync; fetching is an explicit async verb:
314
+
315
+ ```typescript
316
+ // getModels() returns the last-known list (empty before the first refresh)
317
+ await models.refresh({ providers: ['llamacpp'] }); // refresh one provider
318
+ await models.refresh(); // refresh all providers concurrently, best-effort
319
+ const fresh = models.getModel('llamacpp', 'qwen3-30b');
320
+ ```
321
+
322
+ Static built-in providers are no-ops for `refresh()`. See [createProvider()](#createprovider) for building a dynamic provider.
323
+
324
+ ## Auth
325
+
326
+ Every provider owns its auth: how API keys resolve (stored credentials, environment variables, ambient sources like AWS profiles or gcloud ADC) and, where supported, OAuth login/refresh flows.
327
+
328
+ ### How Auth Resolves
329
+
330
+ When you call `models.stream()`, the collection resolves auth through the owning provider and merges it into the request. Explicit per-request values always win:
331
+
332
+ ```typescript
333
+ // Resolved through the provider (env var, stored credential, OAuth token):
334
+ await models.complete(model, context);
335
+
336
+ // Explicit key wins over anything the provider would resolve:
337
+ await models.complete(model, context, { apiKey: 'sk-explicit' });
338
+ ```
339
+
340
+ You can inspect resolution without making a request. Pass a provider ID for provider-scoped auth, or a model to include its static `model.headers`:
341
+
342
+ ```typescript
343
+ const providerAuth = await models.getAuth(model.provider);
344
+ const modelAuth = await models.getAuth(model);
345
+
346
+ if (modelAuth) {
347
+ console.log(`configured via ${modelAuth.source}`); // e.g. "ANTHROPIC_API_KEY", "OAuth", "stored credential"
348
+ console.log(modelAuth.auth.headers); // Provider auth headers + model.headers
349
+ } else {
350
+ console.log('not configured');
351
+ }
352
+ ```
353
+
354
+ 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.
355
+
356
+ `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.
357
+
358
+ ### Transforming Request Headers
359
+
360
+ `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:
361
+
362
+ ```typescript
363
+ const response = await models.completeSimple(model, context, {
364
+ headers: { "X-Client": "my-app" },
365
+ transformHeaders: async (headers) => ({
366
+ ...headers,
367
+ "X-Request-ID": crypto.randomUUID(),
368
+ }),
369
+ });
370
+ ```
371
+
372
+ The ordering is:
373
+
374
+ ```text
375
+ provider auth headers -> model.headers -> explicit options.headers -> transformHeaders -> Provider.stream*()
376
+ ```
377
+
378
+ Header names are merged case-insensitively. Explicit headers override auth/model headers, and the transform has final control; returning `null` for a header suppresses lower-level defaults that support deletion.
379
+
380
+ `transformHeaders` belongs to `Models`, not `Provider`. A `Models` implementation must consume it and remove it before calling `Provider.stream*()`. Provider implementations continue receiving ordinary `ApiStreamOptions` or `SimpleStreamOptions` and never handle the transform themselves. Use this option instead of calling `getAuth(model)` before `stream*()`, which would resolve request auth twice.
381
+
382
+ ### Credential Store
383
+
384
+ Stored credentials (API keys entered interactively, OAuth tokens) live in a `CredentialStore` — one type-tagged credential per provider. mcpi-ai ships an in-memory default; apps inject persistent storage:
385
+
386
+ ```typescript
387
+ import { createModels, type CredentialStore } from '@sammorrowdrums/mcpi-ai';
388
+
389
+ const models = createModels({ credentials: myFileBackedStore });
390
+ // builtinModels() takes the same options:
391
+ // const models = builtinModels({ credentials: myFileBackedStore });
392
+ ```
393
+
394
+ 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.
395
+
396
+ API-key credentials use the same discriminator as mcpi's `auth.json` and can carry provider-scoped env/config values:
397
+
398
+ ```typescript
399
+ const credential = {
400
+ type: 'api_key',
401
+ key: '...',
402
+ env: {
403
+ CLOUDFLARE_ACCOUNT_ID: 'account-id',
404
+ CLOUDFLARE_GATEWAY_ID: 'gateway-id'
405
+ }
406
+ } as const;
407
+ ```
408
+
409
+ ### Environment Variables
410
+
411
+ Built-in providers resolve these env vars (Node.js; in browsers pass `apiKey` explicitly):
412
+
413
+ | Provider | Environment Variable(s) |
414
+ |----------|------------------------|
415
+ | OpenAI | `OPENAI_API_KEY` |
416
+ | Ant Ling | `ANT_LING_API_KEY` |
417
+ | 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`. |
418
+ | Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
419
+ | DeepSeek | `DEEPSEEK_API_KEY` |
420
+ | NVIDIA NIM | `NVIDIA_API_KEY` |
421
+ | Google | `GEMINI_API_KEY` |
422
+ | Vertex AI | `GOOGLE_CLOUD_API_KEY` or `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) + `GOOGLE_CLOUD_LOCATION` + ADC |
423
+ | Mistral | `MISTRAL_API_KEY` |
424
+ | Groq | `GROQ_API_KEY` |
425
+ | Cerebras | `CEREBRAS_API_KEY` |
426
+ | Cloudflare AI Gateway | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
427
+ | Cloudflare Workers AI | `CLOUDFLARE_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` |
428
+ | xAI | `XAI_API_KEY` |
429
+ | Fireworks | `FIREWORKS_API_KEY` |
430
+ | Together AI | `TOGETHER_API_KEY` |
431
+ | Baseten | `BASETEN_API_KEY` |
432
+ | OpenRouter | `OPENROUTER_API_KEY` |
433
+ | Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
434
+ | ZAI Coding Plan (Global) | `ZAI_API_KEY` |
435
+ | ZAI Coding Plan (China) | `ZAI_CODING_CN_API_KEY` |
436
+ | MiniMax (Global) | `MINIMAX_API_KEY` |
437
+ | MiniMax (China) | `MINIMAX_CN_API_KEY` |
438
+ | Moonshot AI / Moonshot AI (China) | `MOONSHOT_API_KEY` |
439
+ | Hugging Face | `HF_TOKEN` |
440
+ | OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
441
+ | Kimi For Coding | `KIMI_API_KEY` |
442
+ | Qwen Token Plan (existing catalog) | `QWEN_TOKEN_PLAN_API_KEY` |
443
+ | Qwen Token Plan (Individual) | `QWEN_TOKEN_PLAN_API_KEY` |
444
+ | Qwen Token Plan (China) | `QWEN_TOKEN_PLAN_CN_API_KEY` |
445
+ | Xiaomi MiMo (API billing) | `XIAOMI_API_KEY` |
446
+ | Xiaomi MiMo Token Plan (China) | `XIAOMI_TOKEN_PLAN_CN_API_KEY` |
447
+ | Xiaomi MiMo Token Plan (Amsterdam) | `XIAOMI_TOKEN_PLAN_AMS_API_KEY` |
448
+ | Xiaomi MiMo Token Plan (Singapore) | `XIAOMI_TOKEN_PLAN_SGP_API_KEY` |
449
+ | GitHub Copilot | `COPILOT_GITHUB_TOKEN` |
450
+
451
+ `qwen-token-plan-individual` and `qwen-token-plan` share the international endpoint and
452
+ `QWEN_TOKEN_PLAN_API_KEY`. The Individual provider exposes only the models documented for Individual
453
+ subscriptions, while the existing provider retains its broader catalog for backward compatibility.
454
+ Stored credentials remain provider-scoped, so save the key under the provider ID you register.
455
+
456
+ 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.
457
+
203
458
  ## Tools
204
459
 
205
460
  Tools enable LLMs to interact with external systems. This library uses TypeBox schemas for type-safe tool definitions with automatic validation using TypeBox's built-in validator and value conversion utilities. TypeBox schemas can be serialized and deserialized as plain JSON, making them ideal for distributed systems.
@@ -207,7 +462,7 @@ Tools enable LLMs to interact with external systems. This library uses TypeBox s
207
462
  ### Defining Tools
208
463
 
209
464
  ```typescript
210
- import { Type, Tool, StringEnum } from '@mariozechner/pi-ai';
465
+ import { Type, type Tool, StringEnum } from '@sammorrowdrums/mcpi-ai';
211
466
 
212
467
  // Define tool parameters with TypeBox
213
468
  const weatherTool: Tool = {
@@ -234,6 +489,40 @@ const bookMeetingTool: Tool = {
234
489
  };
235
490
  ```
236
491
 
492
+ ### Constrained Sampling for Tools
493
+
494
+ Tools can opt in to provider-side constrained sampling. For JSON-schema tools, `strict: 'prefer'` uses provider-side strict schema enforcement when supported and otherwise falls back to normal tool calling. `strict: 'require'` fails the request when the active provider/model cannot honor it. Set `constrainedSampling: false` to explicitly opt out; it behaves the same as omitting the field.
495
+
496
+ ```typescript
497
+ const strictTool: Tool = {
498
+ name: 'edit_file',
499
+ description: 'Edit a file',
500
+ parameters: Type.Object({
501
+ path: Type.String(),
502
+ content: Type.String()
503
+ }, { additionalProperties: false }),
504
+ constrainedSampling: { type: 'json_schema', strict: 'prefer' }
505
+ };
506
+ ```
507
+
508
+ Strict JSON-schema constrained sampling is supported for OpenAI, Anthropic, supported Amazon Bedrock Converse models, Mistral, and Gemini 3 tool calls through the Google Generative AI and Vertex adapters. Google uses `VALIDATED` function-calling mode (or `ANY` when explicitly requested); earlier Gemini versions fall back for `strict: 'prefer'` and reject `strict: 'require'` because they do not enforce required parameters. Bedrock strict-tool capability is generated from model structured-output metadata; custom Bedrock models can override `compat.supportsStrictMode`. OpenAI Responses and Chat Completions can also emit grammar-constrained custom tools with OpenAI Lark or regex grammar variants. If multiple OpenAI variants are supplied, Lark is preferred over regex. Grammar constraints are enforced when the active model supports grammar tools; otherwise the tool falls back to normal function/JSON-schema handling. Grammar tool capability is model metadata: the generated catalog sets `compat.supportsOpenAIGrammarTools` for GPT-5+ models on endpoints that pass OpenAI custom tools through (OpenAI, OpenAI Codex, Azure OpenAI Responses, GitHub Copilot, opencode, and Cloudflare AI Gateway). OpenAI rejects `type: "custom"` tools for pre-GPT-5 models, and gateways that normalize tool schemas (e.g. OpenRouter) mangle them, so the flag stays off elsewhere. Custom model definitions can opt in via `compat`. Grammar-capable models reject grammar configurations without a non-empty supported variant. Native grammar tools must have an object parameter schema with exactly one required string property:
509
+
510
+ ```typescript
511
+ const patchTool: Tool = {
512
+ name: 'apply_patch',
513
+ description: 'Apply a patch',
514
+ parameters: Type.Object({
515
+ input: Type.String()
516
+ }, { additionalProperties: false }),
517
+ constrainedSampling: {
518
+ type: 'grammar',
519
+ variants: {
520
+ openai_lark: 'start: /.+/s'
521
+ }
522
+ }
523
+ };
524
+ ```
525
+
237
526
  ### Handling Tool Calls
238
527
 
239
528
  Tool results use content blocks and can include both text and images:
@@ -242,11 +531,11 @@ Tool results use content blocks and can include both text and images:
242
531
  import { readFileSync } from 'fs';
243
532
 
244
533
  const context: Context = {
245
- messages: [{ role: 'user', content: 'What is the weather in London?' }],
534
+ messages: [{ role: 'user', content: 'What is the weather in London?', timestamp: Date.now() }],
246
535
  tools: [weatherTool]
247
536
  };
248
537
 
249
- const response = await complete(model, context);
538
+ const response = await models.complete(model, context);
250
539
 
251
540
  // Check for tool calls in the response
252
541
  for (const block of response.content) {
@@ -287,7 +576,7 @@ context.messages.push({
287
576
  During streaming, tool call arguments are progressively parsed as they arrive. This enables real-time UI updates before the complete arguments are available:
288
577
 
289
578
  ```typescript
290
- const s = stream(model, context);
579
+ const s = models.stream(model, context);
291
580
 
292
581
  for await (const event of s) {
293
582
  if (event.type === 'toolcall_delta') {
@@ -328,15 +617,13 @@ for await (const event of s) {
328
617
 
329
618
  ### Validating Tool Arguments
330
619
 
331
- When using `agentLoop`, tool arguments are automatically validated against your TypeBox schemas before execution. If validation fails, the error is returned to the model as a tool result, allowing it to retry.
332
-
333
- When implementing your own tool execution loop with `stream()` or `complete()`, use `validateToolCall` to validate arguments before passing them to your tools:
620
+ When implementing your own tool execution loop, use `validateToolCall` to validate arguments before passing them to your tools:
334
621
 
335
622
  ```typescript
336
- import { stream, validateToolCall, Tool } from '@mariozechner/pi-ai';
623
+ import { validateToolCall, type Tool } from '@sammorrowdrums/mcpi-ai';
337
624
 
338
625
  const tools: Tool[] = [weatherTool, calculatorTool];
339
- const s = stream(model, { messages, tools });
626
+ const s = models.stream(model, { messages, tools });
340
627
 
341
628
  for await (const event of s) {
342
629
  if (event.type === 'toolcall_end') {
@@ -381,15 +668,16 @@ All streaming events emitted during assistant message generation:
381
668
  | `done` | Stream complete | `reason`: Stop reason ("stop", "length", "toolUse"), `message`: Final assistant message |
382
669
  | `error` | Error occurred | `reason`: Error type ("error" or "aborted"), `error`: AssistantMessage with partial content |
383
670
 
671
+ 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 mcpi 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.
672
+
384
673
  ## Image Input
385
674
 
386
675
  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.
387
676
 
388
677
  ```typescript
389
678
  import { readFileSync } from 'fs';
390
- import { getModel, complete } from '@mariozechner/pi-ai';
391
679
 
392
- const model = getModel('openai', 'gpt-4o-mini');
680
+ const model = models.getModel('openai', 'gpt-4o-mini')!;
393
681
 
394
682
  // Check if model supports images
395
683
  if (model.input.includes('image')) {
@@ -399,13 +687,14 @@ if (model.input.includes('image')) {
399
687
  const imageBuffer = readFileSync('image.png');
400
688
  const base64Image = imageBuffer.toString('base64');
401
689
 
402
- const response = await complete(model, {
690
+ const response = await models.complete(model, {
403
691
  messages: [{
404
692
  role: 'user',
405
693
  content: [
406
694
  { type: 'text', text: 'What is in this image?' },
407
695
  { type: 'image', data: base64Image, mimeType: 'image/png' }
408
- ]
696
+ ],
697
+ timestamp: Date.now()
409
698
  }]
410
699
  });
411
700
 
@@ -417,6 +706,83 @@ for (const block of response.content) {
417
706
  }
418
707
  ```
419
708
 
709
+ ## Image Generation
710
+
711
+ 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.
712
+
713
+ ### Basic Image Generation
714
+
715
+ ```typescript
716
+ import { builtinImagesModels } from '@sammorrowdrums/mcpi-ai/providers/all';
717
+
718
+ // Every built-in image-generation provider; accepts the same options as createModels()
719
+ const imagesModels = builtinImagesModels();
720
+
721
+ const model = imagesModels.getModel('openrouter', 'google/gemini-2.5-flash-image')!;
722
+
723
+ // Auth resolves through the provider (OPENROUTER_API_KEY here); explicit apiKey wins
724
+ const result = await imagesModels.generateImages(model, {
725
+ input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
726
+ });
727
+
728
+ for (const block of result.output) {
729
+ if (block.type === 'text') {
730
+ console.log(block.text);
731
+ } else if (block.type === 'image') {
732
+ console.log(block.mimeType);
733
+ console.log(block.data.substring(0, 32));
734
+ }
735
+ }
736
+ ```
737
+
738
+ Like the chat side, you can build the collection from parts: `createImagesModels({ credentials?, authContext? })`, the `openrouterImagesProvider()` factory from `@sammorrowdrums/mcpi-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.
739
+
740
+ The old global API (`getImageModel()` / `getImageModels()` / `getImageProviders()` / `generateImages()`) remains available on the [compat entrypoint](#migrating-from-the-old-global-api):
741
+
742
+ ```typescript
743
+ import { getImageModel, generateImages } from '@sammorrowdrums/mcpi-ai/compat';
744
+
745
+ const model = getImageModel('openrouter', 'google/gemini-2.5-flash-image');
746
+ const result = await generateImages(model, {
747
+ input: [{ type: 'text', text: 'Generate a red circle on a plain white background.' }]
748
+ }, {
749
+ apiKey: process.env.OPENROUTER_API_KEY
750
+ });
751
+ ```
752
+
753
+ Some models also support image input:
754
+
755
+ ```typescript
756
+ import { readFileSync } from 'fs';
757
+
758
+ const imageBuffer = readFileSync('input.png');
759
+ const result = await imagesModels.generateImages(model, {
760
+ input: [
761
+ { type: 'text', text: 'Create a variation of this image with a blue background.' },
762
+ { type: 'image', data: imageBuffer.toString('base64'), mimeType: 'image/png' }
763
+ ]
764
+ });
765
+ ```
766
+
767
+ Check capabilities on the model metadata:
768
+
769
+ ```typescript
770
+ console.log(model.input); // ['text', 'image']
771
+ console.log(model.output); // ['image'] or ['image', 'text']
772
+ ```
773
+
774
+ ### Notes and Limitations
775
+
776
+ - Image models live in `ImagesModels` collections, chat models in `Models` collections; the two are separate surfaces.
777
+ - Use `generateImages()`, not the chat/stream APIs.
778
+ - Image-generation models do not participate in tool calling.
779
+ - Outputs are returned in `AssistantImages.output` and can include both base64-encoded `ImageContent` blocks and `TextContent` blocks.
780
+ - Some models return only images, others return images plus text. Check `model.output`.
781
+ - Some models accept image input, others are text-to-image only. Check `model.input`.
782
+ - Like the streaming APIs, image generation supports options such as `apiKey`, `signal`, `headers`, `onPayload`, and `onResponse`, and results may include `stopReason`, `responseId`, and `usage`.
783
+ - 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.
784
+ - At the moment, image generation is available through only one provider, OpenRouter.
785
+
420
786
  ## Thinking/Reasoning
421
787
 
422
788
  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.
@@ -424,16 +790,11 @@ Many models support thinking/reasoning capabilities where they can show their in
424
790
  ### Unified Interface (streamSimple/completeSimple)
425
791
 
426
792
  ```typescript
427
- import { getModel, streamSimple, completeSimple } from '@mariozechner/pi-ai';
428
-
429
793
  // Many models across providers support thinking/reasoning
430
- const model = getModel('anthropic', 'claude-sonnet-4-20250514');
431
- // or getModel('openai', 'gpt-5-mini');
432
- // or getModel('google', 'gemini-2.5-flash');
433
- // or getModel('xai', 'grok-code-fast-1');
434
- // or getModel('groq', 'openai/gpt-oss-20b');
435
- // or getModel('cerebras', 'gpt-oss-120b');
436
- // or getModel('openrouter', 'z-ai/glm-4.5v');
794
+ const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
795
+ // or models.getModel('openai', 'gpt-5-mini');
796
+ // or models.getModel('google', 'gemini-2.5-flash');
797
+ // or models.getModel('xai', 'grok-4.5');
437
798
 
438
799
  // Check if model supports reasoning
439
800
  if (model.reasoning) {
@@ -441,10 +802,10 @@ if (model.reasoning) {
441
802
  }
442
803
 
443
804
  // Use the simplified reasoning option
444
- const response = await completeSimple(model, {
445
- messages: [{ role: 'user', content: 'Solve: 2x + 5 = 13' }]
805
+ const response = await models.completeSimple(model, {
806
+ messages: [{ role: 'user', content: 'Solve: 2x + 5 = 13', timestamp: Date.now() }]
446
807
  }, {
447
- reasoning: 'medium' // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' (xhigh maps to high on non-OpenAI providers)
808
+ reasoning: 'medium' // 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
448
809
  });
449
810
 
450
811
  // Access thinking and text blocks
@@ -457,35 +818,43 @@ for (const block of response.content) {
457
818
  }
458
819
  ```
459
820
 
821
+ `xhigh` and `max` are model-specific, opt-in levels. Use `getSupportedThinkingLevels(model)` to determine whether a concrete model exposes either level; models such as GPT-5.6 can expose both.
822
+
460
823
  ### Provider-Specific Options (stream/complete)
461
824
 
462
- For fine-grained control, use the provider-specific options:
825
+ `models.stream()`/`complete()` accept the owning API's full option set. Use `hasApi()` to narrow a dynamically looked-up model to its API for full option typing:
463
826
 
464
827
  ```typescript
465
- import { getModel, complete } from '@mariozechner/pi-ai';
828
+ import { hasApi } from '@sammorrowdrums/mcpi-ai';
466
829
 
467
830
  // OpenAI Reasoning (o1, o3, gpt-5)
468
- const openaiModel = getModel('openai', 'gpt-5-mini');
469
- await complete(openaiModel, context, {
470
- reasoningEffort: 'medium',
471
- reasoningSummary: 'detailed' // OpenAI Responses API only
472
- });
831
+ const openaiModel = models.getModel('openai', 'gpt-5-mini')!;
832
+ if (hasApi(openaiModel, 'openai-responses')) {
833
+ await models.complete(openaiModel, context, {
834
+ reasoningEffort: 'medium',
835
+ reasoningSummary: 'detailed' // OpenAI Responses API only
836
+ });
837
+ }
473
838
 
474
- // Anthropic Thinking (Claude Sonnet 4)
475
- const anthropicModel = getModel('anthropic', 'claude-sonnet-4-20250514');
476
- await complete(anthropicModel, context, {
477
- thinkingEnabled: true,
478
- thinkingBudgetTokens: 8192 // Optional token limit
479
- });
839
+ // Anthropic Thinking
840
+ const anthropicModel = models.getModel('anthropic', 'claude-sonnet-4-5')!;
841
+ if (hasApi(anthropicModel, 'anthropic-messages')) {
842
+ await models.complete(anthropicModel, context, {
843
+ thinkingEnabled: true,
844
+ thinkingBudgetTokens: 8192 // Optional token limit
845
+ });
846
+ }
480
847
 
481
848
  // Google Gemini Thinking
482
- const googleModel = getModel('google', 'gemini-2.5-flash');
483
- await complete(googleModel, context, {
484
- thinking: {
485
- enabled: true,
486
- budgetTokens: 8192 // -1 for dynamic, 0 to disable
487
- }
488
- });
849
+ const googleModel = models.getModel('google', 'gemini-2.5-flash')!;
850
+ if (hasApi(googleModel, 'google-generative-ai')) {
851
+ await models.complete(googleModel, context, {
852
+ thinking: {
853
+ enabled: true,
854
+ budgetTokens: 8192 // -1 for dynamic, 0 to disable
855
+ }
856
+ });
857
+ }
489
858
  ```
490
859
 
491
860
  ### Streaming Thinking Content
@@ -493,7 +862,7 @@ await complete(googleModel, context, {
493
862
  When streaming, thinking content is delivered through specific events:
494
863
 
495
864
  ```typescript
496
- const s = streamSimple(model, context, { reasoning: 'high' });
865
+ const s = models.streamSimple(model, context, { reasoning: 'high' });
497
866
 
498
867
  for await (const event of s) {
499
868
  switch (event.type) {
@@ -514,7 +883,8 @@ for await (const event of s) {
514
883
 
515
884
  Every `AssistantMessage` includes a `stopReason` field that indicates how the generation ended:
516
885
 
517
- - `"stop"` - Normal completion, the model finished its response
886
+ - `"pending"` - Only present in partial messages when we do not know what the stop reason will be
887
+ - `"stop"` - This is the final message the model will produce this turn
518
888
  - `"length"` - Output hit the maximum token limit
519
889
  - `"toolUse"` - Model is calling tools and expects tool results
520
890
  - `"error"` - An error occurred during generation
@@ -524,11 +894,11 @@ Every `AssistantMessage` includes a `stopReason` field that indicates how the ge
524
894
 
525
895
  ## Error Handling
526
896
 
527
- When a request ends with an error (including aborts and tool call validation errors), the streaming API emits an error event:
897
+ 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:
528
898
 
529
899
  ```typescript
530
900
  // In streaming
531
- for await (const event of stream) {
901
+ for await (const event of s) {
532
902
  if (event.type === 'error') {
533
903
  // event.reason is either "error" or "aborted"
534
904
  // event.error is the AssistantMessage with partial content
@@ -538,7 +908,7 @@ for await (const event of stream) {
538
908
  }
539
909
 
540
910
  // The final message will have the error details
541
- const message = await stream.result();
911
+ const message = await s.result();
542
912
  if (message.stopReason === 'error' || message.stopReason === 'aborted') {
543
913
  console.error('Request failed:', message.errorMessage);
544
914
  // message.content contains any partial content received before the error
@@ -546,21 +916,20 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
546
916
  }
547
917
  ```
548
918
 
919
+ Auth failures (no key configured, OAuth refresh failed, unknown provider) surface the same way: as a stream error with `stopReason: "error"`.
920
+
549
921
  ### Aborting Requests
550
922
 
551
923
  The abort signal allows you to cancel in-progress requests. Aborted requests have `stopReason === 'aborted'`:
552
924
 
553
925
  ```typescript
554
- import { getModel, stream } from '@mariozechner/pi-ai';
555
-
556
- const model = getModel('openai', 'gpt-4o-mini');
557
926
  const controller = new AbortController();
558
927
 
559
928
  // Abort after 2 seconds
560
929
  setTimeout(() => controller.abort(), 2000);
561
930
 
562
- const s = stream(model, {
563
- messages: [{ role: 'user', content: 'Write a long story' }]
931
+ const s = models.stream(model, {
932
+ messages: [{ role: 'user', content: 'Write a long story', timestamp: Date.now() }]
564
933
  }, {
565
934
  signal: controller.signal
566
935
  });
@@ -590,7 +959,7 @@ Aborted messages can be added to the conversation context and continued in subse
590
959
  ```typescript
591
960
  const context = {
592
961
  messages: [
593
- { role: 'user', content: 'Explain quantum computing in detail' }
962
+ { role: 'user', content: 'Explain quantum computing in detail', timestamp: Date.now() }
594
963
  ]
595
964
  };
596
965
 
@@ -598,14 +967,14 @@ const context = {
598
967
  const controller1 = new AbortController();
599
968
  setTimeout(() => controller1.abort(), 2000);
600
969
 
601
- const partial = await complete(model, context, { signal: controller1.signal });
970
+ const partial = await models.complete(model, context, { signal: controller1.signal });
602
971
 
603
972
  // Add the partial response to context
604
973
  context.messages.push(partial);
605
- context.messages.push({ role: 'user', content: 'Please continue' });
974
+ context.messages.push({ role: 'user', content: 'Please continue', timestamp: Date.now() });
606
975
 
607
976
  // Continue the conversation
608
- const continuation = await complete(model, context);
977
+ const continuation = await models.complete(model, context);
609
978
  ```
610
979
 
611
980
  ### Debugging Provider Payloads
@@ -613,7 +982,7 @@ const continuation = await complete(model, context);
613
982
  Use the `onPayload` callback to inspect the request payload sent to the provider. This is useful for debugging request formatting issues or provider validation errors.
614
983
 
615
984
  ```typescript
616
- const response = await complete(model, context, {
985
+ const response = await models.complete(model, context, {
617
986
  onPayload: (payload) => {
618
987
  console.log('Provider payload:', JSON.stringify(payload, null, 2));
619
988
  }
@@ -622,205 +991,121 @@ const response = await complete(model, context, {
622
991
 
623
992
  The callback is supported by `stream`, `complete`, `streamSimple`, and `completeSimple`.
624
993
 
625
- ## APIs, Models, and Providers
626
-
627
- The library uses a registry of API implementations. Built-in APIs include:
628
-
629
- - **`anthropic-messages`**: Anthropic Messages API (`streamAnthropic`, `AnthropicOptions`)
630
- - **`google-generative-ai`**: Google Generative AI API (`streamGoogle`, `GoogleOptions`)
631
- - **`google-gemini-cli`**: Google Cloud Code Assist API (`streamGoogleGeminiCli`, `GoogleGeminiCliOptions`)
632
- - **`google-vertex`**: Google Vertex AI API (`streamGoogleVertex`, `GoogleVertexOptions`)
633
- - **`mistral-conversations`**: Mistral Conversations API (`streamMistral`, `MistralOptions`)
634
- - **`openai-completions`**: OpenAI Chat Completions API (`streamOpenAICompletions`, `OpenAICompletionsOptions`)
635
- - **`openai-responses`**: OpenAI Responses API (`streamOpenAIResponses`, `OpenAIResponsesOptions`)
636
- - **`openai-codex-responses`**: OpenAI Codex Responses API (`streamOpenAICodexResponses`, `OpenAICodexResponsesOptions`)
637
- - **`azure-openai-responses`**: Azure OpenAI Responses API (`streamAzureOpenAIResponses`, `AzureOpenAIResponsesOptions`)
638
- - **`bedrock-converse-stream`**: Amazon Bedrock Converse API (`streamBedrock`, `BedrockOptions`)
994
+ ## Custom Providers
639
995
 
640
- ### Faux provider for tests
996
+ ### createProvider()
641
997
 
642
- `registerFauxProvider()` registers a temporary in-memory provider for tests and demos. It is opt-in and not part of the built-in provider set.
998
+ `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:
643
999
 
644
1000
  ```typescript
645
- import {
646
- complete,
647
- fauxAssistantMessage,
648
- fauxText,
649
- fauxThinking,
650
- fauxToolCall,
651
- registerFauxProvider,
652
- stream,
653
- } from '@mariozechner/pi-ai';
654
-
655
- const registration = registerFauxProvider({
656
- tokensPerSecond: 50 // optional
657
- });
1001
+ import { createModels, createProvider, envApiKeyAuth, type Model } from '@sammorrowdrums/mcpi-ai';
1002
+ import { openAICompletionsApi } from '@sammorrowdrums/mcpi-ai/api/openai-completions.lazy';
658
1003
 
659
- const model = registration.getModel();
660
- const context = {
661
- messages: [{ role: 'user', content: 'Summarize package.json and then call echo', timestamp: Date.now() }]
1004
+ const ollamaModel: Model<'openai-completions'> = {
1005
+ id: 'llama-3.1-8b',
1006
+ name: 'Llama 3.1 8B (Ollama)',
1007
+ api: 'openai-completions',
1008
+ provider: 'ollama',
1009
+ baseUrl: 'http://localhost:11434/v1',
1010
+ reasoning: false,
1011
+ input: ['text'],
1012
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1013
+ contextWindow: 128000,
1014
+ maxTokens: 32000
662
1015
  };
663
1016
 
664
- registration.setResponses([
665
- fauxAssistantMessage([
666
- fauxThinking('Need to inspect package metadata first.'),
667
- fauxToolCall('echo', { text: 'package.json' })
668
- ], { stopReason: 'toolUse' })
669
- ]);
670
-
671
- const first = await complete(model, context, {
672
- sessionId: 'session-1',
673
- cacheRetention: 'short'
1017
+ const ollama = createProvider({
1018
+ id: 'ollama',
1019
+ name: 'Ollama',
1020
+ baseUrl: 'http://localhost:11434/v1',
1021
+ // Every provider declares auth; keyless local servers resolve as configured with no key.
1022
+ auth: { apiKey: { name: 'Ollama', resolve: async () => ({ auth: {} }) } },
1023
+ models: [ollamaModel],
1024
+ api: openAICompletionsApi(),
674
1025
  });
675
- context.messages.push(first);
676
1026
 
677
- context.messages.push({
678
- role: 'toolResult',
679
- toolCallId: first.content.find((block) => block.type === 'toolCall')!.id,
680
- toolName: 'echo',
681
- content: [{ type: 'text', text: 'package.json contents here' }],
682
- isError: false,
683
- timestamp: Date.now()
684
- });
1027
+ const models = createModels();
1028
+ models.setProvider(ollama);
685
1029
 
686
- registration.setResponses([
687
- fauxAssistantMessage([
688
- fauxThinking('Now I can summarize the tool output.'),
689
- fauxText('Here is the summary.')
690
- ])
691
- ]);
1030
+ await models.complete(models.getModel('ollama', 'llama-3.1-8b')!, context);
1031
+ ```
692
1032
 
693
- const s = stream(model, context);
694
- for await (const event of s) {
695
- console.log(event.type);
696
- }
1033
+ For providers with real keys, `envApiKeyAuth(displayName, envVars)` gives the standard behavior (stored credential wins, then the first set env var):
697
1034
 
698
- // Optional: register multiple faux models for model-switching tests
699
- const multiModel = registerFauxProvider({
700
- models: [
701
- { id: 'faux-fast', reasoning: false },
702
- { id: 'faux-thinker', reasoning: true }
703
- ]
1035
+ ```typescript
1036
+ const proxy = createProvider({
1037
+ id: 'my-proxy',
1038
+ auth: { apiKey: envApiKeyAuth('My proxy API key', ['MY_PROXY_API_KEY']) },
1039
+ models: [/* ... */],
1040
+ api: openAICompletionsApi(),
704
1041
  });
705
- const thinker = multiModel.getModel('faux-thinker');
706
-
707
- console.log(thinker?.reasoning);
708
- console.log(registration.getPendingResponseCount());
709
- console.log(registration.state.callCount);
710
- registration.unregister();
711
- multiModel.unregister();
712
1042
  ```
713
1043
 
714
- Notes:
715
- - Responses are consumed from a queue in request start order.
716
- - If the queue is empty, the faux provider returns an assistant error message with `errorMessage: "No more faux responses queued"`.
717
- - Use `registration.setResponses([...])` to replace the remaining queue and `registration.appendResponses([...])` to add more responses.
718
- - `registration.models` exposes all registered faux models. `registration.getModel()` returns the first one, and `registration.getModel(id)` returns a specific one.
719
- - Use `fauxAssistantMessage(...)` for scripted assistant replies. Use `fauxText(...)`, `fauxThinking(...)`, and `fauxToolCall(...)` to build content blocks without filling in low-level fields manually.
720
- - `registration.unregister()` removes the temporary provider from the global API registry.
721
- - Usage is estimated at roughly 1 token per 4 characters. When `sessionId` is present and `cacheRetention` is not `"none"`, prompt cache reads and writes are simulated automatically.
722
- - Tool call arguments stream incrementally via `toolcall_delta` chunks.
723
- - By default, each streamed chunk is emitted on its own microtask. Set `tokensPerSecond` to pace chunk delivery in real time.
724
- - The intended use is one deterministic scripted flow per registration. If you need independent concurrent flows, register separate faux providers.
725
-
726
- ### Providers and Models
1044
+ Mixed-API providers pass a map keyed by `model.api`; each model dispatches to its API's implementation:
727
1045
 
728
- A **provider** offers models through a specific API. For example:
729
- - **Anthropic** models use the `anthropic-messages` API
730
- - **Google** models use the `google-generative-ai` API
731
- - **OpenAI** models use the `openai-responses` API
732
- - **Mistral** models use the `mistral-conversations` API
733
- - **xAI, Cerebras, Groq, etc.** models use the `openai-completions` API (OpenAI-compatible)
1046
+ ```typescript
1047
+ import { anthropicMessagesApi } from '@sammorrowdrums/mcpi-ai/api/anthropic-messages.lazy';
1048
+ import { openAIResponsesApi } from '@sammorrowdrums/mcpi-ai/api/openai-responses.lazy';
1049
+
1050
+ const gateway = createProvider({
1051
+ id: 'my-gateway',
1052
+ auth: { apiKey: envApiKeyAuth('Gateway key', ['GATEWAY_API_KEY']) },
1053
+ models: [/* models with api: 'anthropic-messages' or 'openai-responses' */],
1054
+ api: {
1055
+ 'anthropic-messages': anthropicMessagesApi(),
1056
+ 'openai-responses': openAIResponsesApi(),
1057
+ },
1058
+ });
1059
+ ```
734
1060
 
735
- ### Querying Providers and Models
1061
+ Provider-wide endpoint or request transformations belong in the provider's API implementation: wrap the `ProviderStreams` you pass as `api` so every request goes through the transformation before dispatch. The Cloudflare providers do this to materialize account/gateway endpoint placeholders from the resolved provider env:
736
1062
 
737
1063
  ```typescript
738
- import { getProviders, getModels, getModel } from '@mariozechner/pi-ai';
739
-
740
- // Get all available providers
741
- const providers = getProviders();
742
- console.log(providers); // ['openai', 'anthropic', 'google', 'xai', 'groq', ...]
743
-
744
- // Get all models from a provider (fully typed)
745
- const anthropicModels = getModels('anthropic');
746
- for (const model of anthropicModels) {
747
- console.log(`${model.id}: ${model.name}`);
748
- console.log(` API: ${model.api}`); // 'anthropic-messages'
749
- console.log(` Context: ${model.contextWindow} tokens`);
750
- console.log(` Vision: ${model.input.includes('image')}`);
751
- console.log(` Reasoning: ${model.reasoning}`);
1064
+ function tenantStreams(streams: ProviderStreams): ProviderStreams {
1065
+ const withTenant = (model: Model<Api>) => ({ ...model, baseUrl: model.baseUrl.replace('{tenant}', tenantId) });
1066
+ return {
1067
+ stream: (model, context, options) => streams.stream(withTenant(model), context, options),
1068
+ streamSimple: (model, context, options) => streams.streamSimple(withTenant(model), context, options),
1069
+ };
752
1070
  }
753
1071
 
754
- // Get a specific model (both provider and model ID are auto-completed in IDEs)
755
- const model = getModel('openai', 'gpt-4o-mini');
756
- console.log(`Using ${model.name} via ${model.api} API`);
1072
+ const tenantGateway = createProvider({
1073
+ id: 'tenant-gateway',
1074
+ auth: { apiKey: envApiKeyAuth('Gateway key', ['GATEWAY_API_KEY']) },
1075
+ models: [/* ... */],
1076
+ api: tenantStreams(openAICompletionsApi()),
1077
+ });
757
1078
  ```
758
1079
 
759
- ### Custom Models
760
-
761
- You can create custom models for local inference servers or custom endpoints:
1080
+ 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. Its `read`, `write`, and `delete` operations accept optional cancellation, and `Models` binds those waits to the provider refresh signal.
762
1081
 
763
1082
  ```typescript
764
- import { Model, stream } from '@mariozechner/pi-ai';
1083
+ const models = createModels({ credentials, modelsStore });
1084
+ const llamacpp = createProvider({
1085
+ id: 'llamacpp',
1086
+ auth: { apiKey: { name: 'llama.cpp', resolve: async () => ({ auth: {} }) } },
1087
+ models: [],
1088
+ fetchModels: async ({ signal }) => fetchModelsFromServer('http://localhost:8080', signal),
1089
+ api: openAICompletionsApi(),
1090
+ });
765
1091
 
766
- // Example: Ollama using OpenAI-compatible API
767
- const ollamaModel: Model<'openai-completions'> = {
768
- id: 'llama-3.1-8b',
769
- name: 'Llama 3.1 8B (Ollama)',
770
- api: 'openai-completions',
771
- provider: 'ollama',
772
- baseUrl: 'http://localhost:11434/v1',
773
- reasoning: false,
774
- input: ['text'],
775
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
776
- contextWindow: 128000,
777
- maxTokens: 32000
778
- };
1092
+ models.setProvider(llamacpp);
1093
+ const result = await models.refresh({ signal });
1094
+ if (result.aborted) console.log('refresh cancelled');
1095
+ for (const [provider, error] of result.errors) console.error(provider, error);
1096
+ ```
779
1097
 
780
- // Example: LiteLLM proxy with explicit compat settings
781
- const litellmModel: Model<'openai-completions'> = {
782
- id: 'gpt-4o',
783
- name: 'GPT-4o (via LiteLLM)',
784
- api: 'openai-completions',
785
- provider: 'litellm',
786
- baseUrl: 'http://localhost:4000/v1',
787
- reasoning: false,
788
- input: ['text', 'image'],
789
- cost: { input: 2.5, output: 10, cacheRead: 0, cacheWrite: 0 },
790
- contextWindow: 128000,
791
- maxTokens: 16384,
792
- compat: {
793
- supportsStore: false, // LiteLLM doesn't support the store field
794
- }
795
- };
1098
+ `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.
796
1099
 
797
- // Example: Custom endpoint with headers (bypassing Cloudflare bot detection)
798
- const proxyModel: Model<'anthropic-messages'> = {
799
- id: 'claude-sonnet-4',
800
- name: 'Claude Sonnet 4 (Proxied)',
801
- api: 'anthropic-messages',
802
- provider: 'custom-proxy',
803
- baseUrl: 'https://proxy.example.com/v1',
804
- reasoning: true,
805
- input: ['text', 'image'],
806
- cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
807
- contextWindow: 200000,
808
- maxTokens: 8192,
809
- headers: {
810
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
811
- 'X-Custom-Auth': 'bearer-token-here'
812
- }
813
- };
1100
+ 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.
814
1101
 
815
- // Use the custom model
816
- const response = await stream(ollamaModel, context, {
817
- apiKey: 'dummy' // Ollama doesn't need a real key
818
- });
819
- ```
1102
+ `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. Publication is generation-checked; put synchronous in-memory catalog changes in `update` rather than mutating state before publication.
820
1103
 
821
- Some OpenAI-compatible servers do not understand the `developer` role used for reasoning-capable models. For those providers, set `compat.supportsDeveloperRole` to `false` so the system prompt is sent as a `system` message instead. If the server also does not support `reasoning_effort`, set `compat.supportsReasoningEffort` to `false` too.
1104
+ 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).
822
1105
 
823
- This commonly applies to Ollama, vLLM, SGLang, and similar OpenAI-compatible servers. You can set `compat` at the provider level or per model.
1106
+ Some OpenAI-compatible servers do not understand the `developer` role used for reasoning-capable models. For those providers, set `compat.supportsDeveloperRole` to `false` so the system prompt is sent as a `system` message instead. If the server also does not support `reasoning_effort`, set `compat.supportsReasoningEffort` to `false` too. This commonly applies to Ollama, vLLM, SGLang, and similar OpenAI-compatible servers.
1107
+
1108
+ Use model-level `thinkingLevelMap` to describe model-specific thinking controls. Keys are mcpi thinking levels (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`). Missing standard levels through `high` use provider defaults; `xhigh` and `max` are opt-in and require a non-null map entry. String values are sent to the provider, `null` marks a level unsupported, and maps may skip levels.
824
1109
 
825
1110
  ```typescript
826
1111
  const ollamaReasoningModel: Model<'openai-completions'> = {
@@ -834,6 +1119,13 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
834
1119
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
835
1120
  contextWindow: 131072,
836
1121
  maxTokens: 32000,
1122
+ thinkingLevelMap: {
1123
+ minimal: null,
1124
+ low: null,
1125
+ medium: null,
1126
+ high: 'high',
1127
+ xhigh: null,
1128
+ },
837
1129
  compat: {
838
1130
  supportsDeveloperRole: false,
839
1131
  supportsReasoningEffort: false,
@@ -841,9 +1133,39 @@ const ollamaReasoningModel: Model<'openai-completions'> = {
841
1133
  };
842
1134
  ```
843
1135
 
1136
+ ### Calling API Implementations Directly
1137
+
1138
+ 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:
1139
+
1140
+ ```typescript
1141
+ import { stream } from '@sammorrowdrums/mcpi-ai/api/anthropic-messages';
1142
+
1143
+ const s = stream(claudeModel, context, {
1144
+ apiKey: process.env.ANTHROPIC_API_KEY,
1145
+ thinkingEnabled: true,
1146
+ thinkingBudgetTokens: 2048,
1147
+ });
1148
+ ```
1149
+
1150
+ Built-in API implementations live under `./api/<api-id>`:
1151
+
1152
+ | API id | Options type |
1153
+ |--------|--------------|
1154
+ | `anthropic-messages` | `AnthropicOptions` |
1155
+ | `openai-completions` | `OpenAICompletionsOptions` |
1156
+ | `openai-responses` | `OpenAIResponsesOptions` |
1157
+ | `openai-codex-responses` | `OpenAICodexResponsesOptions` |
1158
+ | `azure-openai-responses` | `AzureOpenAIResponsesOptions` |
1159
+ | `google-generative-ai` | `GoogleOptions` |
1160
+ | `google-vertex` | `GoogleVertexOptions` |
1161
+ | `mistral-conversations` | `MistralOptions` |
1162
+ | `bedrock-converse-stream` | `BedrockOptions` |
1163
+
1164
+ Importing an implementation module loads its SDK. The `./api/<id>.lazy` wrappers (used by the provider factories) defer that load to the first request when the runtime or bundler supports dynamic import chunking. Legacy raw API subpaths from older releases (`./anthropic`, `./google`, `./mistral`, `./openai-completions`, ...) were removed; use `@sammorrowdrums/mcpi-ai/api/<api-id>`.
1165
+
844
1166
  ### OpenAI Compatibility Settings
845
1167
 
846
- The `openai-completions` API is implemented by many providers with minor differences. By default, the library auto-detects compatibility settings based on `baseUrl` for a small set of known OpenAI-compatible providers (Cerebras, xAI, Chutes, DeepSeek, zAi, OpenCode, etc.). For custom proxies or unknown endpoints, you can override these settings via the `compat` field. For `openai-responses` models, the compat field only supports Responses-specific flags.
1168
+ The `openai-completions` API is implemented by many providers with minor differences. By default, the library auto-detects compatibility settings based on `baseUrl` for a small set of known OpenAI-compatible providers (Cerebras, xAI, Chutes, DeepSeek, NVIDIA NIM, Together AI, zAi, OpenCode, Cloudflare Workers AI, etc.). For custom proxies or unknown endpoints, you can override these settings via the `compat` field. For `openai-responses` models, the compat field supports Responses-specific flags.
847
1169
 
848
1170
  ```typescript
849
1171
  interface OpenAICompletionsCompat {
@@ -852,19 +1174,28 @@ interface OpenAICompletionsCompat {
852
1174
  supportsReasoningEffort?: boolean; // Whether provider supports `reasoning_effort` (default: true)
853
1175
  supportsUsageInStreaming?: boolean; // Whether provider supports `stream_options: { include_usage: true }` (default: true)
854
1176
  supportsStrictMode?: boolean; // Whether provider supports `strict` in tool definitions (default: true)
855
- sendSessionAffinityHeaders?: boolean; // Whether to send `session_id`, `x-client-request-id`, and `x-session-affinity` from `sessionId` when caching is enabled (default: false)
1177
+ 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)
1178
+ sendSessionAffinityHeaders?: boolean; // Send session-affinity data from `sessionId` (default: false)
1179
+ 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)
856
1180
  maxTokensField?: 'max_completion_tokens' | 'max_tokens'; // Which field name to use (default: max_completion_tokens)
857
1181
  requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
858
1182
  requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
859
1183
  requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false)
860
- thinkingFormat?: 'openai' | 'zai' | 'qwen' | 'qwen-chat-template'; // Format for reasoning param: 'openai' uses reasoning_effort, 'zai' uses thinking: { type: "enabled" }, 'qwen' uses enable_thinking: boolean, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking (default: openai)
1184
+ requiresReasoningContentOnAssistantMessages?: boolean; // Whether all replayed assistant messages must include empty reasoning_content when reasoning is enabled (default: auto-detected for DeepSeek)
1185
+ 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)
1186
+ chatTemplateKwargs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_kwargs values; use $var for mcpi-controlled thinking values
1187
+ chatTemplateArgs?: Record<string, string | number | boolean | null | { '$var': 'thinking.enabled' | 'thinking.effort'; omitWhenOff?: boolean }>; // chat_template_args values for thinkingFormat: 'baseten'; use $var for mcpi-controlled thinking values
861
1188
  cacheControlFormat?: 'anthropic'; // Anthropic-style cache_control on system prompt, last tool, and last user/assistant text content
862
1189
  openRouterRouting?: OpenRouterRouting; // OpenRouter routing preferences (default: {})
863
1190
  vercelGatewayRouting?: VercelGatewayRouting; // Vercel AI Gateway routing preferences (default: {})
864
1191
  }
865
1192
 
866
1193
  interface OpenAIResponsesCompat {
867
- // Reserved for future use
1194
+ supportsDeveloperRole?: boolean; // Whether provider supports `developer` role vs `system` (default: true)
1195
+ sessionAffinityFormat?: 'openai' | 'openai-nosession' | 'openrouter'; // Session-affinity header format: 'openai' sends `session_id` and `x-client-request-id`; 'openai-nosession' sends `x-client-request-id`; 'openrouter' sends `x-session-id`. Does not affect the `prompt_cache_key` body param (default: auto-detected)
1196
+ supportsLongCacheRetention?: boolean; // Whether provider supports `prompt_cache_retention: "24h"` (default: true)
1197
+ supportsStrictMode?: boolean; // Whether provider supports strict JSON-schema function tools (default: false; enabled in metadata for built-in OpenAI models)
1198
+ 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)
868
1199
  }
869
1200
  ```
870
1201
 
@@ -874,30 +1205,97 @@ If `compat` is not set, the library falls back to URL-based detection. If `compa
874
1205
  - **Custom inference servers**: May use non-standard field names
875
1206
  - **Self-hosted endpoints**: May have different feature support
876
1207
 
877
- ### Type Safety
1208
+ ## Faux Provider for Tests
878
1209
 
879
- Models are typed by their API, which keeps the model metadata accurate. Provider-specific option types are enforced when you call the provider functions directly. The generic `stream` and `complete` functions accept `StreamOptions` with additional provider fields.
1210
+ `fauxProvider()` builds an in-memory provider with scripted responses for tests and demos:
880
1211
 
881
1212
  ```typescript
882
- import { streamAnthropic, type AnthropicOptions } from '@mariozechner/pi-ai';
1213
+ import {
1214
+ createModels,
1215
+ fauxAssistantMessage,
1216
+ fauxProvider,
1217
+ fauxText,
1218
+ fauxThinking,
1219
+ fauxToolCall,
1220
+ } from '@sammorrowdrums/mcpi-ai';
883
1221
 
884
- // TypeScript knows this is an Anthropic model
885
- const claude = getModel('anthropic', 'claude-sonnet-4-20250514');
1222
+ const faux = fauxProvider({
1223
+ tokensPerSecond: 50 // optional
1224
+ });
886
1225
 
887
- const options: AnthropicOptions = {
888
- thinkingEnabled: true,
889
- thinkingBudgetTokens: 2048
1226
+ const models = createModels();
1227
+ models.setProvider(faux.provider);
1228
+
1229
+ const model = faux.getModel();
1230
+ const context = {
1231
+ messages: [{ role: 'user', content: 'Summarize package.json and then call echo', timestamp: Date.now() }]
890
1232
  };
891
1233
 
892
- await streamAnthropic(claude, context, options);
1234
+ faux.setResponses([
1235
+ fauxAssistantMessage([
1236
+ fauxThinking('Need to inspect package metadata first.'),
1237
+ fauxToolCall('echo', { text: 'package.json' })
1238
+ ], { stopReason: 'toolUse' })
1239
+ ]);
1240
+
1241
+ const first = await models.complete(model, context, {
1242
+ sessionId: 'session-1',
1243
+ cacheRetention: 'short'
1244
+ });
1245
+ context.messages.push(first);
1246
+
1247
+ context.messages.push({
1248
+ role: 'toolResult',
1249
+ toolCallId: first.content.find((block) => block.type === 'toolCall')!.id,
1250
+ toolName: 'echo',
1251
+ content: [{ type: 'text', text: 'package.json contents here' }],
1252
+ isError: false,
1253
+ timestamp: Date.now()
1254
+ });
1255
+
1256
+ faux.setResponses([
1257
+ fauxAssistantMessage([
1258
+ fauxThinking('Now I can summarize the tool output.'),
1259
+ fauxText('Here is the summary.')
1260
+ ])
1261
+ ]);
1262
+
1263
+ const s = models.stream(model, context);
1264
+ for await (const event of s) {
1265
+ console.log(event.type);
1266
+ }
1267
+
1268
+ // Optional: multiple faux models for model-switching tests
1269
+ const multiModel = fauxProvider({
1270
+ provider: 'faux-multi',
1271
+ models: [
1272
+ { id: 'faux-fast', reasoning: false },
1273
+ { id: 'faux-thinker', reasoning: true }
1274
+ ]
1275
+ });
1276
+ models.setProvider(multiModel.provider);
1277
+ const thinker = multiModel.getModel('faux-thinker');
1278
+
1279
+ console.log(thinker?.reasoning);
1280
+ console.log(faux.getPendingResponseCount());
1281
+ console.log(faux.state.callCount);
893
1282
  ```
894
1283
 
1284
+ Notes:
1285
+ - Responses are consumed from a queue in request start order.
1286
+ - If the queue is empty, the faux provider returns an assistant error message with `errorMessage: "No more faux responses queued"`.
1287
+ - Use `faux.setResponses([...])` to replace the remaining queue and `faux.appendResponses([...])` to add more responses.
1288
+ - `faux.models` exposes all faux models. `faux.getModel()` returns the first one, and `faux.getModel(id)` returns a specific one.
1289
+ - Use `fauxAssistantMessage(...)` for scripted assistant replies. Use `fauxText(...)`, `fauxThinking(...)`, and `fauxToolCall(...)` to build content blocks without filling in low-level fields manually.
1290
+ - Usage is estimated at roughly 1 token per 4 characters. When `sessionId` is present and `cacheRetention` is not `"none"`, prompt cache reads and writes are simulated automatically.
1291
+ - Tool call arguments stream incrementally via `toolcall_delta` chunks.
1292
+ - By default, each streamed chunk is emitted on its own microtask. Set `tokensPerSecond` to pace chunk delivery in real time.
1293
+ - The intended use is one deterministic scripted flow per handle. If you need independent concurrent flows, create separate faux providers with distinct `provider` ids.
1294
+
895
1295
  ## Cross-Provider Handoffs
896
1296
 
897
1297
  The library supports seamless handoffs between different LLM providers within the same conversation. This allows you to switch models mid-conversation while preserving context, including thinking blocks, tool calls, and tool results.
898
1298
 
899
- ### How It Works
900
-
901
1299
  When messages from one provider are sent to a different provider, the library automatically transforms them for compatibility:
902
1300
 
903
1301
  - **User and tool result messages** are passed through unchanged
@@ -905,98 +1303,86 @@ When messages from one provider are sent to a different provider, the library au
905
1303
  - **Assistant messages from different providers** have their thinking blocks converted to text with `<thinking>` tags
906
1304
  - **Tool calls and regular text** are preserved unchanged
907
1305
 
908
- ### Example: Multi-Provider Conversation
909
-
910
1306
  ```typescript
911
- import { getModel, complete, Context } from '@mariozechner/pi-ai';
1307
+ import { createModels, type Context } from '@sammorrowdrums/mcpi-ai';
1308
+ import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
1309
+ import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
1310
+ import { googleProvider } from '@sammorrowdrums/mcpi-ai/providers/google';
912
1311
 
913
- // Start with Claude
914
- const claude = getModel('anthropic', 'claude-sonnet-4-20250514');
915
- const context: Context = {
916
- messages: []
917
- };
1312
+ const models = createModels();
1313
+ models.setProvider(anthropicProvider());
1314
+ models.setProvider(openaiProvider());
1315
+ models.setProvider(googleProvider());
918
1316
 
919
- context.messages.push({ role: 'user', content: 'What is 25 * 18?' });
920
- const claudeResponse = await complete(claude, context, {
921
- thinkingEnabled: true
922
- });
923
- context.messages.push(claudeResponse);
1317
+ const context: Context = { messages: [] };
1318
+
1319
+ // Start with Claude
1320
+ const claude = models.getModel('anthropic', 'claude-sonnet-4-5')!;
1321
+ context.messages.push({ role: 'user', content: 'What is 25 * 18?', timestamp: Date.now() });
1322
+ context.messages.push(await models.completeSimple(claude, context, { reasoning: 'medium' }));
924
1323
 
925
1324
  // Switch to GPT-5 - it will see Claude's thinking as <thinking> tagged text
926
- const gpt5 = getModel('openai', 'gpt-5-mini');
927
- context.messages.push({ role: 'user', content: 'Is that calculation correct?' });
928
- const gptResponse = await complete(gpt5, context);
929
- context.messages.push(gptResponse);
1325
+ const gpt5 = models.getModel('openai', 'gpt-5-mini')!;
1326
+ context.messages.push({ role: 'user', content: 'Is that calculation correct?', timestamp: Date.now() });
1327
+ context.messages.push(await models.complete(gpt5, context));
930
1328
 
931
1329
  // Switch to Gemini
932
- const gemini = getModel('google', 'gemini-2.5-flash');
933
- context.messages.push({ role: 'user', content: 'What was the original question?' });
934
- const geminiResponse = await complete(gemini, context);
1330
+ const gemini = models.getModel('google', 'gemini-2.5-flash')!;
1331
+ context.messages.push({ role: 'user', content: 'What was the original question?', timestamp: Date.now() });
1332
+ const geminiResponse = await models.complete(gemini, context);
935
1333
  ```
936
1334
 
937
- ### Provider Compatibility
938
-
939
- All providers can handle messages from other providers, including:
940
- - Text content
941
- - Tool calls and tool results (including images in tool results)
942
- - Thinking/reasoning blocks (transformed to tagged text for cross-provider compatibility)
943
- - Aborted messages with partial content
944
-
945
- This enables flexible workflows where you can:
946
- - Start with a fast model for initial responses
947
- - Switch to a more capable model for complex reasoning
948
- - Use specialized models for specific tasks
949
- - Maintain conversation continuity across provider outages
1335
+ 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.
950
1336
 
951
1337
  ## Context Serialization
952
1338
 
953
1339
  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:
954
1340
 
955
1341
  ```typescript
956
- import { Context, getModel, complete } from '@mariozechner/pi-ai';
957
-
958
- // Create and use a context
959
1342
  const context: Context = {
960
1343
  systemPrompt: 'You are a helpful assistant.',
961
1344
  messages: [
962
- { role: 'user', content: 'What is TypeScript?' }
1345
+ { role: 'user', content: 'What is TypeScript?', timestamp: Date.now() }
963
1346
  ]
964
1347
  };
965
1348
 
966
- const model = getModel('openai', 'gpt-4o-mini');
967
- const response = await complete(model, context);
1349
+ const model = models.getModel('openai', 'gpt-4o-mini')!;
1350
+ const response = await models.complete(model, context);
968
1351
  context.messages.push(response);
969
1352
 
970
1353
  // Serialize the entire context
971
1354
  const serialized = JSON.stringify(context);
972
- console.log('Serialized context size:', serialized.length, 'bytes');
973
1355
 
974
1356
  // Save to database, localStorage, file, etc.
975
1357
  localStorage.setItem('conversation', serialized);
976
1358
 
977
1359
  // Later: deserialize and continue the conversation
978
1360
  const restored: Context = JSON.parse(localStorage.getItem('conversation')!);
979
- restored.messages.push({ role: 'user', content: 'Tell me more about its type system' });
1361
+ restored.messages.push({ role: 'user', content: 'Tell me more about its type system', timestamp: Date.now() });
980
1362
 
981
1363
  // Continue with any model
982
- const newModel = getModel('anthropic', 'claude-3-5-haiku-20241022');
983
- const continuation = await complete(newModel, restored);
1364
+ const newModel = models.getModel('anthropic', 'claude-3-5-haiku-20241022')!;
1365
+ const continuation = await models.complete(newModel, restored);
984
1366
  ```
985
1367
 
1368
+ Models are plain serializable data too — no functions or implementations attached — so persisting "which model was this conversation using" is a `JSON.stringify` away.
1369
+
986
1370
  > **Note**: If the context contains images (encoded as base64 as shown in the Image Input section), those will also be serialized.
987
1371
 
988
1372
  ## Browser Usage
989
1373
 
990
- The library supports browser environments. You must pass the API key explicitly since environment variables are not available in browsers:
1374
+ The library supports browser environments. The core entrypoint and provider factories are side-effect free and bundle cleanly. Environment variables are not available in browsers, so pass API keys explicitly — or inject a `CredentialStore` (e.g. localStorage-backed) and let provider auth resolve from stored credentials:
991
1375
 
992
1376
  ```typescript
993
- import { getModel, complete } from '@mariozechner/pi-ai';
1377
+ import { createModels } from '@sammorrowdrums/mcpi-ai';
1378
+ import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
994
1379
 
995
- // API key must be passed explicitly in browser
996
- const model = getModel('anthropic', 'claude-3-5-haiku-20241022');
1380
+ const models = createModels();
1381
+ models.setProvider(anthropicProvider());
997
1382
 
998
- const response = await complete(model, {
999
- messages: [{ role: 'user', content: 'Hello!' }]
1383
+ const model = models.getModel('anthropic', 'claude-3-5-haiku-20241022')!;
1384
+ const response = await models.complete(model, {
1385
+ messages: [{ role: 'user', content: 'Hello!', timestamp: Date.now() }]
1000
1386
  }, {
1001
1387
  apiKey: 'your-api-key'
1002
1388
  });
@@ -1004,95 +1390,133 @@ const response = await complete(model, {
1004
1390
 
1005
1391
  > **Security Warning**: Exposing API keys in frontend code is dangerous. Anyone can extract and abuse your keys. Only use this approach for internal tools or demos. For production applications, use a backend proxy that keeps your API keys secure.
1006
1392
 
1007
- ### Browser Compatibility Notes
1393
+ Browser compatibility notes:
1008
1394
 
1009
- - Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments.
1010
- - OAuth login flows are not supported in browser environments. Use the `@mariozechner/pi-ai/oauth` entry point in Node.js.
1011
- - In browser builds, Bedrock can still appear in model lists. Calls to Bedrock models fail at runtime.
1395
+ - Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments. It can still appear in model lists; calls fail at runtime.
1396
+ - OAuth login flows are Node-only. They are lazy-loaded behind bundler-opaque imports, so registering an OAuth-capable provider does not pull Node-only code into a browser bundle only actually logging in would.
1012
1397
  - Use a server-side proxy or backend service if you need Bedrock or OAuth-based auth from a web app.
1013
1398
 
1014
- ### Environment Variables (Node.js only)
1015
-
1016
- In Node.js environments, you can set environment variables to avoid passing API keys:
1017
-
1018
- | Provider | Environment Variable(s) |
1019
- |----------|------------------------|
1020
- | OpenAI | `OPENAI_API_KEY` |
1021
- | Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME` (optional `AZURE_OPENAI_API_VERSION`, `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` like `model=deployment,model2=deployment2`) |
1022
- | Anthropic | `ANTHROPIC_API_KEY` or `ANTHROPIC_OAUTH_TOKEN` |
1023
- | Google | `GEMINI_API_KEY` |
1024
- | Vertex AI | `GOOGLE_CLOUD_API_KEY` or `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) + `GOOGLE_CLOUD_LOCATION` + ADC |
1025
- | Mistral | `MISTRAL_API_KEY` |
1026
- | Groq | `GROQ_API_KEY` |
1027
- | Cerebras | `CEREBRAS_API_KEY` |
1028
- | xAI | `XAI_API_KEY` |
1029
- | Fireworks | `FIREWORKS_API_KEY` |
1030
- | OpenRouter | `OPENROUTER_API_KEY` |
1031
- | Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
1032
- | zAI | `ZAI_API_KEY` |
1033
- | MiniMax | `MINIMAX_API_KEY` |
1034
- | OpenCode Zen / OpenCode Go | `OPENCODE_API_KEY` |
1035
- | Kimi For Coding | `KIMI_API_KEY` |
1036
- | GitHub Copilot | `COPILOT_GITHUB_TOKEN` or `GH_TOKEN` or `GITHUB_TOKEN` |
1399
+ ## Bundling and Tree Shaking
1037
1400
 
1038
- When set, the library automatically uses these keys:
1401
+ For small bundles, import only the providers you need:
1039
1402
 
1040
1403
  ```typescript
1041
- // Uses OPENAI_API_KEY from environment
1042
- const model = getModel('openai', 'gpt-4o-mini');
1043
- const response = await complete(model, context);
1404
+ import { createModels } from '@sammorrowdrums/mcpi-ai';
1405
+ import { openaiProvider } from '@sammorrowdrums/mcpi-ai/providers/openai';
1044
1406
 
1045
- // Or override with explicit key
1046
- const response = await complete(model, context, {
1047
- apiKey: 'sk-different-key'
1048
- });
1407
+ const models = createModels();
1408
+ models.setProvider(openaiProvider());
1049
1409
  ```
1050
1410
 
1051
- #### Antigravity Version Override
1411
+ Rules:
1052
1412
 
1053
- Set `PI_AI_ANTIGRAVITY_VERSION` to override the Antigravity User-Agent version when Google updates their requirements:
1413
+ - `@sammorrowdrums/mcpi-ai` is the core entrypoint and does not import built-in catalogs, provider factories, or SDK implementations.
1414
+ - `@sammorrowdrums/mcpi-ai/providers/<provider>` imports that provider's catalog and lazy API wrapper only.
1415
+ - `@sammorrowdrums/mcpi-ai/providers/all` imports every built-in provider factory and all catalogs. Use it only when you want the full built-in set.
1416
+ - With code splitting, provider SDKs stay in lazy chunks and load on first request.
1417
+ - Without code splitting, bundlers fold reachable lazy API implementations into the single bundle. A single-provider bundle then includes that provider's SDK; `providers/all` includes all statically visible SDKs. Bedrock is the exception: its AWS SDK implementation is loaded through a bundler-opaque Node-only import.
1418
+ - Importing `@sammorrowdrums/mcpi-ai/api/<api-id>` directly loads that API implementation and its SDK immediately.
1419
+
1420
+ Avoid `@sammorrowdrums/mcpi-ai/compat` in new bundled apps; it preserves the old global API and imports the full built-in catalog surface.
1421
+
1422
+ For single-file Node ESM bundles, some SDK dependencies may still use dynamic CommonJS `require()` internally. If you see errors such as `Dynamic require of "child_process" is not supported`, add a Node `require` shim to the bundle. With esbuild:
1054
1423
 
1055
1424
  ```bash
1056
- export PI_AI_ANTIGRAVITY_VERSION="1.23.0"
1425
+ esbuild app.js --bundle --platform=node --format=esm \
1426
+ --banner:js='import { createRequire } from "module";const require = createRequire(import.meta.url);' \
1427
+ --outfile=app.bundle.js
1057
1428
  ```
1058
1429
 
1059
- #### Cache Retention
1060
-
1061
- Set `PI_CACHE_RETENTION=long` to extend prompt cache retention:
1430
+ This is only for Node bundles; it is not a browser or Cloudflare Workers workaround.
1062
1431
 
1063
- | Provider | Default | With `PI_CACHE_RETENTION=long` |
1064
- |----------|---------|-------------------------------|
1065
- | Anthropic | 5 minutes | 1 hour |
1066
- | OpenAI | in-memory | 24 hours |
1432
+ Bedrock is Node-only. Add it like any other provider:
1067
1433
 
1068
- This only affects direct API calls to `api.anthropic.com` and `api.openai.com`. Proxies and other providers are unaffected.
1434
+ ```typescript
1435
+ import { createModels } from '@sammorrowdrums/mcpi-ai';
1436
+ import { amazonBedrockProvider } from '@sammorrowdrums/mcpi-ai/providers/amazon-bedrock';
1069
1437
 
1070
- > **Note**: Extended cache retention may increase costs for Anthropic (cache writes are charged at a higher rate). OpenAI's 24h retention has no additional cost.
1438
+ const models = createModels();
1439
+ models.setProvider(amazonBedrockProvider());
1440
+ ```
1071
1441
 
1072
- ### Checking Environment Variables
1442
+ In normal Node package usage and code-split bundles, Bedrock loads its AWS SDK implementation lazily. For a standalone single-file bundle that must include Bedrock support, register the implementation module explicitly:
1073
1443
 
1074
1444
  ```typescript
1075
- import { getEnvApiKey } from '@mariozechner/pi-ai';
1445
+ import { setBedrockProviderModule } from '@sammorrowdrums/mcpi-ai/api/bedrock-converse-stream.lazy';
1446
+ import { bedrockProviderModule } from '@sammorrowdrums/mcpi-ai/bedrock-provider';
1447
+
1448
+ setBedrockProviderModule(bedrockProviderModule);
1449
+ ```
1450
+
1451
+ That explicit override bundles the AWS SDK. Without it, Bedrock's opaque runtime import expects the package's Bedrock implementation file to be available at runtime.
1452
+
1453
+ ### Provider-Scoped Environment Overrides
1076
1454
 
1077
- // Check if an API key is set in environment variables
1078
- const key = getEnvApiKey('openai'); // checks OPENAI_API_KEY
1455
+ Pass `env` in stream options to scope provider configuration to a request. Values in `env` are used before process environment variables for provider auth and configuration such as Cloudflare account IDs, Azure OpenAI settings, Vertex project/location, Bedrock settings, `MCPI_CACHE_RETENTION`, and `HTTP_PROXY`/`HTTPS_PROXY`.
1456
+
1457
+ ```typescript
1458
+ const models = builtinModels();
1459
+ const model = models.getModel('cloudflare-ai-gateway', 'workers-ai/@cf/moonshotai/kimi-k2.6')!;
1460
+
1461
+ const response = await models.complete(model, context, {
1462
+ env: {
1463
+ CLOUDFLARE_API_KEY: '...',
1464
+ CLOUDFLARE_ACCOUNT_ID: 'account-id',
1465
+ CLOUDFLARE_GATEWAY_ID: 'gateway-id'
1466
+ }
1467
+ });
1079
1468
  ```
1080
1469
 
1470
+ Use this when one process needs different provider settings per request, or when ambient environment variables should not leak into a provider call.
1471
+
1081
1472
  ## OAuth Providers
1082
1473
 
1083
- Several providers require OAuth authentication instead of static API keys:
1474
+ Several providers support OAuth authentication instead of static API keys:
1084
1475
 
1085
1476
  - **Anthropic** (Claude Pro/Max subscription)
1086
1477
  - **OpenAI Codex** (ChatGPT Plus/Pro subscription, access to GPT-5.x Codex models)
1087
1478
  - **GitHub Copilot** (Copilot subscription)
1088
- - **Google Gemini CLI** (Gemini 2.0/2.5 via Google Cloud Code Assist; free tier or paid subscription)
1089
- - **Antigravity** (Free Gemini 3, Claude, GPT-OSS via Google Cloud)
1479
+ - **OpenRouter** (OAuth PKCE that mints a user-controlled API key)
1480
+
1481
+ 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.
1482
+
1483
+ ```typescript
1484
+ import { createModels } from '@sammorrowdrums/mcpi-ai';
1485
+ import { anthropicProvider } from '@sammorrowdrums/mcpi-ai/providers/anthropic';
1486
+
1487
+ const models = createModels({ credentials: myStore }); // persistent CredentialStore
1488
+ models.setProvider(anthropicProvider());
1489
+
1490
+ // Login: Models drives the flow and persists the credential
1491
+ await models.login('anthropic', 'oauth', {
1492
+ prompt: async (p) => {
1493
+ // p.type: 'text' | 'secret' | 'select' | 'manual_code'
1494
+ // manual_code prompts race a local callback server; p.signal aborts them when the server wins
1495
+ return await askUser(p.message);
1496
+ },
1497
+ notify: (event) => {
1498
+ // event.type: 'info' | 'auth_url' | 'device_code' | 'progress'
1499
+ if (event.type === 'info') {
1500
+ console.log(event.message);
1501
+ for (const link of event.links ?? []) console.log(`${link.label ?? 'More information'}: ${link.url}`);
1502
+ }
1503
+ if (event.type === 'auth_url') console.log(`Open: ${event.url}`);
1504
+ if (event.type === 'device_code') console.log(`Code: ${event.userCode} at ${event.verificationUri}`);
1505
+ if (event.type === 'progress') console.log(event.message);
1506
+ },
1507
+ });
1508
+
1509
+ // From here on, requests resolve and refresh the token automatically
1510
+ const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
1511
+ await models.complete(model, context);
1090
1512
 
1091
- For paid Cloud Code Assist subscriptions, set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` to your project ID.
1513
+ // Logout
1514
+ await models.logout('anthropic');
1515
+ ```
1092
1516
 
1093
1517
  ### Vertex AI
1094
1518
 
1095
- Vertex AI models support either a Google Cloud API key or Application Default Credentials (ADC):
1519
+ Vertex AI models support either a Google Cloud API key or Application Default Credentials (ADC). Its provider-owned API-key login flow can configure either method:
1096
1520
 
1097
1521
  - **API key**: Set `GOOGLE_CLOUD_API_KEY` or pass `apiKey` in the call options.
1098
1522
  - **Local development (ADC)**: Run `gcloud auth application-default login`
@@ -1100,8 +1524,6 @@ Vertex AI models support either a Google Cloud API key or Application Default Cr
1100
1524
 
1101
1525
  When using ADC, also set `GOOGLE_CLOUD_PROJECT` (or `GCLOUD_PROJECT`) and `GOOGLE_CLOUD_LOCATION`. You can also pass `project`/`location` in the call options. When using `GOOGLE_CLOUD_API_KEY`, `project` and `location` are not required.
1102
1526
 
1103
- Example:
1104
-
1105
1527
  ```bash
1106
1528
  # Local (uses your user credentials)
1107
1529
  gcloud auth application-default login
@@ -1112,23 +1534,6 @@ export GOOGLE_CLOUD_LOCATION="us-central1"
1112
1534
  export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
1113
1535
  ```
1114
1536
 
1115
- ```typescript
1116
- import { getModel, complete } from '@mariozechner/pi-ai';
1117
-
1118
- (async () => {
1119
- const model = getModel('google-vertex', 'gemini-2.5-flash');
1120
- const response = await complete(model, {
1121
- messages: [{ role: 'user', content: 'Hello from Vertex AI' }]
1122
- }, {
1123
- apiKey: process.env.GOOGLE_CLOUD_API_KEY,
1124
- });
1125
-
1126
- for (const block of response.content) {
1127
- if (block.type === 'text') console.log(block.text);
1128
- }
1129
- })().catch(console.error);
1130
- ```
1131
-
1132
1537
  Official docs: [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials)
1133
1538
 
1134
1539
  ### CLI Login
@@ -1136,133 +1541,86 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a
1136
1541
  The quickest way to authenticate:
1137
1542
 
1138
1543
  ```bash
1139
- npx @mariozechner/pi-ai login # interactive provider selection
1140
- npx @mariozechner/pi-ai login anthropic # login to specific provider
1141
- npx @mariozechner/pi-ai list # list available providers
1544
+ npx @sammorrowdrums/mcpi-ai login # interactive provider selection
1545
+ npx @sammorrowdrums/mcpi-ai login anthropic # login to specific provider
1546
+ npx @sammorrowdrums/mcpi-ai list # list available providers
1142
1547
  ```
1143
1548
 
1144
1549
  Credentials are saved to `auth.json` in the current directory.
1145
1550
 
1146
1551
  ### Programmatic OAuth
1147
1552
 
1148
- The library provides login and token refresh functions via the `@mariozechner/pi-ai/oauth` entry point. Credential storage is the caller's responsibility.
1149
-
1150
- ```typescript
1151
- import {
1152
- // Login functions (return credentials, do not store)
1153
- loginAnthropic,
1154
- loginOpenAICodex,
1155
- loginGitHubCopilot,
1156
- loginGeminiCli,
1157
- loginAntigravity,
1158
-
1159
- // Token management
1160
- refreshOAuthToken, // (provider, credentials) => new credentials
1161
- getOAuthApiKey, // (provider, credentialsMap) => { newCredentials, apiKey } | null
1162
-
1163
- // Types
1164
- type OAuthProvider, // 'anthropic' | 'openai-codex' | 'github-copilot' | 'google-gemini-cli' | 'google-antigravity'
1165
- type OAuthCredentials,
1166
- } from '@mariozechner/pi-ai/oauth';
1167
- ```
1553
+ Built-in login and refresh flows are private provider implementations. Use provider-owned `OAuthAuth`, which composes with `CredentialStore` and gets locked auto-refresh through `Models`. The `@sammorrowdrums/mcpi-ai/oauth` entry point retains only type declarations required by coding-agent extension OAuth compatibility.
1168
1554
 
1169
- ### Login Flow Example
1555
+ Provider notes:
1170
1556
 
1171
- ```typescript
1172
- import { loginGitHubCopilot } from '@mariozechner/pi-ai/oauth';
1173
- import { writeFileSync } from 'fs';
1557
+ **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.
1174
1558
 
1175
- const credentials = await loginGitHubCopilot({
1176
- onAuth: (url, instructions) => {
1177
- console.log(`Open: ${url}`);
1178
- if (instructions) console.log(instructions);
1179
- },
1180
- onPrompt: async (prompt) => {
1181
- return await getUserInput(prompt.message);
1182
- },
1183
- onProgress: (message) => console.log(message)
1184
- });
1559
+ **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.
1185
1560
 
1186
- // Store credentials yourself
1187
- const auth = { 'github-copilot': { type: 'oauth', ...credentials } };
1188
- writeFileSync('auth.json', JSON.stringify(auth, null, 2));
1189
- ```
1561
+ **GitHub Copilot**: If you get "The requested model is not supported" error, enable the model manually in VS Code: open Copilot Chat, click the model selector, select the model (warning icon), and click "Enable".
1190
1562
 
1191
- ### Using OAuth Tokens
1563
+ ## Migrating from the Old Global API
1192
1564
 
1193
- Use `getOAuthApiKey()` to get an API key, automatically refreshing if expired:
1565
+ Older versions exposed a global API: `stream()`/`complete()` dispatching on `model.api` via a global registry, sync `getModel()`/`getModels()`/`getProviders()` catalog reads, `registerApiProvider()`, `getEnvApiKey()`, and per-API lazy stream functions. That surface lives unchanged on the **compat entrypoint**:
1194
1566
 
1195
1567
  ```typescript
1196
- import { getModel, complete } from '@mariozechner/pi-ai';
1197
- import { getOAuthApiKey } from '@mariozechner/pi-ai/oauth';
1198
- import { readFileSync, writeFileSync } from 'fs';
1199
-
1200
- // Load your stored credentials
1201
- const auth = JSON.parse(readFileSync('auth.json', 'utf-8'));
1202
-
1203
- // Get API key (refreshes if expired)
1204
- const result = await getOAuthApiKey('github-copilot', auth);
1205
- if (!result) throw new Error('Not logged in');
1206
-
1207
- // Save refreshed credentials
1208
- auth['github-copilot'] = { type: 'oauth', ...result.newCredentials };
1209
- writeFileSync('auth.json', JSON.stringify(auth, null, 2));
1210
-
1211
- // Use the API key
1212
- const model = getModel('github-copilot', 'gpt-4o');
1213
- const response = await complete(model, {
1214
- messages: [{ role: 'user', content: 'Hello!' }]
1215
- }, { apiKey: result.apiKey });
1216
- ```
1217
-
1218
- ### Provider Notes
1568
+ // Before
1569
+ import { getModel, complete } from '@sammorrowdrums/mcpi-ai';
1219
1570
 
1220
- **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. You can set `transport` in stream options to `"sse"`, `"websocket"`, or `"auto"` for Codex Responses transport selection. When using WebSocket with a `sessionId`, connections are reused per session and expire after 5 minutes of inactivity.
1221
-
1222
- **Azure OpenAI (Responses)**: Uses the Responses API only. Set `AZURE_OPENAI_API_KEY` and either `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME`. 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.
1571
+ // After (verbatim behavior, one import-path change)
1572
+ import { getModel, complete } from '@sammorrowdrums/mcpi-ai/compat';
1573
+ ```
1223
1574
 
1224
- **GitHub Copilot**: If you get "The requested model is not supported" error, enable the model manually in VS Code: open Copilot Chat, click the model selector, select the model (warning icon), and click "Enable".
1575
+ Compat is a strict superset of the root entrypoint, so a file can switch its import path wholesale. It will be removed in a future release; migrate to `createModels()` + provider factories:
1225
1576
 
1226
- **Google Gemini CLI / Antigravity**: These use Google Cloud OAuth. The `apiKey` returned by `getOAuthApiKey()` is a JSON string containing both the token and project ID, which the library handles automatically.
1577
+ | Old | New |
1578
+ |-----|-----|
1579
+ | `getModel('openai', 'gpt-4o-mini')` | `models.getModel('openai', 'gpt-4o-mini')` or `getBuiltinModel()` from `providers/all` |
1580
+ | `getModels('anthropic')` / `getProviders()` | `models.getModels('anthropic')` / `models.getProviders()` or `getBuiltin*` |
1581
+ | `stream(model, ctx, opts)` (env-key injection) | `models.stream(model, ctx, opts)` (provider auth resolution) |
1582
+ | `registerApiProvider({ api, stream, streamSimple })` | `createProvider({ id, auth, models, api })` + `models.setProvider()` |
1583
+ | `getEnvApiKey('openai')` | `await models.getAuth(model.provider)` |
1584
+ | `streamAnthropic(model, ctx, opts)` | `stream` from `@sammorrowdrums/mcpi-ai/api/anthropic-messages`, or a provider in a collection |
1585
+ | `registerFauxProvider()` | `fauxProvider()` + `models.setProvider()` |
1227
1586
 
1228
1587
  ## Development
1229
1588
 
1230
1589
  ### Adding a New Provider
1231
1590
 
1232
- Adding a new LLM provider requires changes across multiple files. This checklist covers all necessary steps:
1591
+ Adding a new LLM provider requires changes across multiple files. The layered layout: API implementations live in `src/api/`, provider factories in `src/providers/`, stable generated catalog wrappers live in `src/providers/<id>.models.ts`, and `src/models.generated.ts` registers them. This checklist covers all necessary steps:
1233
1592
 
1234
1593
  #### 1. Core Types (`src/types.ts`)
1235
1594
 
1236
- - Add the API identifier to `KnownApi` (for example `"bedrock-converse-stream"`)
1237
- - Create an options interface extending `StreamOptions` (for example `BedrockOptions`)
1595
+ - Add the API identifier to `KnownApi` (for example `"bedrock-converse-stream"`), if it is a new API
1238
1596
  - Add the provider name to `KnownProvider` (for example `"amazon-bedrock"`)
1597
+ - Add the options type to `ApiOptionsMap`
1239
1598
 
1240
- #### 2. Provider Implementation (`src/providers/`)
1599
+ #### 2. API Implementation (`src/api/<api-id>.ts`, only for a new API)
1241
1600
 
1242
- Create a new provider file (for example `amazon-bedrock.ts`) that exports:
1601
+ Create a new API implementation file (for example `bedrock-converse-stream.ts`) that exports exactly `stream` and `streamSimple`, plus:
1243
1602
 
1244
- - `stream<Provider>()` function returning `AssistantMessageEventStream`
1245
- - `streamSimple<Provider>()` for `SimpleStreamOptions` mapping
1246
- - Provider-specific options interface
1603
+ - An options interface extending `StreamOptions` (for example `BedrockOptions`)
1247
1604
  - Message conversion functions to transform `Context` to provider format
1248
1605
  - Tool conversion if the provider supports tools
1249
1606
  - Response parsing to emit standardized events (`text`, `tool_call`, `thinking`, `usage`, `stop`)
1250
1607
 
1251
- #### 3. API Registry Integration (`src/providers/register-builtins.ts`)
1608
+ 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 `@sammorrowdrums/mcpi-ai`.
1252
1609
 
1253
- - Register the API with `registerApiProvider()`
1254
- - Add a package subpath export in `package.json` for the provider module (`./dist/providers/<provider>.js`)
1255
- - Add lazy loader wrappers in `src/providers/register-builtins.ts`, do not statically import provider implementation modules there
1256
- - Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@mariozechner/pi-ai`
1257
- - Add credential detection in `env-api-keys.ts` for the new provider
1258
- - Ensure `streamSimple` handles auth lookup via `getEnvApiKey()` or provider-specific auth
1259
-
1260
- #### 4. Model Generation (`scripts/generate-models.ts`)
1610
+ #### 3. Model Generation (`scripts/generate-models.ts`, `scripts/generate-image-models.ts`)
1261
1611
 
1262
1612
  - Add logic to fetch and parse models from the provider's source (e.g., models.dev API)
1263
- - Map provider model data to the standardized `Model` interface
1613
+ - 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
1614
+ - Map image-generation provider model data to the standardized `ImagesModel` interface via `scripts/generate-image-models.ts`
1264
1615
  - Handle provider-specific quirks (pricing format, capability flags, model ID transformations)
1265
1616
 
1617
+ #### 4. Provider Factory (`src/providers/<id>.ts`)
1618
+
1619
+ - `createProvider()` wiring catalog + auth + the lazy API wrapper
1620
+ - Auth: `envApiKeyAuth` for standard key providers, a custom `ApiKeyAuth` for ambient auth (AWS profiles, ADC), `lazyOAuth` where an OAuth flow exists
1621
+ - Register the factory in `src/providers/all.ts`
1622
+ - If it is a new API: register it in the builtin list in `src/compat.ts` and add the package subpath export in `package.json`
1623
+
1266
1624
  #### 5. Tests (`test/`)
1267
1625
 
1268
1626
  Create or update test files to cover the new provider:
@@ -1278,6 +1636,7 @@ Create or update test files to cover the new provider:
1278
1636
  - `image-tool-result.test.ts` - Images in tool results
1279
1637
  - `total-tokens.test.ts` - Token counting accuracy
1280
1638
  - `cross-provider-handoff.test.ts` - Cross-provider context replay
1639
+ - `providers.test.ts` - Provider listing and auth resolution
1281
1640
 
1282
1641
  For `cross-provider-handoff.test.ts`, add at least one provider/model pair. If the provider exposes multiple model families (for example GPT and Claude), add at least one pair per family.
1283
1642