@clinebot/llms 0.0.20 → 0.0.21

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 (319) hide show
  1. package/dist/config-browser.d.ts +1 -0
  2. package/dist/config-browser.d.ts.map +1 -0
  3. package/dist/config.d.ts +1 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/index.browser.d.ts +1 -0
  6. package/dist/index.browser.d.ts.map +1 -0
  7. package/dist/index.browser.js +5 -5
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +12 -12
  11. package/dist/models/generated-access.d.ts +1 -0
  12. package/dist/models/generated-access.d.ts.map +1 -0
  13. package/dist/models/generated-provider-loaders.d.ts +1 -0
  14. package/dist/models/generated-provider-loaders.d.ts.map +1 -0
  15. package/dist/models/generated.d.ts +1 -0
  16. package/dist/models/generated.d.ts.map +1 -0
  17. package/dist/models/index.d.ts +1 -0
  18. package/dist/models/index.d.ts.map +1 -0
  19. package/dist/models/models-dev-catalog.d.ts +1 -0
  20. package/dist/models/models-dev-catalog.d.ts.map +1 -0
  21. package/dist/models/providers/aihubmix.d.ts +1 -0
  22. package/dist/models/providers/aihubmix.d.ts.map +1 -0
  23. package/dist/models/providers/anthropic.d.ts +1 -0
  24. package/dist/models/providers/anthropic.d.ts.map +1 -0
  25. package/dist/models/providers/asksage.d.ts +1 -0
  26. package/dist/models/providers/asksage.d.ts.map +1 -0
  27. package/dist/models/providers/baseten.d.ts +1 -0
  28. package/dist/models/providers/baseten.d.ts.map +1 -0
  29. package/dist/models/providers/bedrock.d.ts +1 -0
  30. package/dist/models/providers/bedrock.d.ts.map +1 -0
  31. package/dist/models/providers/cerebras.d.ts +1 -0
  32. package/dist/models/providers/cerebras.d.ts.map +1 -0
  33. package/dist/models/providers/claude-code.d.ts +1 -0
  34. package/dist/models/providers/claude-code.d.ts.map +1 -0
  35. package/dist/models/providers/cline.d.ts +1 -0
  36. package/dist/models/providers/cline.d.ts.map +1 -0
  37. package/dist/models/providers/deepseek.d.ts +1 -0
  38. package/dist/models/providers/deepseek.d.ts.map +1 -0
  39. package/dist/models/providers/dify.d.ts +1 -0
  40. package/dist/models/providers/dify.d.ts.map +1 -0
  41. package/dist/models/providers/doubao.d.ts +1 -0
  42. package/dist/models/providers/doubao.d.ts.map +1 -0
  43. package/dist/models/providers/fireworks.d.ts +1 -0
  44. package/dist/models/providers/fireworks.d.ts.map +1 -0
  45. package/dist/models/providers/gemini.d.ts +1 -0
  46. package/dist/models/providers/gemini.d.ts.map +1 -0
  47. package/dist/models/providers/groq.d.ts +1 -0
  48. package/dist/models/providers/groq.d.ts.map +1 -0
  49. package/dist/models/providers/hicap.d.ts +1 -0
  50. package/dist/models/providers/hicap.d.ts.map +1 -0
  51. package/dist/models/providers/huawei-cloud-maas.d.ts +1 -0
  52. package/dist/models/providers/huawei-cloud-maas.d.ts.map +1 -0
  53. package/dist/models/providers/huggingface.d.ts +1 -0
  54. package/dist/models/providers/huggingface.d.ts.map +1 -0
  55. package/dist/models/providers/index.d.ts +1 -0
  56. package/dist/models/providers/index.d.ts.map +1 -0
  57. package/dist/models/providers/litellm.d.ts +1 -0
  58. package/dist/models/providers/litellm.d.ts.map +1 -0
  59. package/dist/models/providers/lmstudio.d.ts +1 -0
  60. package/dist/models/providers/lmstudio.d.ts.map +1 -0
  61. package/dist/models/providers/minimax.d.ts +1 -0
  62. package/dist/models/providers/minimax.d.ts.map +1 -0
  63. package/dist/models/providers/mistral.d.ts +1 -0
  64. package/dist/models/providers/mistral.d.ts.map +1 -0
  65. package/dist/models/providers/moonshot.d.ts +1 -0
  66. package/dist/models/providers/moonshot.d.ts.map +1 -0
  67. package/dist/models/providers/nebius.d.ts +1 -0
  68. package/dist/models/providers/nebius.d.ts.map +1 -0
  69. package/dist/models/providers/nous-research.d.ts +1 -0
  70. package/dist/models/providers/nous-research.d.ts.map +1 -0
  71. package/dist/models/providers/oca.d.ts +1 -0
  72. package/dist/models/providers/oca.d.ts.map +1 -0
  73. package/dist/models/providers/ollama.d.ts +1 -0
  74. package/dist/models/providers/ollama.d.ts.map +1 -0
  75. package/dist/models/providers/openai-codex.d.ts +1 -0
  76. package/dist/models/providers/openai-codex.d.ts.map +1 -0
  77. package/dist/models/providers/openai.d.ts +1 -0
  78. package/dist/models/providers/openai.d.ts.map +1 -0
  79. package/dist/models/providers/opencode.d.ts +1 -0
  80. package/dist/models/providers/opencode.d.ts.map +1 -0
  81. package/dist/models/providers/openrouter.d.ts +1 -0
  82. package/dist/models/providers/openrouter.d.ts.map +1 -0
  83. package/dist/models/providers/qwen-code.d.ts +1 -0
  84. package/dist/models/providers/qwen-code.d.ts.map +1 -0
  85. package/dist/models/providers/qwen.d.ts +1 -0
  86. package/dist/models/providers/qwen.d.ts.map +1 -0
  87. package/dist/models/providers/requesty.d.ts +1 -0
  88. package/dist/models/providers/requesty.d.ts.map +1 -0
  89. package/dist/models/providers/sambanova.d.ts +1 -0
  90. package/dist/models/providers/sambanova.d.ts.map +1 -0
  91. package/dist/models/providers/sapaicore.d.ts +1 -0
  92. package/dist/models/providers/sapaicore.d.ts.map +1 -0
  93. package/dist/models/providers/together.d.ts +1 -0
  94. package/dist/models/providers/together.d.ts.map +1 -0
  95. package/dist/models/providers/vercel-ai-gateway.d.ts +1 -0
  96. package/dist/models/providers/vercel-ai-gateway.d.ts.map +1 -0
  97. package/dist/models/providers/vertex.d.ts +1 -0
  98. package/dist/models/providers/vertex.d.ts.map +1 -0
  99. package/dist/models/providers/xai.d.ts +1 -0
  100. package/dist/models/providers/xai.d.ts.map +1 -0
  101. package/dist/models/providers/zai.d.ts +1 -0
  102. package/dist/models/providers/zai.d.ts.map +1 -0
  103. package/dist/models/query.d.ts +1 -0
  104. package/dist/models/query.d.ts.map +1 -0
  105. package/dist/models/registry.d.ts +1 -0
  106. package/dist/models/registry.d.ts.map +1 -0
  107. package/dist/models/schemas/index.d.ts +1 -0
  108. package/dist/models/schemas/index.d.ts.map +1 -0
  109. package/dist/models/schemas/model.d.ts +1 -0
  110. package/dist/models/schemas/model.d.ts.map +1 -0
  111. package/dist/models/schemas/query.d.ts +1 -0
  112. package/dist/models/schemas/query.d.ts.map +1 -0
  113. package/dist/providers/handlers/ai-sdk-community.d.ts +1 -0
  114. package/dist/providers/handlers/ai-sdk-community.d.ts.map +1 -0
  115. package/dist/providers/handlers/ai-sdk-provider-base.d.ts +1 -0
  116. package/dist/providers/handlers/ai-sdk-provider-base.d.ts.map +1 -0
  117. package/dist/providers/handlers/anthropic-base.d.ts +1 -0
  118. package/dist/providers/handlers/anthropic-base.d.ts.map +1 -0
  119. package/dist/providers/handlers/asksage.d.ts +1 -0
  120. package/dist/providers/handlers/asksage.d.ts.map +1 -0
  121. package/dist/providers/handlers/auth.d.ts +1 -0
  122. package/dist/providers/handlers/auth.d.ts.map +1 -0
  123. package/dist/providers/handlers/base.d.ts +1 -0
  124. package/dist/providers/handlers/base.d.ts.map +1 -0
  125. package/dist/providers/handlers/bedrock-base.d.ts +1 -0
  126. package/dist/providers/handlers/bedrock-base.d.ts.map +1 -0
  127. package/dist/providers/handlers/bedrock-client.d.ts +1 -0
  128. package/dist/providers/handlers/bedrock-client.d.ts.map +1 -0
  129. package/dist/providers/handlers/community-sdk.d.ts +1 -0
  130. package/dist/providers/handlers/community-sdk.d.ts.map +1 -0
  131. package/dist/providers/handlers/fetch-base.d.ts +1 -0
  132. package/dist/providers/handlers/fetch-base.d.ts.map +1 -0
  133. package/dist/providers/handlers/gemini-base.d.ts +1 -0
  134. package/dist/providers/handlers/gemini-base.d.ts.map +1 -0
  135. package/dist/providers/handlers/index.d.ts +1 -0
  136. package/dist/providers/handlers/index.d.ts.map +1 -0
  137. package/dist/providers/handlers/openai-base.d.ts +1 -0
  138. package/dist/providers/handlers/openai-base.d.ts.map +1 -0
  139. package/dist/providers/handlers/openai-responses.d.ts +1 -0
  140. package/dist/providers/handlers/openai-responses.d.ts.map +1 -0
  141. package/dist/providers/handlers/providers.d.ts +1 -0
  142. package/dist/providers/handlers/providers.d.ts.map +1 -0
  143. package/dist/providers/handlers/r1-base.d.ts +1 -0
  144. package/dist/providers/handlers/r1-base.d.ts.map +1 -0
  145. package/dist/providers/handlers/registry.d.ts +1 -0
  146. package/dist/providers/handlers/registry.d.ts.map +1 -0
  147. package/dist/providers/handlers/vertex.d.ts +1 -0
  148. package/dist/providers/handlers/vertex.d.ts.map +1 -0
  149. package/dist/providers/index.d.ts +1 -0
  150. package/dist/providers/index.d.ts.map +1 -0
  151. package/dist/providers/public.browser.d.ts +1 -0
  152. package/dist/providers/public.browser.d.ts.map +1 -0
  153. package/dist/providers/public.d.ts +1 -0
  154. package/dist/providers/public.d.ts.map +1 -0
  155. package/dist/providers/shared/openai-compatible.d.ts +1 -0
  156. package/dist/providers/shared/openai-compatible.d.ts.map +1 -0
  157. package/dist/providers/transform/ai-sdk-community-format.d.ts +1 -0
  158. package/dist/providers/transform/ai-sdk-community-format.d.ts.map +1 -0
  159. package/dist/providers/transform/anthropic-format.d.ts +1 -0
  160. package/dist/providers/transform/anthropic-format.d.ts.map +1 -0
  161. package/dist/providers/transform/content-format.d.ts +1 -0
  162. package/dist/providers/transform/content-format.d.ts.map +1 -0
  163. package/dist/providers/transform/gemini-format.d.ts +1 -0
  164. package/dist/providers/transform/gemini-format.d.ts.map +1 -0
  165. package/dist/providers/transform/index.d.ts +1 -0
  166. package/dist/providers/transform/index.d.ts.map +1 -0
  167. package/dist/providers/transform/openai-format.d.ts +1 -0
  168. package/dist/providers/transform/openai-format.d.ts.map +1 -0
  169. package/dist/providers/transform/r1-format.d.ts +1 -0
  170. package/dist/providers/transform/r1-format.d.ts.map +1 -0
  171. package/dist/providers/types/config.d.ts +1 -0
  172. package/dist/providers/types/config.d.ts.map +1 -0
  173. package/dist/providers/types/handler.d.ts +1 -0
  174. package/dist/providers/types/handler.d.ts.map +1 -0
  175. package/dist/providers/types/index.d.ts +1 -0
  176. package/dist/providers/types/index.d.ts.map +1 -0
  177. package/dist/providers/types/messages.d.ts +1 -0
  178. package/dist/providers/types/messages.d.ts.map +1 -0
  179. package/dist/providers/types/model-info.d.ts +1 -0
  180. package/dist/providers/types/model-info.d.ts.map +1 -0
  181. package/dist/providers/types/provider-ids.d.ts +1 -1
  182. package/dist/providers/types/provider-ids.d.ts.map +1 -0
  183. package/dist/providers/types/settings.d.ts +1 -0
  184. package/dist/providers/types/settings.d.ts.map +1 -0
  185. package/dist/providers/types/stream.d.ts +1 -0
  186. package/dist/providers/types/stream.d.ts.map +1 -0
  187. package/dist/providers/utils/index.d.ts +1 -0
  188. package/dist/providers/utils/index.d.ts.map +1 -0
  189. package/dist/providers/utils/retry.d.ts +1 -0
  190. package/dist/providers/utils/retry.d.ts.map +1 -0
  191. package/dist/providers/utils/stream-processor.d.ts +1 -0
  192. package/dist/providers/utils/stream-processor.d.ts.map +1 -0
  193. package/dist/providers/utils/tool-processor.d.ts +1 -0
  194. package/dist/providers/utils/tool-processor.d.ts.map +1 -0
  195. package/dist/sdk.d.ts +1 -0
  196. package/dist/sdk.d.ts.map +1 -0
  197. package/dist/types.d.ts +1 -0
  198. package/dist/types.d.ts.map +1 -0
  199. package/package.json +2 -3
  200. package/src/catalog.ts +0 -20
  201. package/src/config-browser.ts +0 -11
  202. package/src/config.ts +0 -49
  203. package/src/index.browser.ts +0 -9
  204. package/src/index.ts +0 -10
  205. package/src/live-providers.test.ts +0 -138
  206. package/src/models/generated-access.ts +0 -41
  207. package/src/models/generated-provider-loaders.ts +0 -166
  208. package/src/models/generated.ts +0 -11785
  209. package/src/models/index.ts +0 -271
  210. package/src/models/models-dev-catalog.test.ts +0 -161
  211. package/src/models/models-dev-catalog.ts +0 -168
  212. package/src/models/providers/aihubmix.ts +0 -19
  213. package/src/models/providers/anthropic.ts +0 -60
  214. package/src/models/providers/asksage.ts +0 -19
  215. package/src/models/providers/baseten.ts +0 -21
  216. package/src/models/providers/bedrock.ts +0 -30
  217. package/src/models/providers/cerebras.ts +0 -24
  218. package/src/models/providers/claude-code.ts +0 -51
  219. package/src/models/providers/cline.ts +0 -25
  220. package/src/models/providers/deepseek.ts +0 -33
  221. package/src/models/providers/dify.ts +0 -17
  222. package/src/models/providers/doubao.ts +0 -33
  223. package/src/models/providers/fireworks.ts +0 -34
  224. package/src/models/providers/gemini.ts +0 -43
  225. package/src/models/providers/groq.ts +0 -33
  226. package/src/models/providers/hicap.ts +0 -18
  227. package/src/models/providers/huawei-cloud-maas.ts +0 -18
  228. package/src/models/providers/huggingface.ts +0 -22
  229. package/src/models/providers/index.ts +0 -162
  230. package/src/models/providers/litellm.ts +0 -19
  231. package/src/models/providers/lmstudio.ts +0 -22
  232. package/src/models/providers/minimax.ts +0 -34
  233. package/src/models/providers/mistral.ts +0 -19
  234. package/src/models/providers/moonshot.ts +0 -34
  235. package/src/models/providers/nebius.ts +0 -24
  236. package/src/models/providers/nous-research.ts +0 -21
  237. package/src/models/providers/oca.ts +0 -30
  238. package/src/models/providers/ollama.ts +0 -18
  239. package/src/models/providers/openai-codex.ts +0 -46
  240. package/src/models/providers/openai.ts +0 -43
  241. package/src/models/providers/opencode.ts +0 -28
  242. package/src/models/providers/openrouter.ts +0 -24
  243. package/src/models/providers/qwen-code.ts +0 -33
  244. package/src/models/providers/qwen.ts +0 -34
  245. package/src/models/providers/requesty.ts +0 -23
  246. package/src/models/providers/sambanova.ts +0 -23
  247. package/src/models/providers/sapaicore.ts +0 -34
  248. package/src/models/providers/together.ts +0 -35
  249. package/src/models/providers/vercel-ai-gateway.ts +0 -23
  250. package/src/models/providers/vertex.ts +0 -36
  251. package/src/models/providers/xai.ts +0 -34
  252. package/src/models/providers/zai.ts +0 -25
  253. package/src/models/query.ts +0 -407
  254. package/src/models/registry.ts +0 -511
  255. package/src/models/schemas/index.ts +0 -62
  256. package/src/models/schemas/model.ts +0 -308
  257. package/src/models/schemas/query.ts +0 -336
  258. package/src/providers/browser.ts +0 -4
  259. package/src/providers/handlers/ai-sdk-community.ts +0 -229
  260. package/src/providers/handlers/ai-sdk-provider-base.ts +0 -203
  261. package/src/providers/handlers/anthropic-base.test.ts +0 -30
  262. package/src/providers/handlers/anthropic-base.ts +0 -387
  263. package/src/providers/handlers/asksage.test.ts +0 -103
  264. package/src/providers/handlers/asksage.ts +0 -138
  265. package/src/providers/handlers/auth.test.ts +0 -19
  266. package/src/providers/handlers/auth.ts +0 -121
  267. package/src/providers/handlers/base.test.ts +0 -230
  268. package/src/providers/handlers/base.ts +0 -310
  269. package/src/providers/handlers/bedrock-base.ts +0 -390
  270. package/src/providers/handlers/bedrock-client.ts +0 -100
  271. package/src/providers/handlers/codex.test.ts +0 -160
  272. package/src/providers/handlers/community-sdk.test.ts +0 -321
  273. package/src/providers/handlers/community-sdk.ts +0 -391
  274. package/src/providers/handlers/fetch-base.ts +0 -68
  275. package/src/providers/handlers/gemini-base.test.ts +0 -261
  276. package/src/providers/handlers/gemini-base.ts +0 -307
  277. package/src/providers/handlers/index.ts +0 -67
  278. package/src/providers/handlers/openai-base.ts +0 -341
  279. package/src/providers/handlers/openai-responses.test.ts +0 -259
  280. package/src/providers/handlers/openai-responses.ts +0 -634
  281. package/src/providers/handlers/providers.test.ts +0 -120
  282. package/src/providers/handlers/providers.ts +0 -563
  283. package/src/providers/handlers/r1-base.ts +0 -283
  284. package/src/providers/handlers/registry.ts +0 -185
  285. package/src/providers/handlers/vertex.test.ts +0 -124
  286. package/src/providers/handlers/vertex.ts +0 -302
  287. package/src/providers/index.ts +0 -534
  288. package/src/providers/public.browser.ts +0 -20
  289. package/src/providers/public.ts +0 -51
  290. package/src/providers/shared/openai-compatible.ts +0 -63
  291. package/src/providers/transform/ai-sdk-community-format.test.ts +0 -73
  292. package/src/providers/transform/ai-sdk-community-format.ts +0 -115
  293. package/src/providers/transform/anthropic-format.ts +0 -230
  294. package/src/providers/transform/content-format.ts +0 -34
  295. package/src/providers/transform/format-conversion.test.ts +0 -413
  296. package/src/providers/transform/gemini-format.ts +0 -262
  297. package/src/providers/transform/index.ts +0 -22
  298. package/src/providers/transform/openai-format.ts +0 -290
  299. package/src/providers/transform/r1-format.ts +0 -287
  300. package/src/providers/types/config.ts +0 -396
  301. package/src/providers/types/handler.ts +0 -92
  302. package/src/providers/types/index.ts +0 -120
  303. package/src/providers/types/messages.ts +0 -162
  304. package/src/providers/types/model-info.test.ts +0 -57
  305. package/src/providers/types/model-info.ts +0 -65
  306. package/src/providers/types/provider-ids.test.ts +0 -12
  307. package/src/providers/types/provider-ids.ts +0 -89
  308. package/src/providers/types/settings.test.ts +0 -49
  309. package/src/providers/types/settings.ts +0 -533
  310. package/src/providers/types/stream.ts +0 -117
  311. package/src/providers/utils/index.ts +0 -27
  312. package/src/providers/utils/retry.test.ts +0 -140
  313. package/src/providers/utils/retry.ts +0 -188
  314. package/src/providers/utils/stream-processor.test.ts +0 -232
  315. package/src/providers/utils/stream-processor.ts +0 -472
  316. package/src/providers/utils/tool-processor.test.ts +0 -235
  317. package/src/providers/utils/tool-processor.ts +0 -146
  318. package/src/sdk.ts +0 -264
  319. package/src/types.ts +0 -79
@@ -1,341 +0,0 @@
1
- /**
2
- * OpenAI Base Handler
3
- *
4
- * Base class for all handlers using the OpenAI SDK.
5
- * This handles the common streaming logic and can be extended for:
6
- * - Standard OpenAI API
7
- * - OpenAI-compatible providers (DeepSeek, xAI, Together, etc.)
8
- * - OpenRouter
9
- * - Azure OpenAI
10
- */
11
-
12
- import OpenAI from "openai";
13
- import type { ChatCompletionChunk } from "openai/resources/chat/completions";
14
- import {
15
- convertToOpenAIMessages,
16
- getOpenAIToolParams,
17
- } from "../transform/openai-format";
18
- import type {
19
- ApiStream,
20
- HandlerModelInfo,
21
- ModelCapability,
22
- ModelInfo,
23
- ProviderConfig,
24
- } from "../types";
25
- import type { Message, ToolDefinition } from "../types/messages";
26
- import { retryStream } from "../utils/retry";
27
- import { ToolCallProcessor } from "../utils/tool-processor";
28
- import { getMissingApiKeyError, resolveApiKeyForProvider } from "./auth";
29
- import { BaseHandler } from "./base";
30
-
31
- const DEFAULT_REASONING_EFFORT = "medium" as const;
32
-
33
- /**
34
- * Base handler for OpenAI SDK-based providers
35
- *
36
- * Uses ProviderConfig fields:
37
- * - baseUrl: Base URL for the API
38
- * - modelId: Model ID
39
- * - knownModels: Known models with their info
40
- * - headers: Custom headers
41
- * - capabilities: Array of supported capabilities (reasoning, prompt-cache, etc.)
42
- */
43
- export class OpenAIBaseHandler extends BaseHandler {
44
- protected client: OpenAI | undefined;
45
-
46
- /**
47
- * Ensure the OpenAI client is initialized
48
- * Can be overridden for custom client creation (e.g., Azure)
49
- */
50
- protected ensureClient(): OpenAI {
51
- if (!this.client) {
52
- const baseURL = this.config.baseUrl;
53
-
54
- if (!baseURL) {
55
- throw new Error("Base URL is required. Set baseUrl in config.");
56
- }
57
- const apiKey = resolveApiKeyForProvider(
58
- this.config.providerId,
59
- this.config.apiKey,
60
- );
61
- if (!apiKey) {
62
- throw new Error(getMissingApiKeyError(this.config.providerId));
63
- }
64
- const requestHeaders = this.getRequestHeaders();
65
- // const hasAuthorizationHeader = Object.keys(requestHeaders).some((key) => key.toLowerCase() === "authorization")
66
-
67
- this.client = new OpenAI({
68
- apiKey,
69
- baseURL,
70
- defaultHeaders: requestHeaders,
71
- });
72
- }
73
- return this.client;
74
- }
75
-
76
- /**
77
- * Get model info, falling back to provider defaults
78
- */
79
- getModel(): HandlerModelInfo {
80
- const modelId = this.config.modelId;
81
- if (!modelId) {
82
- throw new Error("Model ID is required. Set modelId in config.");
83
- }
84
-
85
- const modelInfo =
86
- this.config.modelInfo ??
87
- this.config.knownModels?.[modelId] ??
88
- this.getDefaultModelInfo();
89
-
90
- return { id: modelId, info: { ...modelInfo, id: modelId } };
91
- }
92
-
93
- protected getDefaultModelInfo(): ModelInfo {
94
- const capabilities: ModelCapability[] = this.config.capabilities?.includes(
95
- "prompt-cache",
96
- )
97
- ? ["prompt-cache"]
98
- : [];
99
- return {
100
- id: this.config.modelId,
101
- capabilities,
102
- };
103
- }
104
-
105
- getMessages(
106
- systemPrompt: string,
107
- messages: Message[],
108
- ): OpenAI.Chat.ChatCompletionMessageParam[] {
109
- const model = this.getModel();
110
- const supportsPromptCache = this.supportsPromptCache(model.info);
111
- const systemMessage = supportsPromptCache
112
- ? ({
113
- role: "system",
114
- content: [
115
- {
116
- type: "text",
117
- text: systemPrompt,
118
- cache_control: { type: "ephemeral" },
119
- },
120
- ],
121
- } as unknown as OpenAI.Chat.ChatCompletionMessageParam)
122
- : { role: "system" as const, content: systemPrompt };
123
-
124
- return [
125
- systemMessage,
126
- ...convertToOpenAIMessages(messages, supportsPromptCache),
127
- ];
128
- }
129
-
130
- /**
131
- * Create a streaming message
132
- */
133
- async *createMessage(
134
- systemPrompt: string,
135
- messages: Message[],
136
- tools?: ToolDefinition[],
137
- ): ApiStream {
138
- yield* retryStream(() =>
139
- this.createMessageInternal(systemPrompt, messages, tools),
140
- );
141
- }
142
-
143
- private async *createMessageInternal(
144
- systemPrompt: string,
145
- messages: Message[],
146
- tools?: ToolDefinition[],
147
- ): ApiStream {
148
- const client = this.ensureClient();
149
- const { id: modelId, info: modelInfo } = this.getModel();
150
- const responseId = this.createResponseId();
151
-
152
- // Convert messages to OpenAI format
153
- const openAiMessages = this.getMessages(systemPrompt, messages);
154
-
155
- // Build request options
156
- const requestOptions: Record<string, unknown> &
157
- OpenAI.ChatCompletionCreateParamsStreaming = {
158
- model: modelId,
159
- messages: openAiMessages,
160
- stream: true,
161
- stream_options: { include_usage: true },
162
- ...getOpenAIToolParams(tools, {
163
- // OpenRouter can reject strict function schemas on some routed models.
164
- strict: this.config.providerId !== "openrouter",
165
- }),
166
- };
167
-
168
- // Add top-level cache_control for OpenRouter with Anthropic models.
169
- // This enables automatic caching where the cache breakpoint advances
170
- // as the conversation grows, rather than relying on explicit per-block
171
- // breakpoints which are limited to 4.
172
- if (
173
- this.config.providerId === "openrouter" &&
174
- modelId.startsWith("anthropic/")
175
- ) {
176
- requestOptions.cache_control = { type: "ephemeral" };
177
- }
178
-
179
- // Add max tokens if configured
180
- const maxTokens = modelInfo.maxTokens ?? this.config.maxOutputTokens;
181
- if (maxTokens) {
182
- requestOptions.max_completion_tokens = maxTokens;
183
- }
184
-
185
- // Add temperature if not a reasoning model
186
- const modelSupportsReasoning =
187
- modelInfo.capabilities?.includes("reasoning") ?? false;
188
- if (!modelSupportsReasoning) {
189
- requestOptions.temperature = modelInfo.temperature ?? 0;
190
- }
191
-
192
- // Add reasoning effort for supported models
193
- const supportsReasoningEffort =
194
- modelInfo.capabilities?.includes("reasoning-effort") ||
195
- modelInfo.capabilities?.includes("reasoning") ||
196
- false;
197
- const effectiveReasoningEffort =
198
- this.config.reasoningEffort ??
199
- (this.config.thinking ? DEFAULT_REASONING_EFFORT : undefined);
200
- if (supportsReasoningEffort && effectiveReasoningEffort) {
201
- (
202
- requestOptions as OpenAI.ChatCompletionCreateParamsStreaming & {
203
- reasoning_effort?: string;
204
- }
205
- ).reasoning_effort = effectiveReasoningEffort;
206
- }
207
-
208
- const requestHeaders = this.getRequestHeaders();
209
- const hasAuthorizationHeader = Object.keys(requestHeaders).some(
210
- (key) => key.toLowerCase() === "authorization",
211
- );
212
- const apiKey = resolveApiKeyForProvider(
213
- this.config.providerId,
214
- this.config.apiKey,
215
- );
216
- if (!hasAuthorizationHeader && apiKey) {
217
- requestHeaders.Authorization = `Bearer ${apiKey}`;
218
- }
219
- const abortSignal = this.getAbortSignal();
220
- let stream: AsyncIterable<ChatCompletionChunk>;
221
- try {
222
- stream = await client.chat.completions.create(requestOptions, {
223
- signal: abortSignal,
224
- headers: requestHeaders,
225
- });
226
- } catch (error) {
227
- throw this.normalizeOpenAICompatibleBadRequest(error) ?? error;
228
- }
229
- const toolCallProcessor = new ToolCallProcessor();
230
- let finishReason: string | null = null;
231
-
232
- for await (const chunk of stream) {
233
- const choice = chunk.choices?.[0];
234
- if (choice?.finish_reason) {
235
- finishReason = choice.finish_reason;
236
- }
237
- yield* this.withResponseIdForAll(
238
- this.processChunk(chunk, toolCallProcessor, modelInfo, responseId),
239
- responseId,
240
- );
241
- }
242
-
243
- yield {
244
- type: "done",
245
- success: true,
246
- id: responseId,
247
- incompleteReason: finishReason === "length" ? "max_tokens" : undefined,
248
- };
249
- }
250
-
251
- /**
252
- * Process a single chunk from the stream
253
- * Can be overridden for provider-specific handling
254
- */
255
- protected *processChunk(
256
- chunk: ChatCompletionChunk,
257
- toolCallProcessor: ToolCallProcessor,
258
- _modelInfo: ModelInfo,
259
- responseId: string,
260
- ): Generator<import("../types").ApiStreamChunk> {
261
- const rawDelta = chunk.choices?.[0]?.delta;
262
- const delta = rawDelta && {
263
- ...rawDelta,
264
- reasoning_content: (rawDelta as { reasoning_content?: string })
265
- .reasoning_content,
266
- };
267
-
268
- // Handle text content
269
- if (delta?.content) {
270
- yield { type: "text", text: delta.content, id: responseId };
271
- }
272
-
273
- // Handle reasoning content (DeepSeek, xAI, etc.)
274
- if (delta?.reasoning_content) {
275
- yield {
276
- type: "reasoning",
277
- reasoning: delta.reasoning_content,
278
- id: responseId,
279
- };
280
- }
281
-
282
- // Handle tool calls
283
- if (delta?.tool_calls) {
284
- yield* toolCallProcessor.processToolCallDeltas(
285
- delta.tool_calls.map((tc) => ({
286
- index: tc.index,
287
- id: tc.id,
288
- function: tc.function,
289
- })),
290
- responseId,
291
- );
292
- }
293
-
294
- // Handle usage information
295
- if (chunk.usage) {
296
- const inputTokens = chunk.usage.prompt_tokens ?? 0;
297
- const outputTokens = chunk.usage.completion_tokens ?? 0;
298
- const usageWithCache = chunk.usage as typeof chunk.usage & {
299
- prompt_tokens_details?: {
300
- cached_tokens?: number;
301
- cache_write_tokens?: number;
302
- };
303
- cache_creation_input_tokens?: number;
304
- cache_read_input_tokens?: number;
305
- };
306
- const cacheReadTokens =
307
- usageWithCache.prompt_tokens_details?.cached_tokens ??
308
- usageWithCache.cache_read_input_tokens ??
309
- 0;
310
- const cacheWriteTokens =
311
- usageWithCache.prompt_tokens_details?.cache_write_tokens ??
312
- usageWithCache.cache_creation_input_tokens ??
313
- 0;
314
-
315
- yield {
316
- type: "usage",
317
- inputTokens: Math.max(
318
- 0,
319
- inputTokens - cacheReadTokens - cacheWriteTokens,
320
- ),
321
- outputTokens,
322
- cacheReadTokens,
323
- cacheWriteTokens,
324
- totalCost: this.calculateCostFromInclusiveInput(
325
- inputTokens,
326
- outputTokens,
327
- cacheReadTokens,
328
- cacheWriteTokens,
329
- ),
330
- id: responseId,
331
- };
332
- }
333
- }
334
- }
335
-
336
- /**
337
- * Create an OpenAI-compatible handler
338
- */
339
- export function createOpenAIHandler(config: ProviderConfig): OpenAIBaseHandler {
340
- return new OpenAIBaseHandler(config);
341
- }
@@ -1,259 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { Message } from "../types/messages";
3
- import type { ApiStreamChunk } from "../types/stream";
4
- import { OpenAIResponsesHandler } from "./openai-responses";
5
-
6
- class TestOpenAIResponsesHandler extends OpenAIResponsesHandler {
7
- private readonly functionCallMetadataByItemId = new Map<
8
- string,
9
- { callId?: string; name?: string }
10
- >();
11
-
12
- processChunkForTest(chunk: any, responseId = "resp_1"): ApiStreamChunk[] {
13
- return [
14
- ...this.processResponseChunk(
15
- chunk,
16
- { id: "gpt-5.4", capabilities: ["tools"] },
17
- responseId,
18
- this.functionCallMetadataByItemId,
19
- ),
20
- ];
21
- }
22
- }
23
-
24
- describe("OpenAIResponsesHandler", () => {
25
- it("converts tool_use/tool_result message history into Responses input items", () => {
26
- const handler = new TestOpenAIResponsesHandler({
27
- providerId: "openai-native",
28
- modelId: "gpt-5.4",
29
- apiKey: "test-key",
30
- baseUrl: "https://example.com",
31
- });
32
-
33
- const messages: Message[] = [
34
- { role: "user", content: [{ type: "text", text: "Run pwd" }] },
35
- {
36
- role: "assistant",
37
- content: [
38
- { type: "text", text: "Running command..." },
39
- {
40
- type: "tool_use",
41
- id: "fc_1",
42
- call_id: "call_1",
43
- name: "run_commands",
44
- input: { commands: ["pwd"] },
45
- },
46
- { type: "text", text: "Waiting for output" },
47
- ],
48
- },
49
- {
50
- role: "user",
51
- content: [
52
- {
53
- type: "tool_result",
54
- tool_use_id: "call_1",
55
- content: "/tmp/workspace",
56
- },
57
- { type: "text", text: "continue" },
58
- ],
59
- },
60
- ];
61
-
62
- const input = handler.getMessages("system", messages);
63
-
64
- expect(input).toEqual([
65
- {
66
- type: "message",
67
- role: "user",
68
- content: [{ type: "input_text", text: "Run pwd" }],
69
- },
70
- {
71
- type: "message",
72
- role: "assistant",
73
- content: [{ type: "output_text", text: "Running command..." }],
74
- },
75
- {
76
- type: "function_call",
77
- call_id: "call_1",
78
- name: "run_commands",
79
- arguments: '{"commands":["pwd"]}',
80
- },
81
- {
82
- type: "message",
83
- role: "assistant",
84
- content: [{ type: "output_text", text: "Waiting for output" }],
85
- },
86
- {
87
- type: "function_call_output",
88
- call_id: "call_1",
89
- output: "/tmp/workspace",
90
- },
91
- {
92
- type: "message",
93
- role: "user",
94
- content: [{ type: "input_text", text: "continue" }],
95
- },
96
- ]);
97
- });
98
-
99
- it("falls back to tool_use id when call_id is unavailable", () => {
100
- const handler = new TestOpenAIResponsesHandler({
101
- providerId: "openai-native",
102
- modelId: "gpt-5.4",
103
- apiKey: "test-key",
104
- baseUrl: "https://example.com",
105
- });
106
-
107
- const messages: Message[] = [
108
- {
109
- role: "assistant",
110
- content: [
111
- {
112
- type: "tool_use",
113
- id: "fc_123",
114
- name: "search_codebase",
115
- input: { pattern: "history" },
116
- },
117
- ],
118
- },
119
- {
120
- role: "user",
121
- content: [
122
- {
123
- type: "tool_result",
124
- tool_use_id: "fc_123",
125
- content: "found",
126
- },
127
- ],
128
- },
129
- ];
130
-
131
- const input = handler.getMessages("system", messages);
132
- expect(input).toEqual([
133
- {
134
- type: "function_call",
135
- call_id: "fc_123",
136
- name: "search_codebase",
137
- arguments: '{"pattern":"history"}',
138
- },
139
- {
140
- type: "function_call_output",
141
- call_id: "fc_123",
142
- output: "found",
143
- },
144
- ]);
145
- });
146
-
147
- it("does not map function-call item ids to tool names", () => {
148
- const handler = new TestOpenAIResponsesHandler({
149
- providerId: "openai-native",
150
- modelId: "gpt-5.4",
151
- apiKey: "test-key",
152
- baseUrl: "https://example.com",
153
- });
154
-
155
- const itemId = "fc_03aad4ff6c019bed0069ba5e9ad030819f8b2b06c5ac013811";
156
-
157
- const addedChunks = handler.processChunkForTest({
158
- type: "response.output_item.added",
159
- item: {
160
- type: "function_call",
161
- id: itemId,
162
- call_id: "call_1",
163
- name: "run_commands",
164
- arguments: "{}",
165
- },
166
- });
167
- const deltaChunks = handler.processChunkForTest({
168
- type: "response.function_call_arguments.delta",
169
- item_id: itemId,
170
- delta: '{"commands":["pwd"]',
171
- });
172
-
173
- expect(addedChunks).toHaveLength(1);
174
- expect(deltaChunks).toHaveLength(1);
175
- expect(deltaChunks[0]).toMatchObject({
176
- type: "tool_calls",
177
- tool_call: {
178
- call_id: "call_1",
179
- function: {
180
- id: itemId,
181
- name: "run_commands",
182
- },
183
- },
184
- });
185
- });
186
-
187
- it("leaves tool name undefined for argument deltas without metadata", () => {
188
- const handler = new TestOpenAIResponsesHandler({
189
- providerId: "openai-native",
190
- modelId: "gpt-5.4",
191
- apiKey: "test-key",
192
- baseUrl: "https://example.com",
193
- });
194
-
195
- const itemId = "fc_unknown";
196
- const deltaChunks = handler.processChunkForTest({
197
- type: "response.function_call_arguments.delta",
198
- item_id: itemId,
199
- delta: '{"x":1}',
200
- });
201
-
202
- expect(deltaChunks).toHaveLength(1);
203
- expect(deltaChunks[0]).toMatchObject({
204
- type: "tool_calls",
205
- tool_call: {
206
- function: {
207
- id: itemId,
208
- name: undefined,
209
- },
210
- },
211
- });
212
- });
213
-
214
- it("keeps cached input tokens separate in usage chunks", () => {
215
- const handler = new TestOpenAIResponsesHandler({
216
- providerId: "openai-native",
217
- modelId: "gpt-5.4",
218
- apiKey: "test-key",
219
- baseUrl: "https://example.com",
220
- modelInfo: {
221
- id: "gpt-5.4",
222
- pricing: {
223
- input: 1,
224
- output: 2,
225
- cacheRead: 0.5,
226
- },
227
- },
228
- });
229
-
230
- const chunks = handler.processChunkForTest({
231
- type: "response.completed",
232
- response: {
233
- id: "resp_usage",
234
- usage: {
235
- input_tokens: 100,
236
- output_tokens: 40,
237
- input_tokens_details: {
238
- cached_tokens: 25,
239
- },
240
- output_tokens_details: {
241
- reasoning_tokens: 10,
242
- },
243
- },
244
- },
245
- });
246
-
247
- expect(chunks[0]).toMatchObject({
248
- type: "usage",
249
- inputTokens: 75,
250
- outputTokens: 40,
251
- cacheReadTokens: 25,
252
- cacheWriteTokens: 0,
253
- });
254
- expect(chunks[0]?.type).toBe("usage");
255
- if (chunks[0]?.type === "usage") {
256
- expect(chunks[0].totalCost).toBeCloseTo(0.0001675, 10);
257
- }
258
- });
259
- });