@clinebot/llms 0.0.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 (219) hide show
  1. package/README.md +198 -0
  2. package/dist/config-browser.d.ts +3 -0
  3. package/dist/config.d.ts +3 -0
  4. package/dist/index.browser.d.ts +4 -0
  5. package/dist/index.browser.js +1 -0
  6. package/dist/index.d.ts +5 -0
  7. package/dist/index.js +7 -0
  8. package/dist/models/generated-access.d.ts +4 -0
  9. package/dist/models/generated-provider-loaders.d.ts +13 -0
  10. package/dist/models/generated.d.ts +14 -0
  11. package/dist/models/index.d.ts +43 -0
  12. package/dist/models/models-dev-catalog.d.ts +32 -0
  13. package/dist/models/providers/aihubmix.d.ts +5 -0
  14. package/dist/models/providers/anthropic.d.ts +53 -0
  15. package/dist/models/providers/asksage.d.ts +5 -0
  16. package/dist/models/providers/baseten.d.ts +5 -0
  17. package/dist/models/providers/bedrock.d.ts +7 -0
  18. package/dist/models/providers/cerebras.d.ts +7 -0
  19. package/dist/models/providers/claude-code.d.ts +4 -0
  20. package/dist/models/providers/cline.d.ts +34 -0
  21. package/dist/models/providers/deepseek.d.ts +8 -0
  22. package/dist/models/providers/dify.d.ts +5 -0
  23. package/dist/models/providers/doubao.d.ts +7 -0
  24. package/dist/models/providers/fireworks.d.ts +8 -0
  25. package/dist/models/providers/gemini.d.ts +9 -0
  26. package/dist/models/providers/groq.d.ts +8 -0
  27. package/dist/models/providers/hicap.d.ts +5 -0
  28. package/dist/models/providers/huawei-cloud-maas.d.ts +5 -0
  29. package/dist/models/providers/huggingface.d.ts +6 -0
  30. package/dist/models/providers/index.d.ts +45 -0
  31. package/dist/models/providers/litellm.d.ts +5 -0
  32. package/dist/models/providers/lmstudio.d.ts +5 -0
  33. package/dist/models/providers/minimax.d.ts +7 -0
  34. package/dist/models/providers/mistral.d.ts +5 -0
  35. package/dist/models/providers/moonshot.d.ts +7 -0
  36. package/dist/models/providers/nebius.d.ts +7 -0
  37. package/dist/models/providers/nous-research.d.ts +7 -0
  38. package/dist/models/providers/oca.d.ts +9 -0
  39. package/dist/models/providers/ollama.d.ts +5 -0
  40. package/dist/models/providers/openai-codex.d.ts +10 -0
  41. package/dist/models/providers/openai.d.ts +9 -0
  42. package/dist/models/providers/opencode.d.ts +10 -0
  43. package/dist/models/providers/openrouter.d.ts +7 -0
  44. package/dist/models/providers/qwen-code.d.ts +7 -0
  45. package/dist/models/providers/qwen.d.ts +7 -0
  46. package/dist/models/providers/requesty.d.ts +6 -0
  47. package/dist/models/providers/sambanova.d.ts +7 -0
  48. package/dist/models/providers/sapaicore.d.ts +7 -0
  49. package/dist/models/providers/together.d.ts +8 -0
  50. package/dist/models/providers/vercel-ai-gateway.d.ts +5 -0
  51. package/dist/models/providers/vertex.d.ts +7 -0
  52. package/dist/models/providers/xai.d.ts +8 -0
  53. package/dist/models/providers/zai.d.ts +7 -0
  54. package/dist/models/query.d.ts +181 -0
  55. package/dist/models/registry.d.ts +123 -0
  56. package/dist/models/schemas/index.d.ts +7 -0
  57. package/dist/models/schemas/model.d.ts +340 -0
  58. package/dist/models/schemas/query.d.ts +191 -0
  59. package/dist/providers/handlers/ai-sdk-community.d.ts +46 -0
  60. package/dist/providers/handlers/ai-sdk-provider-base.d.ts +32 -0
  61. package/dist/providers/handlers/anthropic-base.d.ts +26 -0
  62. package/dist/providers/handlers/asksage.d.ts +12 -0
  63. package/dist/providers/handlers/auth.d.ts +5 -0
  64. package/dist/providers/handlers/base.d.ts +55 -0
  65. package/dist/providers/handlers/bedrock-base.d.ts +23 -0
  66. package/dist/providers/handlers/bedrock-client.d.ts +4 -0
  67. package/dist/providers/handlers/community-sdk.d.ts +97 -0
  68. package/dist/providers/handlers/fetch-base.d.ts +18 -0
  69. package/dist/providers/handlers/gemini-base.d.ts +25 -0
  70. package/dist/providers/handlers/index.d.ts +19 -0
  71. package/dist/providers/handlers/openai-base.d.ts +54 -0
  72. package/dist/providers/handlers/openai-responses.d.ts +64 -0
  73. package/dist/providers/handlers/providers.d.ts +43 -0
  74. package/dist/providers/handlers/r1-base.d.ts +62 -0
  75. package/dist/providers/handlers/registry.d.ts +106 -0
  76. package/dist/providers/handlers/vertex.d.ts +32 -0
  77. package/dist/providers/index.d.ts +100 -0
  78. package/dist/providers/public.browser.d.ts +2 -0
  79. package/dist/providers/public.d.ts +3 -0
  80. package/dist/providers/shared/openai-compatible.d.ts +10 -0
  81. package/dist/providers/transform/ai-sdk-community-format.d.ts +9 -0
  82. package/dist/providers/transform/anthropic-format.d.ts +24 -0
  83. package/dist/providers/transform/content-format.d.ts +3 -0
  84. package/dist/providers/transform/gemini-format.d.ts +19 -0
  85. package/dist/providers/transform/index.d.ts +10 -0
  86. package/dist/providers/transform/openai-format.d.ts +36 -0
  87. package/dist/providers/transform/r1-format.d.ts +26 -0
  88. package/dist/providers/types/config.d.ts +261 -0
  89. package/dist/providers/types/handler.d.ts +71 -0
  90. package/dist/providers/types/index.d.ts +11 -0
  91. package/dist/providers/types/messages.d.ts +139 -0
  92. package/dist/providers/types/model-info.d.ts +32 -0
  93. package/dist/providers/types/provider-ids.d.ts +63 -0
  94. package/dist/providers/types/settings.d.ts +308 -0
  95. package/dist/providers/types/stream.d.ts +106 -0
  96. package/dist/providers/utils/index.d.ts +7 -0
  97. package/dist/providers/utils/retry.d.ts +38 -0
  98. package/dist/providers/utils/stream-processor.d.ts +110 -0
  99. package/dist/providers/utils/tool-processor.d.ts +34 -0
  100. package/dist/sdk.d.ts +18 -0
  101. package/dist/types.d.ts +60 -0
  102. package/package.json +66 -0
  103. package/src/catalog.ts +20 -0
  104. package/src/config-browser.ts +11 -0
  105. package/src/config.ts +49 -0
  106. package/src/index.browser.ts +9 -0
  107. package/src/index.ts +10 -0
  108. package/src/live-providers.test.ts +137 -0
  109. package/src/models/generated-access.ts +41 -0
  110. package/src/models/generated-provider-loaders.ts +166 -0
  111. package/src/models/generated.ts +11997 -0
  112. package/src/models/index.ts +271 -0
  113. package/src/models/models-dev-catalog.test.ts +161 -0
  114. package/src/models/models-dev-catalog.ts +161 -0
  115. package/src/models/providers/aihubmix.ts +19 -0
  116. package/src/models/providers/anthropic.ts +60 -0
  117. package/src/models/providers/asksage.ts +19 -0
  118. package/src/models/providers/baseten.ts +21 -0
  119. package/src/models/providers/bedrock.ts +30 -0
  120. package/src/models/providers/cerebras.ts +24 -0
  121. package/src/models/providers/claude-code.ts +51 -0
  122. package/src/models/providers/cline.ts +25 -0
  123. package/src/models/providers/deepseek.ts +33 -0
  124. package/src/models/providers/dify.ts +17 -0
  125. package/src/models/providers/doubao.ts +33 -0
  126. package/src/models/providers/fireworks.ts +34 -0
  127. package/src/models/providers/gemini.ts +43 -0
  128. package/src/models/providers/groq.ts +33 -0
  129. package/src/models/providers/hicap.ts +18 -0
  130. package/src/models/providers/huawei-cloud-maas.ts +18 -0
  131. package/src/models/providers/huggingface.ts +22 -0
  132. package/src/models/providers/index.ts +162 -0
  133. package/src/models/providers/litellm.ts +19 -0
  134. package/src/models/providers/lmstudio.ts +22 -0
  135. package/src/models/providers/minimax.ts +34 -0
  136. package/src/models/providers/mistral.ts +19 -0
  137. package/src/models/providers/moonshot.ts +34 -0
  138. package/src/models/providers/nebius.ts +24 -0
  139. package/src/models/providers/nous-research.ts +21 -0
  140. package/src/models/providers/oca.ts +30 -0
  141. package/src/models/providers/ollama.ts +18 -0
  142. package/src/models/providers/openai-codex.ts +30 -0
  143. package/src/models/providers/openai.ts +43 -0
  144. package/src/models/providers/opencode.ts +28 -0
  145. package/src/models/providers/openrouter.ts +24 -0
  146. package/src/models/providers/qwen-code.ts +33 -0
  147. package/src/models/providers/qwen.ts +34 -0
  148. package/src/models/providers/requesty.ts +23 -0
  149. package/src/models/providers/sambanova.ts +23 -0
  150. package/src/models/providers/sapaicore.ts +34 -0
  151. package/src/models/providers/together.ts +35 -0
  152. package/src/models/providers/vercel-ai-gateway.ts +23 -0
  153. package/src/models/providers/vertex.ts +36 -0
  154. package/src/models/providers/xai.ts +34 -0
  155. package/src/models/providers/zai.ts +25 -0
  156. package/src/models/query.ts +407 -0
  157. package/src/models/registry.ts +511 -0
  158. package/src/models/schemas/index.ts +62 -0
  159. package/src/models/schemas/model.ts +308 -0
  160. package/src/models/schemas/query.ts +336 -0
  161. package/src/providers/browser.ts +4 -0
  162. package/src/providers/handlers/ai-sdk-community.ts +226 -0
  163. package/src/providers/handlers/ai-sdk-provider-base.ts +193 -0
  164. package/src/providers/handlers/anthropic-base.ts +372 -0
  165. package/src/providers/handlers/asksage.test.ts +103 -0
  166. package/src/providers/handlers/asksage.ts +138 -0
  167. package/src/providers/handlers/auth.test.ts +19 -0
  168. package/src/providers/handlers/auth.ts +121 -0
  169. package/src/providers/handlers/base.test.ts +46 -0
  170. package/src/providers/handlers/base.ts +160 -0
  171. package/src/providers/handlers/bedrock-base.ts +390 -0
  172. package/src/providers/handlers/bedrock-client.ts +100 -0
  173. package/src/providers/handlers/codex.test.ts +123 -0
  174. package/src/providers/handlers/community-sdk.test.ts +288 -0
  175. package/src/providers/handlers/community-sdk.ts +392 -0
  176. package/src/providers/handlers/fetch-base.ts +68 -0
  177. package/src/providers/handlers/gemini-base.ts +302 -0
  178. package/src/providers/handlers/index.ts +67 -0
  179. package/src/providers/handlers/openai-base.ts +277 -0
  180. package/src/providers/handlers/openai-responses.ts +598 -0
  181. package/src/providers/handlers/providers.test.ts +120 -0
  182. package/src/providers/handlers/providers.ts +563 -0
  183. package/src/providers/handlers/r1-base.ts +280 -0
  184. package/src/providers/handlers/registry.ts +185 -0
  185. package/src/providers/handlers/vertex.test.ts +124 -0
  186. package/src/providers/handlers/vertex.ts +292 -0
  187. package/src/providers/index.ts +534 -0
  188. package/src/providers/public.browser.ts +20 -0
  189. package/src/providers/public.ts +51 -0
  190. package/src/providers/shared/openai-compatible.ts +63 -0
  191. package/src/providers/transform/ai-sdk-community-format.test.ts +73 -0
  192. package/src/providers/transform/ai-sdk-community-format.ts +115 -0
  193. package/src/providers/transform/anthropic-format.ts +218 -0
  194. package/src/providers/transform/content-format.ts +34 -0
  195. package/src/providers/transform/format-conversion.test.ts +310 -0
  196. package/src/providers/transform/gemini-format.ts +167 -0
  197. package/src/providers/transform/index.ts +22 -0
  198. package/src/providers/transform/openai-format.ts +247 -0
  199. package/src/providers/transform/r1-format.ts +287 -0
  200. package/src/providers/types/config.ts +388 -0
  201. package/src/providers/types/handler.ts +87 -0
  202. package/src/providers/types/index.ts +120 -0
  203. package/src/providers/types/messages.ts +158 -0
  204. package/src/providers/types/model-info.test.ts +57 -0
  205. package/src/providers/types/model-info.ts +65 -0
  206. package/src/providers/types/provider-ids.test.ts +12 -0
  207. package/src/providers/types/provider-ids.ts +89 -0
  208. package/src/providers/types/settings.test.ts +49 -0
  209. package/src/providers/types/settings.ts +533 -0
  210. package/src/providers/types/stream.ts +117 -0
  211. package/src/providers/utils/index.ts +27 -0
  212. package/src/providers/utils/retry.test.ts +140 -0
  213. package/src/providers/utils/retry.ts +188 -0
  214. package/src/providers/utils/stream-processor.test.ts +232 -0
  215. package/src/providers/utils/stream-processor.ts +472 -0
  216. package/src/providers/utils/tool-processor.test.ts +34 -0
  217. package/src/providers/utils/tool-processor.ts +111 -0
  218. package/src/sdk.ts +264 -0
  219. package/src/types.ts +79 -0
@@ -0,0 +1,310 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { Message } from "../types/messages";
3
+ import {
4
+ convertToAnthropicMessages,
5
+ convertToGeminiMessages,
6
+ convertToOpenAIMessages,
7
+ convertToolsToAnthropic,
8
+ convertToolsToGemini,
9
+ convertToolsToOpenAI,
10
+ convertToR1Messages,
11
+ } from "./index";
12
+
13
+ describe("format conversion", () => {
14
+ it("converts file content blocks to text for user and tool_result payloads", () => {
15
+ const fileText =
16
+ '<file_content path="/repo/README.md">\nhello from file\n</file_content>';
17
+ const messages: Message[] = [
18
+ {
19
+ role: "user",
20
+ content: [
21
+ { type: "file", path: "/repo/README.md", content: "hello from file" },
22
+ ],
23
+ },
24
+ {
25
+ role: "assistant",
26
+ content: [
27
+ {
28
+ type: "tool_use",
29
+ id: "call_1",
30
+ name: "read_file",
31
+ input: { path: "/repo/README.md" },
32
+ },
33
+ ],
34
+ },
35
+ {
36
+ role: "user",
37
+ content: [
38
+ {
39
+ type: "tool_result",
40
+ tool_use_id: "call_1",
41
+ content: [
42
+ {
43
+ type: "file",
44
+ path: "/repo/README.md",
45
+ content: "hello from file",
46
+ },
47
+ ],
48
+ },
49
+ ],
50
+ },
51
+ ];
52
+
53
+ const openai = convertToOpenAIMessages(messages) as any[];
54
+ expect(openai[0]).toMatchObject({ role: "user", content: fileText });
55
+ expect(openai[2]).toMatchObject({ role: "tool", content: fileText });
56
+
57
+ const gemini = convertToGeminiMessages(messages) as any[];
58
+ expect(gemini[0]?.parts?.[0]?.text).toBe(fileText);
59
+ expect(gemini[2]?.parts?.[0]?.functionResponse?.response?.result).toBe(
60
+ fileText,
61
+ );
62
+
63
+ const anthropic = convertToAnthropicMessages(messages) as any[];
64
+ expect(anthropic[0]?.content?.[0]).toMatchObject({
65
+ type: "text",
66
+ text: fileText,
67
+ });
68
+ expect(anthropic[2]?.content?.[0]).toMatchObject({
69
+ type: "tool_result",
70
+ content: [{ type: "text", text: fileText }],
71
+ });
72
+
73
+ const r1 = convertToR1Messages(messages) as any[];
74
+ expect(r1[0]).toMatchObject({ role: "user", content: fileText });
75
+ expect(r1[2]).toMatchObject({ role: "tool", content: fileText });
76
+ });
77
+
78
+ it("replays gemini thought signatures on text/tool_use/thinking parts", () => {
79
+ const messages: Message[] = [
80
+ { role: "user", content: "start" },
81
+ {
82
+ role: "assistant",
83
+ content: [
84
+ {
85
+ type: "tool_use",
86
+ id: "call_1",
87
+ name: "run_commands",
88
+ input: { command: "echo hi" },
89
+ signature: "sig-a",
90
+ },
91
+ { type: "thinking", thinking: "need to run", signature: "sig-think" },
92
+ { type: "text", text: "done", signature: "sig-text" },
93
+ ],
94
+ },
95
+ {
96
+ role: "user",
97
+ content: [
98
+ { type: "tool_result", tool_use_id: "run_commands", content: "ok" },
99
+ ],
100
+ },
101
+ ];
102
+
103
+ const gemini = convertToGeminiMessages(messages);
104
+ const assistant = gemini[1] as any;
105
+ expect(assistant.role).toBe("model");
106
+ expect(assistant.parts[0].functionCall.name).toBe("run_commands");
107
+ expect(assistant.parts[0].thoughtSignature).toBe("sig-a");
108
+ expect(assistant.parts[1].thought).toBe(true);
109
+ expect(assistant.parts[1].thoughtSignature).toBe("sig-think");
110
+ expect(assistant.parts[2].text).toBe("done");
111
+ expect(assistant.parts[2].thoughtSignature).toBe("sig-text");
112
+ });
113
+
114
+ it("converts multiple tool_result blocks for openai without dropping any", () => {
115
+ const messages: Message[] = [
116
+ { role: "user", content: "check both" },
117
+ {
118
+ role: "assistant",
119
+ content: [
120
+ {
121
+ type: "tool_use",
122
+ id: "call_1",
123
+ name: "get_weather",
124
+ input: { city: "Paris" },
125
+ signature: "sig1",
126
+ },
127
+ {
128
+ type: "tool_use",
129
+ id: "call_2",
130
+ name: "get_weather",
131
+ input: { city: "London" },
132
+ },
133
+ ],
134
+ },
135
+ {
136
+ role: "user",
137
+ content: [
138
+ {
139
+ type: "tool_result",
140
+ tool_use_id: "call_1",
141
+ content: '{"temp":"15C"}',
142
+ },
143
+ {
144
+ type: "tool_result",
145
+ tool_use_id: "call_2",
146
+ content: '{"temp":"12C"}',
147
+ },
148
+ { type: "text", text: "summarize now" },
149
+ ],
150
+ },
151
+ ];
152
+
153
+ const openai = convertToOpenAIMessages(messages) as any[];
154
+ expect(openai).toHaveLength(5);
155
+ expect(openai[1].role).toBe("assistant");
156
+ expect(openai[1].tool_calls).toHaveLength(2);
157
+ expect(openai[1].tool_calls[0].function.name).toBe("get_weather");
158
+ expect(openai[2]).toMatchObject({ role: "tool", tool_call_id: "call_1" });
159
+ expect(openai[3]).toMatchObject({ role: "tool", tool_call_id: "call_2" });
160
+ expect(openai[4]).toMatchObject({ role: "user", content: "summarize now" });
161
+ // Ensure Gemini-specific signature metadata does not leak into OpenAI messages.
162
+ expect(openai[1].tool_calls[0].extra_content).toBeUndefined();
163
+ });
164
+
165
+ it("normalizes array-shaped tool_use input for openai replay", () => {
166
+ const messages: Message[] = [
167
+ { role: "user", content: "run these" },
168
+ {
169
+ role: "assistant",
170
+ content: [
171
+ {
172
+ type: "tool_use",
173
+ id: "call_1",
174
+ name: "run_commands",
175
+ input: ["ls -la", "pwd"] as unknown as Record<string, unknown>,
176
+ },
177
+ ],
178
+ },
179
+ ];
180
+
181
+ const openai = convertToOpenAIMessages(messages) as any[];
182
+ expect(openai[1]?.tool_calls?.[0]).toMatchObject({
183
+ id: "call_1",
184
+ type: "function",
185
+ function: {
186
+ name: "run_commands",
187
+ arguments: JSON.stringify({ commands: ["ls -la", "pwd"] }),
188
+ },
189
+ });
190
+ });
191
+
192
+ it("keeps anthropic thinking signature and cache marker behavior", () => {
193
+ const messages: Message[] = [
194
+ { role: "user", content: "hello" },
195
+ {
196
+ role: "assistant",
197
+ content: [
198
+ {
199
+ type: "thinking",
200
+ thinking: "reasoning",
201
+ signature: "anthropic-sig",
202
+ },
203
+ ],
204
+ },
205
+ ];
206
+
207
+ const anthropic = convertToAnthropicMessages(messages, true) as any[];
208
+ expect(anthropic[1].content[0].type).toBe("thinking");
209
+ expect(anthropic[1].content[0].signature).toBe("anthropic-sig");
210
+ });
211
+
212
+ it("normalizes array-shaped tool_use input for anthropic replay", () => {
213
+ const messages: Message[] = [
214
+ { role: "user", content: "run these" },
215
+ {
216
+ role: "assistant",
217
+ content: [
218
+ {
219
+ type: "tool_use",
220
+ id: "call_1",
221
+ name: "run_commands",
222
+ input: ["ls -la", "pwd"] as unknown as Record<string, unknown>,
223
+ },
224
+ ],
225
+ },
226
+ ];
227
+
228
+ const anthropic = convertToAnthropicMessages(messages) as any[];
229
+ expect(anthropic[1]?.content?.[0]).toMatchObject({
230
+ type: "tool_use",
231
+ id: "call_1",
232
+ name: "run_commands",
233
+ input: {
234
+ commands: ["ls -la", "pwd"],
235
+ },
236
+ });
237
+ });
238
+
239
+ it("handles R1 interchange with tool results and reasoning_content", () => {
240
+ const messages: Message[] = [
241
+ { role: "user", content: "check weather" },
242
+ {
243
+ role: "assistant",
244
+ content: [
245
+ { type: "thinking", thinking: "need both cities" },
246
+ {
247
+ type: "tool_use",
248
+ id: "call_1",
249
+ name: "get_weather",
250
+ input: { city: "Paris" },
251
+ },
252
+ {
253
+ type: "tool_use",
254
+ id: "call_2",
255
+ name: "get_weather",
256
+ input: { city: "London" },
257
+ },
258
+ ],
259
+ },
260
+ {
261
+ role: "user",
262
+ content: [
263
+ {
264
+ type: "tool_result",
265
+ tool_use_id: "call_1",
266
+ content: '{"temp":"15C"}',
267
+ },
268
+ {
269
+ type: "tool_result",
270
+ tool_use_id: "call_2",
271
+ content: '{"temp":"12C"}',
272
+ },
273
+ ],
274
+ },
275
+ ];
276
+
277
+ const r1 = convertToR1Messages(messages) as any[];
278
+ expect(r1[1].role).toBe("assistant");
279
+ expect(r1[1].tool_calls).toHaveLength(2);
280
+ expect(r1[1].reasoning_content).toBe("need both cities");
281
+ expect(r1[2]).toMatchObject({ role: "tool", tool_call_id: "call_1" });
282
+ expect(r1[3]).toMatchObject({ role: "tool", tool_call_id: "call_2" });
283
+ });
284
+
285
+ it("converts tools for all providers", () => {
286
+ const tools = [
287
+ {
288
+ name: "read_file",
289
+ description: "Read file",
290
+ inputSchema: {
291
+ type: "object",
292
+ properties: { path: { type: "string" } },
293
+ },
294
+ },
295
+ ];
296
+
297
+ expect(convertToolsToOpenAI(tools)[0]).toMatchObject({
298
+ type: "function",
299
+ function: { name: "read_file", strict: true },
300
+ });
301
+ expect(convertToolsToOpenAI(tools, { strict: false })[0]).toMatchObject({
302
+ type: "function",
303
+ function: { name: "read_file", strict: false },
304
+ });
305
+ expect(convertToolsToAnthropic(tools)[0]).toMatchObject({
306
+ name: "read_file",
307
+ });
308
+ expect(convertToolsToGemini(tools)[0]).toMatchObject({ name: "read_file" });
309
+ });
310
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Gemini Message Format Converter
3
+ *
4
+ * Converts our unified Message format to Google Gemini's Content format.
5
+ */
6
+
7
+ import { formatFileContentBlock } from "@clinebot/shared";
8
+ import type { Content, FunctionDeclaration, Part } from "@google/genai";
9
+ import type {
10
+ ContentBlock,
11
+ FileContent,
12
+ ImageContent,
13
+ Message,
14
+ TextContent,
15
+ ThinkingContent,
16
+ ToolResultContent,
17
+ ToolUseContent,
18
+ } from "../types/messages";
19
+ import {
20
+ normalizeToolUseInput,
21
+ serializeToolResultContent,
22
+ } from "./content-format";
23
+
24
+ /**
25
+ * Convert messages to Gemini format
26
+ */
27
+ export function convertToGeminiMessages(messages: Message[]): Content[] {
28
+ return messages.map(convertMessage).filter((m): m is Content => m !== null);
29
+ }
30
+
31
+ function convertMessage(message: Message): Content | null {
32
+ const { role, content } = message;
33
+
34
+ // Map roles: Gemini uses "user" and "model"
35
+ const geminiRole = role === "assistant" ? "model" : "user";
36
+
37
+ // Simple string content
38
+ if (typeof content === "string") {
39
+ return {
40
+ role: geminiRole,
41
+ parts: [{ text: content }],
42
+ };
43
+ }
44
+
45
+ // Array content
46
+ const parts = convertContentBlocks(content);
47
+ if (parts.length === 0) {
48
+ return null;
49
+ }
50
+
51
+ return {
52
+ role: geminiRole,
53
+ parts,
54
+ };
55
+ }
56
+
57
+ function convertContentBlocks(content: ContentBlock[]): Part[] {
58
+ const parts: Part[] = [];
59
+
60
+ for (const block of content) {
61
+ const converted = convertContentBlock(block);
62
+ if (converted) {
63
+ parts.push(converted);
64
+ }
65
+ }
66
+
67
+ return parts;
68
+ }
69
+
70
+ function convertContentBlock(block: ContentBlock): Part | null {
71
+ switch (block.type) {
72
+ case "text": {
73
+ const textBlock = block as TextContent;
74
+ const part: Part = { text: textBlock.text };
75
+ if (textBlock.signature) {
76
+ (part as any).thoughtSignature = textBlock.signature;
77
+ }
78
+ return part;
79
+ }
80
+
81
+ case "file": {
82
+ const fileBlock = block as FileContent;
83
+ const part: Part = {
84
+ text: formatFileContentBlock(fileBlock.path, fileBlock.content),
85
+ };
86
+
87
+ return part;
88
+ }
89
+
90
+ case "image": {
91
+ const imageBlock = block as ImageContent;
92
+ return {
93
+ inlineData: {
94
+ mimeType: imageBlock.mediaType,
95
+ data: imageBlock.data,
96
+ },
97
+ };
98
+ }
99
+
100
+ case "tool_use": {
101
+ const toolBlock = block as ToolUseContent;
102
+ const part: Part = {
103
+ functionCall: {
104
+ name: toolBlock.name,
105
+ args: normalizeToolUseInput(toolBlock.input),
106
+ },
107
+ };
108
+ if (toolBlock.signature) {
109
+ (part as any).thoughtSignature = toolBlock.signature;
110
+ }
111
+ return part;
112
+ }
113
+
114
+ case "tool_result": {
115
+ const resultBlock = block as ToolResultContent;
116
+ let responseContent: Record<string, unknown>;
117
+
118
+ if (typeof resultBlock.content === "string") {
119
+ responseContent = { result: resultBlock.content };
120
+ } else {
121
+ responseContent = {
122
+ result: serializeToolResultContent(resultBlock.content),
123
+ };
124
+ }
125
+
126
+ if (resultBlock.is_error) {
127
+ responseContent.error = true;
128
+ }
129
+
130
+ return {
131
+ functionResponse: {
132
+ name: resultBlock.tool_use_id, // Gemini uses the function name here
133
+ response: responseContent,
134
+ },
135
+ };
136
+ }
137
+
138
+ case "thinking": {
139
+ const thinkingBlock = block as ThinkingContent;
140
+ // Gemini uses thought: true to mark thinking blocks
141
+ const part = {
142
+ text: thinkingBlock.thinking,
143
+ thought: true,
144
+ } as Part;
145
+ if (thinkingBlock.signature) {
146
+ (part as any).thoughtSignature = thinkingBlock.signature;
147
+ }
148
+ return part;
149
+ }
150
+
151
+ default:
152
+ return null;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Convert tool definitions to Gemini format
158
+ */
159
+ export function convertToolsToGemini(
160
+ tools: Array<{ name: string; description: string; inputSchema: unknown }>,
161
+ ): FunctionDeclaration[] {
162
+ return tools.map((tool) => ({
163
+ name: tool.name,
164
+ description: tool.description,
165
+ parameters: tool.inputSchema as FunctionDeclaration["parameters"],
166
+ }));
167
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Transform Module Index
3
+ *
4
+ * Re-exports all message format converters.
5
+ */
6
+
7
+ export {
8
+ type AiSdkMessage,
9
+ type AiSdkMessagePart,
10
+ toAiSdkMessages,
11
+ } from "./ai-sdk-community-format";
12
+ export {
13
+ convertToAnthropicMessages,
14
+ convertToolsToAnthropic,
15
+ } from "./anthropic-format";
16
+ export { convertToGeminiMessages, convertToolsToGemini } from "./gemini-format";
17
+ export {
18
+ convertToOpenAIMessages,
19
+ convertToolsToOpenAI,
20
+ getOpenAIToolParams,
21
+ } from "./openai-format";
22
+ export { convertToR1Messages, type R1Message } from "./r1-format";