@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,68 +0,0 @@
1
- import type { ApiStream } from "../types";
2
- import type { Message, ToolDefinition } from "../types/messages";
3
- import { retryStream } from "../utils/retry";
4
- import { BaseHandler } from "./base";
5
-
6
- type JsonRecord = Record<string, unknown>;
7
-
8
- export abstract class FetchBaseHandler extends BaseHandler {
9
- protected abstract getDefaultBaseUrl(): string;
10
-
11
- getMessages(systemPrompt: string, messages: Message[]): unknown {
12
- return {
13
- systemPrompt,
14
- messages,
15
- };
16
- }
17
-
18
- protected getBaseUrl(): string {
19
- return this.config.baseUrl?.trim() || this.getDefaultBaseUrl();
20
- }
21
-
22
- protected getJsonHeaders(
23
- extra?: Record<string, string>,
24
- ): Record<string, string> {
25
- return {
26
- "Content-Type": "application/json",
27
- ...this.getRequestHeaders(),
28
- ...(extra ?? {}),
29
- };
30
- }
31
-
32
- protected async fetchJson<T>(
33
- path: string,
34
- init: {
35
- method?: string;
36
- body?: JsonRecord;
37
- headers?: Record<string, string>;
38
- },
39
- ): Promise<T> {
40
- const response = await fetch(`${this.getBaseUrl()}${path}`, {
41
- method: init.method ?? "POST",
42
- headers: this.getJsonHeaders(init.headers),
43
- body: init.body ? JSON.stringify(init.body) : undefined,
44
- signal: this.getAbortSignal(),
45
- });
46
- if (!response.ok) {
47
- const details = await response.text();
48
- throw new Error(`HTTP ${response.status}: ${details}`);
49
- }
50
- return (await response.json()) as T;
51
- }
52
-
53
- async *createMessage(
54
- systemPrompt: string,
55
- messages: Message[],
56
- tools?: ToolDefinition[],
57
- ): ApiStream {
58
- void tools;
59
- yield* retryStream(() =>
60
- this.createMessageWithFetch(systemPrompt, messages),
61
- );
62
- }
63
-
64
- protected abstract createMessageWithFetch(
65
- systemPrompt: string,
66
- messages: Message[],
67
- ): ApiStream;
68
- }
@@ -1,261 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import type { ToolDefinition } from "../types/messages";
3
- import type { ApiStreamChunk } from "../types/stream";
4
-
5
- const generateContentStreamSpy = vi.fn();
6
- const googleGenAIConstructorSpy = vi.fn();
7
-
8
- vi.mock("@google/genai", () => {
9
- class GoogleGenAI {
10
- models = {
11
- generateContentStream: generateContentStreamSpy,
12
- };
13
-
14
- constructor(config: unknown) {
15
- googleGenAIConstructorSpy(config);
16
- }
17
- }
18
-
19
- return {
20
- GoogleGenAI,
21
- FunctionCallingConfigMode: { AUTO: "AUTO" },
22
- ThinkingLevel: { HIGH: "HIGH", LOW: "LOW" },
23
- };
24
- });
25
-
26
- import { GeminiHandler } from "./gemini-base";
27
-
28
- function createAsyncIterable<T>(items: T[]): AsyncIterable<T> {
29
- return {
30
- async *[Symbol.asyncIterator]() {
31
- for (const item of items) {
32
- yield item;
33
- }
34
- },
35
- };
36
- }
37
-
38
- async function collectChunks(stream: AsyncIterable<ApiStreamChunk>) {
39
- const chunks: ApiStreamChunk[] = [];
40
- for await (const chunk of stream) {
41
- chunks.push(chunk);
42
- }
43
- return chunks;
44
- }
45
-
46
- describe("GeminiHandler", () => {
47
- beforeEach(() => {
48
- vi.clearAllMocks();
49
- });
50
-
51
- it("preserves per-call ids for parallel function calls and keeps falsy args", async () => {
52
- generateContentStreamSpy.mockResolvedValue(
53
- createAsyncIterable([
54
- {
55
- candidates: [
56
- {
57
- content: {
58
- parts: [
59
- {
60
- functionCall: {
61
- id: "call_a",
62
- name: "power_disco_ball",
63
- args: { power: false },
64
- },
65
- },
66
- {
67
- functionCall: {
68
- id: "call_b",
69
- name: "dim_lights",
70
- args: { brightness: 0 },
71
- },
72
- },
73
- ],
74
- },
75
- },
76
- ],
77
- usageMetadata: {
78
- promptTokenCount: 10,
79
- candidatesTokenCount: 4,
80
- },
81
- },
82
- ]),
83
- );
84
-
85
- const handler = new GeminiHandler({
86
- providerId: "gemini",
87
- modelId: "gemini-2.5-flash",
88
- apiKey: "test-key",
89
- modelInfo: {
90
- id: "gemini-2.5-flash",
91
- contextWindow: 1_000_000,
92
- maxTokens: 8192,
93
- temperature: 1,
94
- },
95
- });
96
-
97
- const tools: ToolDefinition[] = [
98
- {
99
- name: "power_disco_ball",
100
- description: "toggle disco ball power",
101
- inputSchema: {
102
- type: "object",
103
- properties: { power: { type: "boolean" } },
104
- required: ["power"],
105
- },
106
- },
107
- {
108
- name: "dim_lights",
109
- description: "set light brightness",
110
- inputSchema: {
111
- type: "object",
112
- properties: { brightness: { type: "number" } },
113
- required: ["brightness"],
114
- },
115
- },
116
- ];
117
-
118
- const chunks = await collectChunks(
119
- handler.createMessage(
120
- "You are helpful.",
121
- [{ role: "user", content: "start" }],
122
- tools,
123
- ),
124
- );
125
-
126
- const toolChunks = chunks.filter((chunk) => chunk.type === "tool_calls");
127
- expect(toolChunks).toHaveLength(2);
128
- expect(toolChunks[0]).toMatchObject({
129
- tool_call: {
130
- call_id: "call_a",
131
- function: {
132
- id: "call_a",
133
- name: "power_disco_ball",
134
- arguments: { power: false },
135
- },
136
- },
137
- });
138
- expect(toolChunks[1]).toMatchObject({
139
- tool_call: {
140
- call_id: "call_b",
141
- function: {
142
- id: "call_b",
143
- name: "dim_lights",
144
- arguments: { brightness: 0 },
145
- },
146
- },
147
- });
148
- });
149
-
150
- it("generates distinct fallback ids when Gemini omits functionCall.id", async () => {
151
- generateContentStreamSpy.mockResolvedValue(
152
- createAsyncIterable([
153
- {
154
- candidates: [
155
- {
156
- content: {
157
- parts: [
158
- {
159
- functionCall: {
160
- name: "read_file",
161
- args: { path: "a.ts" },
162
- },
163
- },
164
- {
165
- functionCall: {
166
- name: "search_files",
167
- args: { query: "TODO" },
168
- },
169
- },
170
- ],
171
- },
172
- },
173
- ],
174
- usageMetadata: {
175
- promptTokenCount: 5,
176
- candidatesTokenCount: 3,
177
- },
178
- },
179
- ]),
180
- );
181
-
182
- const handler = new GeminiHandler({
183
- providerId: "gemini",
184
- modelId: "gemini-2.5-flash",
185
- apiKey: "test-key",
186
- });
187
-
188
- const chunks = await collectChunks(
189
- handler.createMessage(
190
- "System",
191
- [{ role: "user", content: "go" }],
192
- [
193
- {
194
- name: "read_file",
195
- description: "read file",
196
- inputSchema: {
197
- type: "object",
198
- properties: { path: { type: "string" } },
199
- },
200
- },
201
- {
202
- name: "search_files",
203
- description: "search files",
204
- inputSchema: {
205
- type: "object",
206
- properties: { query: { type: "string" } },
207
- },
208
- },
209
- ],
210
- ),
211
- );
212
-
213
- const toolChunks = chunks.filter((chunk) => chunk.type === "tool_calls");
214
- expect(toolChunks).toHaveLength(2);
215
- const firstId = toolChunks[0].tool_call.call_id;
216
- const secondId = toolChunks[1].tool_call.call_id;
217
- expect(firstId).toBeTruthy();
218
- expect(secondId).toBeTruthy();
219
- expect(firstId).not.toBe(secondId);
220
- });
221
-
222
- it("defaults maxOutputTokens to 8192 for gemini-3-flash when no model or config limit is provided", async () => {
223
- generateContentStreamSpy.mockResolvedValue(createAsyncIterable([]));
224
-
225
- const handler = new GeminiHandler({
226
- providerId: "gemini",
227
- modelId: "gemini-3-flash",
228
- apiKey: "test-key",
229
- });
230
-
231
- await collectChunks(
232
- handler.createMessage("System", [{ role: "user", content: "go" }]),
233
- );
234
-
235
- expect(generateContentStreamSpy).toHaveBeenCalledTimes(1);
236
- const request = generateContentStreamSpy.mock.calls[0]?.[0] as {
237
- config?: { maxOutputTokens?: number };
238
- };
239
- expect(request.config?.maxOutputTokens).toBe(8192);
240
- });
241
-
242
- it("defaults maxOutputTokens to 128000 for non gemini-3-flash models when no model or config limit is provided", async () => {
243
- generateContentStreamSpy.mockResolvedValue(createAsyncIterable([]));
244
-
245
- const handler = new GeminiHandler({
246
- providerId: "gemini",
247
- modelId: "gemini-2.5-flash",
248
- apiKey: "test-key",
249
- });
250
-
251
- await collectChunks(
252
- handler.createMessage("System", [{ role: "user", content: "go" }]),
253
- );
254
-
255
- expect(generateContentStreamSpy).toHaveBeenCalledTimes(1);
256
- const request = generateContentStreamSpy.mock.calls[0]?.[0] as {
257
- config?: { maxOutputTokens?: number };
258
- };
259
- expect(request.config?.maxOutputTokens).toBe(128000);
260
- });
261
- });
@@ -1,307 +0,0 @@
1
- /**
2
- * Gemini Base Handler
3
- *
4
- * Handler for Google's Gemini API using the official SDK.
5
- * Supports Vertex AI, thinking/reasoning, and native tool calling.
6
- */
7
-
8
- import {
9
- FunctionCallingConfigMode,
10
- type GenerateContentConfig,
11
- GoogleGenAI,
12
- ThinkingLevel,
13
- } from "@google/genai";
14
- import {
15
- convertToGeminiMessages,
16
- convertToolsToGemini,
17
- } from "../transform/gemini-format";
18
- import {
19
- type ApiStream,
20
- type HandlerModelInfo,
21
- type ProviderConfig,
22
- supportsModelThinking,
23
- } from "../types";
24
- import type { Message, ToolDefinition } from "../types/messages";
25
- import { RetriableError, retryStream } from "../utils/retry";
26
- import { BaseHandler } from "./base";
27
-
28
- const DEFAULT_THINKING_BUDGET_TOKENS = 1024;
29
- const DEFAULT_MAX_OUTPUT_TOKENS = 128_000;
30
- const GEMINI_3_FLASH_MAX_OUTPUT_TOKENS = 8192;
31
-
32
- function isGemini3FlashModel(modelId: string): boolean {
33
- const normalized = modelId.toLowerCase();
34
- return (
35
- normalized.includes("gemini-3-flash") ||
36
- normalized.includes("gemini-3.0-flash")
37
- );
38
- }
39
-
40
- /**
41
- * Handler for Google's Gemini API
42
- */
43
- export class GeminiHandler extends BaseHandler {
44
- private client: GoogleGenAI | undefined;
45
-
46
- private ensureClient(): GoogleGenAI {
47
- if (!this.client) {
48
- // Check for Vertex AI configuration
49
- if (this.config.gcp?.projectId) {
50
- this.client = new GoogleGenAI({
51
- vertexai: true,
52
- project: this.config.gcp.projectId,
53
- location: this.config.region ?? "us-central1",
54
- httpOptions: {
55
- headers: this.getRequestHeaders(),
56
- },
57
- });
58
- } else {
59
- // Standard API key auth
60
- if (!this.config.apiKey) {
61
- throw new Error(
62
- "Gemini API key is required when not using Vertex AI",
63
- );
64
- }
65
-
66
- this.client = new GoogleGenAI({
67
- apiKey: this.config.apiKey,
68
- httpOptions: {
69
- headers: this.getRequestHeaders(),
70
- },
71
- });
72
- }
73
- }
74
- return this.client;
75
- }
76
-
77
- getModel(): HandlerModelInfo {
78
- const modelId = this.config.modelId;
79
- const knownModels = this.config.knownModels ?? {};
80
- const fallbackModel = knownModels[modelId] ?? {};
81
- const modelInfo = this.config.modelInfo ?? fallbackModel;
82
-
83
- return { id: modelId, info: { ...modelInfo, id: modelId } };
84
- }
85
-
86
- getMessages(_systemPrompt: string, messages: Message[]) {
87
- return convertToGeminiMessages(messages);
88
- }
89
-
90
- async *createMessage(
91
- systemPrompt: string,
92
- messages: Message[],
93
- tools?: ToolDefinition[],
94
- ): ApiStream {
95
- yield* retryStream(
96
- () => this.createMessageInternal(systemPrompt, messages, tools),
97
- { maxRetries: 4, baseDelay: 2000, maxDelay: 15000 },
98
- );
99
- }
100
-
101
- private async *createMessageInternal(
102
- systemPrompt: string,
103
- messages: Message[],
104
- tools?: ToolDefinition[],
105
- ): ApiStream {
106
- const client = this.ensureClient();
107
- const { id: modelId, info } = this.getModel();
108
- const abortSignal = this.getAbortSignal();
109
- const responseId = this.createResponseId();
110
-
111
- // Convert messages
112
- const contents = this.getMessages(systemPrompt, messages);
113
-
114
- const thinkingSupported = supportsModelThinking(info);
115
- const thinkingRequested =
116
- this.config.thinking === true ||
117
- typeof this.config.thinkingBudgetTokens === "number" ||
118
- typeof this.config.reasoningEffort === "string";
119
- let thinkingBudget = 0;
120
- let thinkingLevel: ThinkingLevel | undefined;
121
-
122
- if (thinkingSupported && thinkingRequested) {
123
- const requestedBudget =
124
- this.config.thinkingBudgetTokens ??
125
- (this.config.thinking ? DEFAULT_THINKING_BUDGET_TOKENS : 0);
126
- thinkingBudget = Math.min(
127
- Math.max(0, requestedBudget),
128
- info.thinkingConfig?.maxBudget ?? 24576,
129
- );
130
-
131
- // If thinkingConfig has a thinkingLevel, it supports thinking level control
132
- if (info.thinkingConfig?.thinkingLevel) {
133
- const level = this.config.reasoningEffort;
134
- if (level === "high") {
135
- thinkingLevel = ThinkingLevel.HIGH;
136
- } else if (level === "low" || level === "medium") {
137
- thinkingLevel = ThinkingLevel.LOW;
138
- }
139
- }
140
- }
141
-
142
- // Build request config with abort signal
143
- const fallbackMaxOutputTokens = isGemini3FlashModel(modelId)
144
- ? GEMINI_3_FLASH_MAX_OUTPUT_TOKENS
145
- : DEFAULT_MAX_OUTPUT_TOKENS;
146
- const maxOutputTokens =
147
- info.maxTokens ?? this.config.maxOutputTokens ?? fallbackMaxOutputTokens;
148
- const requestConfig: GenerateContentConfig = {
149
- httpOptions: this.config.baseUrl
150
- ? { baseUrl: this.config.baseUrl, headers: this.getRequestHeaders() }
151
- : undefined,
152
- abortSignal,
153
- systemInstruction: systemPrompt,
154
- temperature: info.temperature ?? 1,
155
- maxOutputTokens,
156
- };
157
-
158
- // Add thinking config only when explicitly requested and supported.
159
- if (
160
- info.thinkingConfig &&
161
- thinkingSupported &&
162
- thinkingRequested &&
163
- (thinkingBudget > 0 || !!thinkingLevel)
164
- ) {
165
- requestConfig.thinkingConfig = {
166
- thinkingBudget: thinkingLevel ? undefined : thinkingBudget,
167
- thinkingLevel,
168
- includeThoughts: true,
169
- };
170
- }
171
-
172
- // Add tools if provided
173
- if (tools && tools.length > 0) {
174
- const functionDeclarations = convertToolsToGemini(tools);
175
- requestConfig.tools = [{ functionDeclarations }];
176
- requestConfig.toolConfig = {
177
- functionCallingConfig: {
178
- mode: FunctionCallingConfigMode.AUTO,
179
- },
180
- };
181
- }
182
-
183
- try {
184
- const result = await client.models.generateContentStream({
185
- model: modelId,
186
- contents,
187
- config: requestConfig,
188
- });
189
-
190
- let promptTokens = 0;
191
- let outputTokens = 0;
192
- let cacheReadTokens = 0;
193
- let thoughtsTokenCount = 0;
194
- let syntheticToolCallIndex = 0;
195
-
196
- for await (const chunk of result) {
197
- // Handle content parts
198
- const parts = chunk?.candidates?.[0]?.content?.parts ?? [];
199
-
200
- for (const part of parts) {
201
- if ((part as any).thought && part.text) {
202
- // Thinking content
203
- yield {
204
- type: "reasoning",
205
- reasoning: part.text || "",
206
- signature: (part as any).thoughtSignature,
207
- id: responseId,
208
- };
209
- } else if (part.text) {
210
- // Regular text
211
- yield {
212
- type: "text",
213
- text: part.text,
214
- id: responseId,
215
- signature: (part as any).thoughtSignature,
216
- };
217
- }
218
-
219
- if (part.functionCall) {
220
- // Tool call
221
- const functionCall = part.functionCall;
222
- const callId =
223
- functionCall.id ??
224
- `${responseId}_tool_${syntheticToolCallIndex++}`;
225
- if (functionCall.name) {
226
- yield {
227
- type: "tool_calls",
228
- tool_call: {
229
- call_id: callId,
230
- function: {
231
- id: callId,
232
- name: functionCall.name,
233
- arguments:
234
- (functionCall.args as Record<string, unknown>) ?? {},
235
- },
236
- },
237
- id: responseId,
238
- signature: (part as any).thoughtSignature,
239
- };
240
- }
241
- }
242
- }
243
-
244
- // Track usage
245
- if (chunk.usageMetadata) {
246
- promptTokens = chunk.usageMetadata.promptTokenCount ?? promptTokens;
247
- outputTokens =
248
- chunk.usageMetadata.candidatesTokenCount ?? outputTokens;
249
- thoughtsTokenCount =
250
- (chunk.usageMetadata as any).thoughtsTokenCount ??
251
- thoughtsTokenCount;
252
- cacheReadTokens =
253
- (chunk.usageMetadata as any).cachedContentTokenCount ??
254
- cacheReadTokens;
255
- }
256
- }
257
-
258
- // Yield final usage
259
- const totalCost = this.calculateGeminiCost(
260
- promptTokens,
261
- outputTokens,
262
- thoughtsTokenCount,
263
- cacheReadTokens,
264
- );
265
-
266
- yield {
267
- type: "usage",
268
- inputTokens: promptTokens,
269
- outputTokens,
270
- thoughtsTokenCount,
271
- cacheReadTokens,
272
- cacheWriteTokens: 0,
273
- totalCost,
274
- id: responseId,
275
- };
276
-
277
- // Yield done chunk to indicate streaming completed successfully
278
- yield { type: "done", success: true, id: responseId };
279
- } catch (error) {
280
- // Handle rate limit errors with retry info
281
- if (error instanceof Error && error.message.includes("429")) {
282
- throw new RetriableError(error.message, undefined, { cause: error });
283
- }
284
- throw error;
285
- }
286
- }
287
-
288
- private calculateGeminiCost(
289
- inputTokens: number,
290
- outputTokens: number,
291
- thoughtsTokenCount: number,
292
- cacheReadTokens: number,
293
- ): number | undefined {
294
- return this.calculateCost(
295
- inputTokens,
296
- outputTokens + thoughtsTokenCount,
297
- cacheReadTokens,
298
- );
299
- }
300
- }
301
-
302
- /**
303
- * Create a Gemini handler
304
- */
305
- export function createGeminiHandler(config: ProviderConfig): GeminiHandler {
306
- return new GeminiHandler(config);
307
- }
@@ -1,67 +0,0 @@
1
- /**
2
- * Handlers Index
3
- *
4
- * Re-exports all handler classes and factory functions.
5
- */
6
-
7
- export { AnthropicHandler, createAnthropicHandler } from "./anthropic-base";
8
- export { AskSageHandler, createAskSageHandler } from "./asksage";
9
- export {
10
- getMissingApiKeyError,
11
- getProviderEnvKeys,
12
- normalizeProviderId,
13
- resolveApiKeyForProvider,
14
- } from "./auth";
15
- // Base classes
16
- export { BaseHandler } from "./base";
17
- export { BedrockHandler, createBedrockHandler } from "./bedrock-base";
18
- export {
19
- ClaudeCodeHandler,
20
- CodexHandler,
21
- createClaudeCodeHandler,
22
- createCodexHandler,
23
- createDifyHandler,
24
- createMistralHandler,
25
- createOpenCodeHandler,
26
- createSapAiCoreHandler,
27
- DifyHandler,
28
- MistralHandler,
29
- OpenCodeHandler,
30
- SapAiCoreHandler,
31
- } from "./community-sdk";
32
- export { FetchBaseHandler } from "./fetch-base";
33
- export { createGeminiHandler, GeminiHandler } from "./gemini-base";
34
- // OpenAI Chat Completions API handler
35
- export { createOpenAIHandler, OpenAIBaseHandler } from "./openai-base";
36
- // OpenAI Responses API handler
37
- export {
38
- createOpenAIResponsesHandler,
39
- OpenAIResponsesHandler,
40
- } from "./openai-responses";
41
- // Provider configurations
42
- export {
43
- clearLiveModelsCatalogCache,
44
- clearPrivateModelsCatalogCache,
45
- DEFAULT_MODELS_CATALOG_URL,
46
- getLiveModelsCatalog,
47
- getProviderConfig,
48
- isOpenAICompatibleProvider,
49
- OPENAI_COMPATIBLE_PROVIDERS,
50
- type ProviderDefaults,
51
- resolveProviderConfig,
52
- } from "./providers";
53
- // R1-based handlers (DeepSeek Reasoner, etc.)
54
- export { createR1Handler, R1BaseHandler } from "./r1-base";
55
- // Custom handler registry
56
- export {
57
- clearRegistry,
58
- getRegisteredHandler,
59
- getRegisteredHandlerAsync,
60
- getRegisteredProviderIds,
61
- hasRegisteredHandler,
62
- isRegisteredHandlerAsync,
63
- registerAsyncHandler,
64
- registerHandler,
65
- unregisterHandler,
66
- } from "./registry";
67
- export { createVertexHandler, VertexHandler } from "./vertex";