@clinebot/llms 0.0.18 → 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 +3 -4
  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,290 +0,0 @@
1
- /**
2
- * OpenAI Message Format Converter
3
- *
4
- * Converts our unified Message format to OpenAI's ChatCompletionMessageParam format.
5
- */
6
-
7
- import { formatFileContentBlock } from "@clinebot/shared";
8
- import type OpenAI from "openai";
9
- import type {
10
- ContentBlock,
11
- FileContent,
12
- ImageContent,
13
- Message,
14
- TextContent,
15
- ToolResultContent,
16
- ToolUseContent,
17
- } from "../types/messages";
18
- import {
19
- normalizeToolUseInput,
20
- serializeToolResultContent,
21
- } from "./content-format";
22
-
23
- type OpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
24
- type OpenAIContentPart = OpenAI.Chat.ChatCompletionContentPart;
25
-
26
- /**
27
- * Convert messages to OpenAI format
28
- */
29
- export function convertToOpenAIMessages(
30
- messages: Message[],
31
- enableCaching = false,
32
- ): OpenAIMessage[] {
33
- const lastUserIndex = enableCaching
34
- ? messages.map((m) => m.role).lastIndexOf("user")
35
- : -1;
36
- return messages.flatMap((message, index) =>
37
- convertMessage(message, enableCaching && index === lastUserIndex),
38
- );
39
- }
40
-
41
- function convertMessage(
42
- message: Message,
43
- addCacheControl: boolean,
44
- ): OpenAIMessage[] {
45
- const { role, content } = message;
46
-
47
- // Simple string content
48
- if (typeof content === "string") {
49
- if (role !== "user" || !addCacheControl) {
50
- return [{ role, content } as OpenAIMessage];
51
- }
52
-
53
- return [
54
- {
55
- role,
56
- content: [
57
- {
58
- type: "text",
59
- text: content,
60
- cache_control: { type: "ephemeral" },
61
- },
62
- ],
63
- } as unknown as OpenAIMessage,
64
- ];
65
- }
66
-
67
- // Array content - need to process blocks
68
- if (role === "assistant") {
69
- return [convertAssistantMessage(content)];
70
- } else {
71
- return convertUserMessage(content, addCacheControl);
72
- }
73
- }
74
-
75
- function convertAssistantMessage(content: ContentBlock[]): OpenAIMessage {
76
- const textParts: string[] = [];
77
- const toolCalls: OpenAI.Chat.ChatCompletionMessageToolCall[] = [];
78
-
79
- for (const block of content) {
80
- switch (block.type) {
81
- case "text":
82
- textParts.push((block as TextContent).text);
83
- break;
84
- case "tool_use": {
85
- const toolUse = block as ToolUseContent;
86
- toolCalls.push({
87
- id: toolUse.id,
88
- type: "function",
89
- function: {
90
- name: toolUse.name,
91
- arguments: JSON.stringify(normalizeToolUseInput(toolUse.input)),
92
- },
93
- });
94
- break;
95
- }
96
- case "thinking":
97
- // OpenAI doesn't have native thinking blocks, skip
98
- break;
99
- }
100
- }
101
-
102
- const message: OpenAI.Chat.ChatCompletionAssistantMessageParam = {
103
- role: "assistant",
104
- content: textParts.length > 0 ? textParts.join("\n") : null,
105
- };
106
-
107
- if (toolCalls.length > 0) {
108
- message.tool_calls = toolCalls;
109
- }
110
-
111
- return message;
112
- }
113
-
114
- function convertUserMessage(
115
- content: ContentBlock[],
116
- addCacheControl: boolean,
117
- ): OpenAIMessage[] {
118
- const messages: OpenAIMessage[] = [];
119
-
120
- // Convert all tool results to separate tool messages
121
- const toolResults = content.filter(
122
- (b) => b.type === "tool_result",
123
- ) as ToolResultContent[];
124
- for (const result of toolResults) {
125
- messages.push({
126
- role: "tool",
127
- tool_call_id: result.tool_use_id,
128
- content: serializeToolResultContent(result.content),
129
- });
130
- }
131
-
132
- // Preserve any non-tool user content as a regular user message
133
- const userContent = content.filter((b) => b.type !== "tool_result");
134
- if (userContent.length === 0) {
135
- return messages;
136
- }
137
-
138
- const parts: OpenAIContentPart[] = [];
139
-
140
- for (const block of userContent) {
141
- switch (block.type) {
142
- case "text":
143
- parts.push({ type: "text", text: (block as TextContent).text });
144
- break;
145
- case "file": {
146
- const fileBlock = block as FileContent;
147
- parts.push({
148
- type: "text",
149
- text: formatFileContentBlock(fileBlock.path, fileBlock.content),
150
- });
151
- break;
152
- }
153
- case "image": {
154
- const img = block as ImageContent;
155
- parts.push({
156
- type: "image_url",
157
- image_url: {
158
- url: `data:${img.mediaType};base64,${img.data}`,
159
- },
160
- });
161
- break;
162
- }
163
- }
164
- }
165
- if (parts.length === 0) {
166
- return messages;
167
- }
168
-
169
- if (addCacheControl) {
170
- for (let i = parts.length - 1; i >= 0; i--) {
171
- if (parts[i].type === "text") {
172
- parts[i] = {
173
- ...(parts[i] as OpenAI.Chat.ChatCompletionContentPartText),
174
- cache_control: { type: "ephemeral" },
175
- } as unknown as OpenAIContentPart;
176
- break;
177
- }
178
- }
179
- }
180
-
181
- messages.push({
182
- role: "user",
183
- content:
184
- parts.length === 1 && parts[0].type === "text" && !addCacheControl
185
- ? parts[0].text
186
- : (parts as unknown as OpenAI.Chat.ChatCompletionUserMessageParam["content"]),
187
- });
188
-
189
- return messages;
190
- }
191
-
192
- /**
193
- * Normalize a JSON Schema for OpenAI strict mode.
194
- *
195
- * Strict mode requires:
196
- * - `additionalProperties: false` on every object
197
- * - All properties listed in `required` (optional ones become nullable)
198
- */
199
- function normalizeForStrictMode(schema: unknown): unknown {
200
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
201
- return schema;
202
- }
203
-
204
- const s = { ...(schema as Record<string, unknown>) };
205
-
206
- // Remove $schema – OpenAI rejects it
207
- delete s.$schema;
208
-
209
- if (s.type === "object") {
210
- s.additionalProperties = false;
211
-
212
- const properties = s.properties as Record<string, unknown> | undefined;
213
- const required = (s.required as string[] | undefined) ?? [];
214
-
215
- if (properties) {
216
- const allKeys = Object.keys(properties);
217
- const requiredSet = new Set(required);
218
-
219
- // Make every property required; wrap non-required ones as nullable
220
- const normalized: Record<string, unknown> = {};
221
- for (const key of allKeys) {
222
- let prop = normalizeForStrictMode(properties[key]);
223
- if (!requiredSet.has(key)) {
224
- // Wrap as nullable via anyOf
225
- prop = { anyOf: [prop, { type: "null" }] };
226
- }
227
- normalized[key] = prop;
228
- }
229
- s.properties = normalized;
230
- s.required = allKeys;
231
- }
232
- }
233
-
234
- // Recurse into nested schemas
235
- if (s.items) {
236
- s.items = Array.isArray(s.items)
237
- ? s.items.map((i) => normalizeForStrictMode(i))
238
- : normalizeForStrictMode(s.items);
239
- }
240
- for (const keyword of ["anyOf", "oneOf", "allOf"] as const) {
241
- if (Array.isArray(s[keyword])) {
242
- s[keyword] = (s[keyword] as unknown[]).map((i) =>
243
- normalizeForStrictMode(i),
244
- );
245
- }
246
- }
247
-
248
- return s;
249
- }
250
-
251
- /**
252
- * Convert tool definitions to OpenAI format
253
- */
254
- export function convertToolsToOpenAI(
255
- tools: Array<{ name: string; description: string; inputSchema: unknown }>,
256
- options?: { strict?: boolean },
257
- ): OpenAI.Chat.ChatCompletionTool[] {
258
- const strict = options?.strict ?? true;
259
- return tools.map((tool) => ({
260
- type: "function" as const,
261
- function: {
262
- name: tool.name,
263
- description: tool.description,
264
- parameters: normalizeForStrictMode(
265
- tool.inputSchema,
266
- ) as OpenAI.FunctionParameters,
267
- strict,
268
- },
269
- }));
270
- }
271
-
272
- /**
273
- * Build tool params for OpenAI request
274
- */
275
- export function getOpenAIToolParams(
276
- tools?: Array<{ name: string; description: string; inputSchema: unknown }>,
277
- options?: { strict?: boolean },
278
- ): {
279
- tools?: OpenAI.Chat.ChatCompletionTool[];
280
- tool_choice?: OpenAI.Chat.ChatCompletionToolChoiceOption;
281
- } {
282
- if (!tools || tools.length === 0) {
283
- return {};
284
- }
285
-
286
- return {
287
- tools: convertToolsToOpenAI(tools, options),
288
- tool_choice: "auto",
289
- };
290
- }
@@ -1,287 +0,0 @@
1
- /**
2
- * R1 Message Format Converter
3
- *
4
- * Handles the special message format required by DeepSeek Reasoner and other R1-based models.
5
- * Key requirements:
6
- * 1. Consecutive messages with the same role must be merged
7
- * 2. reasoning_content should be passed back during tool calling in the same turn
8
- * 3. No temperature parameter for reasoner models
9
- */
10
-
11
- import { formatFileContentBlock } from "@clinebot/shared";
12
- import type OpenAI from "openai";
13
- import type {
14
- ContentBlock,
15
- FileContent,
16
- ImageContent,
17
- Message,
18
- TextContent,
19
- ThinkingContent,
20
- ToolResultContent,
21
- ToolUseContent,
22
- } from "../types/messages";
23
- import {
24
- normalizeToolUseInput,
25
- serializeToolResultContent,
26
- } from "./content-format";
27
-
28
- type OpenAIMessage = OpenAI.Chat.ChatCompletionMessageParam;
29
- type OpenAIContentPart = OpenAI.Chat.ChatCompletionContentPart;
30
-
31
- /**
32
- * DeepSeek Reasoner message format with reasoning_content support
33
- */
34
- export type R1Message = OpenAI.Chat.ChatCompletionMessageParam & {
35
- reasoning_content?: string;
36
- };
37
-
38
- /**
39
- * Convert messages to R1 format
40
- *
41
- * This handles:
42
- * 1. Converting content blocks to OpenAI format
43
- * 2. Merging consecutive messages with the same role
44
- * 3. Adding reasoning_content for tool calling continuations
45
- */
46
- export function convertToR1Messages(messages: Message[]): R1Message[] {
47
- // First convert to OpenAI format
48
- const openAiMessages = messages.flatMap(convertMessageToOpenAI);
49
-
50
- // Then merge consecutive same-role messages
51
- const merged = mergeConsecutiveMessages(openAiMessages);
52
-
53
- // Finally add reasoning_content for current turn assistant messages
54
- return addReasoningContent(merged, messages);
55
- }
56
-
57
- /**
58
- * Convert a single message to OpenAI format (without merging)
59
- */
60
- function convertMessageToOpenAI(message: Message): OpenAIMessage[] {
61
- const { role, content } = message;
62
-
63
- // Simple string content
64
- if (typeof content === "string") {
65
- return [{ role, content } as OpenAIMessage];
66
- }
67
-
68
- // Array content - need to process blocks
69
- if (role === "assistant") {
70
- return [convertAssistantMessage(content)];
71
- } else {
72
- return convertUserMessage(content);
73
- }
74
- }
75
-
76
- function convertAssistantMessage(content: ContentBlock[]): OpenAIMessage {
77
- const textParts: string[] = [];
78
- const toolCalls: OpenAI.Chat.ChatCompletionMessageToolCall[] = [];
79
-
80
- for (const block of content) {
81
- switch (block.type) {
82
- case "text":
83
- textParts.push((block as TextContent).text);
84
- break;
85
- case "file": {
86
- const fileBlock = block as FileContent;
87
- textParts.push(
88
- formatFileContentBlock(fileBlock.path, fileBlock.content),
89
- );
90
- break;
91
- }
92
- case "tool_use": {
93
- const toolUse = block as ToolUseContent;
94
- toolCalls.push({
95
- id: toolUse.id,
96
- type: "function",
97
- function: {
98
- name: toolUse.name,
99
- arguments: JSON.stringify(normalizeToolUseInput(toolUse.input)),
100
- },
101
- });
102
- break;
103
- }
104
- case "thinking":
105
- // Thinking blocks are handled separately via reasoning_content
106
- break;
107
- }
108
- }
109
-
110
- const message: OpenAI.Chat.ChatCompletionAssistantMessageParam = {
111
- role: "assistant",
112
- content: textParts.length > 0 ? textParts.join("\n") : null,
113
- };
114
-
115
- if (toolCalls.length > 0) {
116
- message.tool_calls = toolCalls;
117
- }
118
-
119
- return message;
120
- }
121
-
122
- function convertUserMessage(content: ContentBlock[]): OpenAIMessage[] {
123
- const messages: OpenAIMessage[] = [];
124
-
125
- // Convert all tool results to separate tool messages
126
- const toolResults = content.filter(
127
- (b) => b.type === "tool_result",
128
- ) as ToolResultContent[];
129
- for (const result of toolResults) {
130
- messages.push({
131
- role: "tool",
132
- tool_call_id: result.tool_use_id,
133
- content: serializeToolResultContent(result.content),
134
- });
135
- }
136
-
137
- // Regular user message with text/images
138
- const userContent = content.filter((b) => b.type !== "tool_result");
139
- const parts: OpenAIContentPart[] = [];
140
-
141
- for (const block of userContent) {
142
- switch (block.type) {
143
- case "text":
144
- parts.push({ type: "text", text: (block as TextContent).text });
145
- break;
146
- case "image": {
147
- const img = block as ImageContent;
148
- parts.push({
149
- type: "image_url",
150
- image_url: {
151
- url: `data:${img.mediaType};base64,${img.data}`,
152
- },
153
- });
154
- break;
155
- }
156
- case "file": {
157
- const fileBlock = block as FileContent;
158
- parts.push({
159
- type: "text",
160
- text: formatFileContentBlock(fileBlock.path, fileBlock.content),
161
- });
162
- break;
163
- }
164
- }
165
- }
166
- if (parts.length === 0) {
167
- return messages;
168
- }
169
-
170
- messages.push({
171
- role: "user",
172
- content:
173
- parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts,
174
- });
175
-
176
- return messages;
177
- }
178
-
179
- /**
180
- * Merge consecutive messages with the same role
181
- *
182
- * DeepSeek Reasoner does not support successive messages with the same role,
183
- * so we need to merge them together.
184
- */
185
- function mergeConsecutiveMessages(messages: OpenAIMessage[]): OpenAIMessage[] {
186
- return messages.reduce<OpenAIMessage[]>((merged, message) => {
187
- const lastMessage = merged[merged.length - 1];
188
-
189
- // Never merge tool messages: each tool response has its own tool_call_id.
190
- if (lastMessage?.role === message.role && message.role !== "tool") {
191
- mergeMessageContent(lastMessage, message);
192
- } else {
193
- merged.push({ ...message });
194
- }
195
-
196
- return merged;
197
- }, []);
198
- }
199
-
200
- /**
201
- * Merge content from source message into target message
202
- */
203
- function mergeMessageContent(
204
- target: OpenAIMessage,
205
- source: OpenAIMessage,
206
- ): void {
207
- const targetContent = (target as any).content;
208
- const sourceContent = (source as any).content;
209
-
210
- if (typeof targetContent === "string" && typeof sourceContent === "string") {
211
- (target as any).content = `${targetContent}\n${sourceContent}`;
212
- } else {
213
- // Convert to array format and merge
214
- const targetArray = normalizeToArray(targetContent);
215
- const sourceArray = normalizeToArray(sourceContent);
216
- (target as any).content = [...targetArray, ...sourceArray];
217
- }
218
- }
219
-
220
- function normalizeToArray(
221
- content: string | null | OpenAIContentPart[],
222
- ): (
223
- | OpenAI.Chat.ChatCompletionContentPartText
224
- | OpenAI.Chat.ChatCompletionContentPartImage
225
- )[] {
226
- if (content === null || content === undefined) {
227
- return [];
228
- }
229
- if (Array.isArray(content)) {
230
- return content as (
231
- | OpenAI.Chat.ChatCompletionContentPartText
232
- | OpenAI.Chat.ChatCompletionContentPartImage
233
- )[];
234
- }
235
- return [{ type: "text" as const, text: content }];
236
- }
237
-
238
- /**
239
- * Add reasoning_content to assistant messages for DeepSeek Reasoner
240
- *
241
- * Per DeepSeek API: reasoning_content should be passed back during tool calling
242
- * in the same turn, and omitted when starting a new turn.
243
- */
244
- function addReasoningContent(
245
- openAiMessages: OpenAIMessage[],
246
- originalMessages: Message[],
247
- ): R1Message[] {
248
- // Find last user message index (start of current turn)
249
- let lastUserIndex = -1;
250
- for (let i = openAiMessages.length - 1; i >= 0; i--) {
251
- if (openAiMessages[i].role === "user") {
252
- lastUserIndex = i;
253
- break;
254
- }
255
- }
256
-
257
- // Extract thinking content from original messages, keyed by assistant message index
258
- const thinkingByIndex = new Map<number, string>();
259
- let assistantIdx = 0;
260
-
261
- for (const msg of originalMessages) {
262
- if (msg.role === "assistant") {
263
- if (Array.isArray(msg.content)) {
264
- const thinking = msg.content
265
- .filter((p): p is ThinkingContent => p.type === "thinking")
266
- .map((p) => p.thinking)
267
- .join("\n");
268
- if (thinking) {
269
- thinkingByIndex.set(assistantIdx, thinking);
270
- }
271
- }
272
- assistantIdx++;
273
- }
274
- }
275
-
276
- // Add reasoning_content only to assistant messages in current turn
277
- let aiIdx = 0;
278
- return openAiMessages.map((msg, i): R1Message => {
279
- if (msg.role === "assistant") {
280
- const thinking = thinkingByIndex.get(aiIdx++);
281
- if (thinking && i >= lastUserIndex) {
282
- return { ...msg, reasoning_content: thinking };
283
- }
284
- }
285
- return msg;
286
- });
287
- }