@marinoscar/platform-api 0.1.0-next.4 → 0.1.0-next.6

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 (241) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/ai/core/ai-error.d.ts +15 -158
  3. package/dist/ai/core/ai-error.d.ts.map +1 -1
  4. package/dist/ai/core/ai-error.js +58 -221
  5. package/dist/ai/core/ai-error.js.map +1 -1
  6. package/dist/ai/core/capabilities.d.ts +3 -126
  7. package/dist/ai/core/capabilities.d.ts.map +1 -1
  8. package/dist/ai/core/capabilities.js +13 -109
  9. package/dist/ai/core/capabilities.js.map +1 -1
  10. package/dist/ai/core/conversation.d.ts +2 -15
  11. package/dist/ai/core/conversation.d.ts.map +1 -1
  12. package/dist/ai/core/conversation.js +9 -54
  13. package/dist/ai/core/conversation.js.map +1 -1
  14. package/dist/ai/core/hosted-tools.d.ts +3 -172
  15. package/dist/ai/core/hosted-tools.d.ts.map +1 -1
  16. package/dist/ai/core/hosted-tools.js +20 -286
  17. package/dist/ai/core/hosted-tools.js.map +1 -1
  18. package/dist/ai/core/provider-adapter.interface.d.ts +3 -260
  19. package/dist/ai/core/provider-adapter.interface.d.ts.map +1 -1
  20. package/dist/ai/core/provider-adapter.interface.js +7 -29
  21. package/dist/ai/core/provider-adapter.interface.js.map +1 -1
  22. package/dist/ai/core/structured-output.d.ts +3 -49
  23. package/dist/ai/core/structured-output.d.ts.map +1 -1
  24. package/dist/ai/core/structured-output.js +9 -80
  25. package/dist/ai/core/structured-output.js.map +1 -1
  26. package/dist/ai/core/tools.d.ts +3 -93
  27. package/dist/ai/core/tools.d.ts.map +1 -1
  28. package/dist/ai/core/tools.js +9 -73
  29. package/dist/ai/core/tools.js.map +1 -1
  30. package/dist/ai/core/types/file-inputs.types.d.ts +3 -102
  31. package/dist/ai/core/types/file-inputs.types.d.ts.map +1 -1
  32. package/dist/ai/core/types/file-inputs.types.js +14 -78
  33. package/dist/ai/core/types/file-inputs.types.js.map +1 -1
  34. package/dist/ai/core/types/media.types.d.ts +3 -616
  35. package/dist/ai/core/types/media.types.d.ts.map +1 -1
  36. package/dist/ai/core/types/media.types.js +33 -203
  37. package/dist/ai/core/types/media.types.js.map +1 -1
  38. package/dist/ai/core/types/responses.types.d.ts +3 -619
  39. package/dist/ai/core/types/responses.types.d.ts.map +1 -1
  40. package/dist/ai/core/types/responses.types.js +9 -57
  41. package/dist/ai/core/types/responses.types.js.map +1 -1
  42. package/dist/ai/http/dto/ai-images.dto.d.ts +4 -4
  43. package/dist/ai/index.d.ts +1 -1
  44. package/dist/ai/index.d.ts.map +1 -1
  45. package/dist/ai/index.js +3 -3
  46. package/dist/ai/index.js.map +1 -1
  47. package/dist/ai/providers/anthropic/anthropic-client.factory.d.ts +13 -15
  48. package/dist/ai/providers/anthropic/anthropic-client.factory.d.ts.map +1 -1
  49. package/dist/ai/providers/anthropic/anthropic-client.factory.js +23 -52
  50. package/dist/ai/providers/anthropic/anthropic-client.factory.js.map +1 -1
  51. package/dist/ai/providers/anthropic/anthropic.adapter.d.ts +15 -51
  52. package/dist/ai/providers/anthropic/anthropic.adapter.d.ts.map +1 -1
  53. package/dist/ai/providers/anthropic/anthropic.adapter.js +29 -293
  54. package/dist/ai/providers/anthropic/anthropic.adapter.js.map +1 -1
  55. package/dist/ai/providers/azure-openai/azure-openai-client.factory.d.ts +10 -14
  56. package/dist/ai/providers/azure-openai/azure-openai-client.factory.d.ts.map +1 -1
  57. package/dist/ai/providers/azure-openai/azure-openai-client.factory.js +20 -53
  58. package/dist/ai/providers/azure-openai/azure-openai-client.factory.js.map +1 -1
  59. package/dist/ai/providers/azure-openai/azure-openai.adapter.d.ts +14 -31
  60. package/dist/ai/providers/azure-openai/azure-openai.adapter.d.ts.map +1 -1
  61. package/dist/ai/providers/azure-openai/azure-openai.adapter.js +25 -170
  62. package/dist/ai/providers/azure-openai/azure-openai.adapter.js.map +1 -1
  63. package/dist/ai/providers/gemini/gemini-client.factory.d.ts +13 -18
  64. package/dist/ai/providers/gemini/gemini-client.factory.d.ts.map +1 -1
  65. package/dist/ai/providers/gemini/gemini-client.factory.js +24 -68
  66. package/dist/ai/providers/gemini/gemini-client.factory.js.map +1 -1
  67. package/dist/ai/providers/gemini/gemini.adapter.d.ts +15 -58
  68. package/dist/ai/providers/gemini/gemini.adapter.d.ts.map +1 -1
  69. package/dist/ai/providers/gemini/gemini.adapter.js +29 -348
  70. package/dist/ai/providers/gemini/gemini.adapter.js.map +1 -1
  71. package/dist/ai/providers/openai/openai-client.factory.d.ts +13 -35
  72. package/dist/ai/providers/openai/openai-client.factory.d.ts.map +1 -1
  73. package/dist/ai/providers/openai/openai-client.factory.js +25 -64
  74. package/dist/ai/providers/openai/openai-client.factory.js.map +1 -1
  75. package/dist/ai/providers/openai/openai.adapter.d.ts +15 -48
  76. package/dist/ai/providers/openai/openai.adapter.d.ts.map +1 -1
  77. package/dist/ai/providers/openai/openai.adapter.js +27 -206
  78. package/dist/ai/providers/openai/openai.adapter.js.map +1 -1
  79. package/dist/ai/providers/openai-compatible/openai-compatible-client.factory.d.ts +11 -11
  80. package/dist/ai/providers/openai-compatible/openai-compatible-client.factory.d.ts.map +1 -1
  81. package/dist/ai/providers/openai-compatible/openai-compatible-client.factory.js +18 -41
  82. package/dist/ai/providers/openai-compatible/openai-compatible-client.factory.js.map +1 -1
  83. package/dist/ai/providers/openai-compatible/openai-compatible.adapter.d.ts +14 -32
  84. package/dist/ai/providers/openai-compatible/openai-compatible.adapter.d.ts.map +1 -1
  85. package/dist/ai/providers/openai-compatible/openai-compatible.adapter.js +25 -149
  86. package/dist/ai/providers/openai-compatible/openai-compatible.adapter.js.map +1 -1
  87. package/dist/ai/providers/platform-ai-tracer.d.ts +4 -0
  88. package/dist/ai/providers/platform-ai-tracer.d.ts.map +1 -0
  89. package/dist/ai/providers/platform-ai-tracer.js +20 -0
  90. package/dist/ai/providers/platform-ai-tracer.js.map +1 -0
  91. package/dist/ai/runtime/ai-image-run-request.d.ts +1 -1
  92. package/dist/ai/testing/conformance/ai-no-sdk-leak.suite.d.ts +28 -5
  93. package/dist/ai/testing/conformance/ai-no-sdk-leak.suite.d.ts.map +1 -1
  94. package/dist/ai/testing/conformance/ai-no-sdk-leak.suite.js +54 -9
  95. package/dist/ai/testing/conformance/ai-no-sdk-leak.suite.js.map +1 -1
  96. package/dist/ai/testing/index.d.ts +1 -1
  97. package/dist/ai/testing/index.d.ts.map +1 -1
  98. package/dist/ai/testing/index.js +2 -1
  99. package/dist/ai/testing/index.js.map +1 -1
  100. package/dist/jobs/rate-limit.error.d.ts +2 -26
  101. package/dist/jobs/rate-limit.error.d.ts.map +1 -1
  102. package/dist/jobs/rate-limit.error.js +4 -52
  103. package/dist/jobs/rate-limit.error.js.map +1 -1
  104. package/dist/storage/config/storage-config.controller.d.ts +1 -1
  105. package/package.json +3 -2
  106. package/dist/ai/providers/anthropic/anthropic-errors.d.ts +0 -16
  107. package/dist/ai/providers/anthropic/anthropic-errors.d.ts.map +0 -1
  108. package/dist/ai/providers/anthropic/anthropic-errors.js +0 -156
  109. package/dist/ai/providers/anthropic/anthropic-errors.js.map +0 -1
  110. package/dist/ai/providers/anthropic/anthropic-messages.mapper.d.ts +0 -80
  111. package/dist/ai/providers/anthropic/anthropic-messages.mapper.d.ts.map +0 -1
  112. package/dist/ai/providers/anthropic/anthropic-messages.mapper.js +0 -536
  113. package/dist/ai/providers/anthropic/anthropic-messages.mapper.js.map +0 -1
  114. package/dist/ai/providers/anthropic/anthropic-model-catalog.d.ts +0 -49
  115. package/dist/ai/providers/anthropic/anthropic-model-catalog.d.ts.map +0 -1
  116. package/dist/ai/providers/anthropic/anthropic-model-catalog.js +0 -198
  117. package/dist/ai/providers/anthropic/anthropic-model-catalog.js.map +0 -1
  118. package/dist/ai/providers/anthropic/anthropic-stream.mapper.d.ts +0 -35
  119. package/dist/ai/providers/anthropic/anthropic-stream.mapper.d.ts.map +0 -1
  120. package/dist/ai/providers/anthropic/anthropic-stream.mapper.js +0 -203
  121. package/dist/ai/providers/anthropic/anthropic-stream.mapper.js.map +0 -1
  122. package/dist/ai/providers/anthropic/testing/anthropic-fixtures.d.ts +0 -18
  123. package/dist/ai/providers/anthropic/testing/anthropic-fixtures.d.ts.map +0 -1
  124. package/dist/ai/providers/anthropic/testing/anthropic-fixtures.js +0 -120
  125. package/dist/ai/providers/anthropic/testing/anthropic-fixtures.js.map +0 -1
  126. package/dist/ai/providers/anthropic/testing/anthropic-mock-transport.d.ts +0 -59
  127. package/dist/ai/providers/anthropic/testing/anthropic-mock-transport.d.ts.map +0 -1
  128. package/dist/ai/providers/anthropic/testing/anthropic-mock-transport.js +0 -206
  129. package/dist/ai/providers/anthropic/testing/anthropic-mock-transport.js.map +0 -1
  130. package/dist/ai/providers/azure-openai/azure-openai-settings.d.ts +0 -23
  131. package/dist/ai/providers/azure-openai/azure-openai-settings.d.ts.map +0 -1
  132. package/dist/ai/providers/azure-openai/azure-openai-settings.js +0 -54
  133. package/dist/ai/providers/azure-openai/azure-openai-settings.js.map +0 -1
  134. package/dist/ai/providers/gemini/gemini-content.mapper.d.ts +0 -98
  135. package/dist/ai/providers/gemini/gemini-content.mapper.d.ts.map +0 -1
  136. package/dist/ai/providers/gemini/gemini-content.mapper.js +0 -665
  137. package/dist/ai/providers/gemini/gemini-content.mapper.js.map +0 -1
  138. package/dist/ai/providers/gemini/gemini-embeddings.mapper.d.ts +0 -13
  139. package/dist/ai/providers/gemini/gemini-embeddings.mapper.d.ts.map +0 -1
  140. package/dist/ai/providers/gemini/gemini-embeddings.mapper.js +0 -78
  141. package/dist/ai/providers/gemini/gemini-embeddings.mapper.js.map +0 -1
  142. package/dist/ai/providers/gemini/gemini-errors.d.ts +0 -32
  143. package/dist/ai/providers/gemini/gemini-errors.d.ts.map +0 -1
  144. package/dist/ai/providers/gemini/gemini-errors.js +0 -191
  145. package/dist/ai/providers/gemini/gemini-errors.js.map +0 -1
  146. package/dist/ai/providers/gemini/gemini-model-catalog.d.ts +0 -45
  147. package/dist/ai/providers/gemini/gemini-model-catalog.d.ts.map +0 -1
  148. package/dist/ai/providers/gemini/gemini-model-catalog.js +0 -264
  149. package/dist/ai/providers/gemini/gemini-model-catalog.js.map +0 -1
  150. package/dist/ai/providers/gemini/gemini-stream.mapper.d.ts +0 -31
  151. package/dist/ai/providers/gemini/gemini-stream.mapper.d.ts.map +0 -1
  152. package/dist/ai/providers/gemini/gemini-stream.mapper.js +0 -118
  153. package/dist/ai/providers/gemini/gemini-stream.mapper.js.map +0 -1
  154. package/dist/ai/providers/gemini/testing/gemini-fixtures.d.ts +0 -80
  155. package/dist/ai/providers/gemini/testing/gemini-fixtures.d.ts.map +0 -1
  156. package/dist/ai/providers/gemini/testing/gemini-fixtures.js +0 -118
  157. package/dist/ai/providers/gemini/testing/gemini-fixtures.js.map +0 -1
  158. package/dist/ai/providers/gemini/testing/gemini-mock-transport.d.ts +0 -82
  159. package/dist/ai/providers/gemini/testing/gemini-mock-transport.d.ts.map +0 -1
  160. package/dist/ai/providers/gemini/testing/gemini-mock-transport.js +0 -342
  161. package/dist/ai/providers/gemini/testing/gemini-mock-transport.js.map +0 -1
  162. package/dist/ai/providers/openai/openai-audio.mapper.d.ts +0 -42
  163. package/dist/ai/providers/openai/openai-audio.mapper.d.ts.map +0 -1
  164. package/dist/ai/providers/openai/openai-audio.mapper.js +0 -266
  165. package/dist/ai/providers/openai/openai-audio.mapper.js.map +0 -1
  166. package/dist/ai/providers/openai/openai-call-telemetry.d.ts +0 -25
  167. package/dist/ai/providers/openai/openai-call-telemetry.d.ts.map +0 -1
  168. package/dist/ai/providers/openai/openai-call-telemetry.js +0 -93
  169. package/dist/ai/providers/openai/openai-call-telemetry.js.map +0 -1
  170. package/dist/ai/providers/openai/openai-chat-completions-stream.mapper.d.ts +0 -41
  171. package/dist/ai/providers/openai/openai-chat-completions-stream.mapper.d.ts.map +0 -1
  172. package/dist/ai/providers/openai/openai-chat-completions-stream.mapper.js +0 -173
  173. package/dist/ai/providers/openai/openai-chat-completions-stream.mapper.js.map +0 -1
  174. package/dist/ai/providers/openai/openai-chat-completions.engine.d.ts +0 -33
  175. package/dist/ai/providers/openai/openai-chat-completions.engine.d.ts.map +0 -1
  176. package/dist/ai/providers/openai/openai-chat-completions.engine.js +0 -165
  177. package/dist/ai/providers/openai/openai-chat-completions.engine.js.map +0 -1
  178. package/dist/ai/providers/openai/openai-chat-completions.mapper.d.ts +0 -42
  179. package/dist/ai/providers/openai/openai-chat-completions.mapper.d.ts.map +0 -1
  180. package/dist/ai/providers/openai/openai-chat-completions.mapper.js +0 -304
  181. package/dist/ai/providers/openai/openai-chat-completions.mapper.js.map +0 -1
  182. package/dist/ai/providers/openai/openai-embeddings.mapper.d.ts +0 -25
  183. package/dist/ai/providers/openai/openai-embeddings.mapper.d.ts.map +0 -1
  184. package/dist/ai/providers/openai/openai-embeddings.mapper.js +0 -81
  185. package/dist/ai/providers/openai/openai-embeddings.mapper.js.map +0 -1
  186. package/dist/ai/providers/openai/openai-errors.d.ts +0 -45
  187. package/dist/ai/providers/openai/openai-errors.d.ts.map +0 -1
  188. package/dist/ai/providers/openai/openai-errors.js +0 -247
  189. package/dist/ai/providers/openai/openai-errors.js.map +0 -1
  190. package/dist/ai/providers/openai/openai-family-guards.d.ts +0 -4
  191. package/dist/ai/providers/openai/openai-family-guards.d.ts.map +0 -1
  192. package/dist/ai/providers/openai/openai-family-guards.js +0 -19
  193. package/dist/ai/providers/openai/openai-family-guards.js.map +0 -1
  194. package/dist/ai/providers/openai/openai-images.mapper.d.ts +0 -23
  195. package/dist/ai/providers/openai/openai-images.mapper.d.ts.map +0 -1
  196. package/dist/ai/providers/openai/openai-images.mapper.js +0 -164
  197. package/dist/ai/providers/openai/openai-images.mapper.js.map +0 -1
  198. package/dist/ai/providers/openai/openai-model-catalog.d.ts +0 -36
  199. package/dist/ai/providers/openai/openai-model-catalog.d.ts.map +0 -1
  200. package/dist/ai/providers/openai/openai-model-catalog.js +0 -209
  201. package/dist/ai/providers/openai/openai-model-catalog.js.map +0 -1
  202. package/dist/ai/providers/openai/openai-realtime.mapper.d.ts +0 -18
  203. package/dist/ai/providers/openai/openai-realtime.mapper.d.ts.map +0 -1
  204. package/dist/ai/providers/openai/openai-realtime.mapper.js +0 -159
  205. package/dist/ai/providers/openai/openai-realtime.mapper.js.map +0 -1
  206. package/dist/ai/providers/openai/openai-redirect-guard.d.ts +0 -9
  207. package/dist/ai/providers/openai/openai-redirect-guard.d.ts.map +0 -1
  208. package/dist/ai/providers/openai/openai-redirect-guard.js +0 -61
  209. package/dist/ai/providers/openai/openai-redirect-guard.js.map +0 -1
  210. package/dist/ai/providers/openai/openai-responses.engine.d.ts +0 -50
  211. package/dist/ai/providers/openai/openai-responses.engine.d.ts.map +0 -1
  212. package/dist/ai/providers/openai/openai-responses.engine.js +0 -206
  213. package/dist/ai/providers/openai/openai-responses.engine.js.map +0 -1
  214. package/dist/ai/providers/openai/openai-responses.mapper.d.ts +0 -64
  215. package/dist/ai/providers/openai/openai-responses.mapper.d.ts.map +0 -1
  216. package/dist/ai/providers/openai/openai-responses.mapper.js +0 -580
  217. package/dist/ai/providers/openai/openai-responses.mapper.js.map +0 -1
  218. package/dist/ai/providers/openai/openai-stream.mapper.d.ts +0 -28
  219. package/dist/ai/providers/openai/openai-stream.mapper.d.ts.map +0 -1
  220. package/dist/ai/providers/openai/openai-stream.mapper.js +0 -139
  221. package/dist/ai/providers/openai/openai-stream.mapper.js.map +0 -1
  222. package/dist/ai/providers/openai/testing/chat-completions-fixtures.d.ts +0 -24
  223. package/dist/ai/providers/openai/testing/chat-completions-fixtures.d.ts.map +0 -1
  224. package/dist/ai/providers/openai/testing/chat-completions-fixtures.js +0 -72
  225. package/dist/ai/providers/openai/testing/chat-completions-fixtures.js.map +0 -1
  226. package/dist/ai/providers/openai/testing/conformance-responders.d.ts +0 -7
  227. package/dist/ai/providers/openai/testing/conformance-responders.d.ts.map +0 -1
  228. package/dist/ai/providers/openai/testing/conformance-responders.js +0 -64
  229. package/dist/ai/providers/openai/testing/conformance-responders.js.map +0 -1
  230. package/dist/ai/providers/openai/testing/openai-fixtures.d.ts +0 -52
  231. package/dist/ai/providers/openai/testing/openai-fixtures.d.ts.map +0 -1
  232. package/dist/ai/providers/openai/testing/openai-fixtures.js +0 -208
  233. package/dist/ai/providers/openai/testing/openai-fixtures.js.map +0 -1
  234. package/dist/ai/providers/openai/testing/openai-mock-transport.d.ts +0 -242
  235. package/dist/ai/providers/openai/testing/openai-mock-transport.d.ts.map +0 -1
  236. package/dist/ai/providers/openai/testing/openai-mock-transport.js +0 -523
  237. package/dist/ai/providers/openai/testing/openai-mock-transport.js.map +0 -1
  238. package/dist/ai/providers/openai-compatible/openai-compatible-settings.d.ts +0 -13
  239. package/dist/ai/providers/openai-compatible/openai-compatible-settings.d.ts.map +0 -1
  240. package/dist/ai/providers/openai-compatible/openai-compatible-settings.js +0 -29
  241. package/dist/ai/providers/openai-compatible/openai-compatible-settings.js.map +0 -1
@@ -1,665 +0,0 @@
1
- "use strict";
2
- // =============================================================================
3
- // Our Responses types <-> Gemini `generateContent` (issue #447, epic #421)
4
- // =============================================================================
5
- //
6
- // Pure functions, no I/O: `toGeminiRequest` builds the SDK parameters from an
7
- // `AiResponseRequest`; `GeminiOutputAssembler` turns response PARTS into our
8
- // output items, and is shared by `create` (`fromGeminiResponse`) and the
9
- // stream mapper, so a streamed and a non-streamed call cannot disagree about
10
- // what a response means.
11
- //
12
- // REQUEST MAPPING
13
- //
14
- // instructions (+ system/developer messages) -> `systemInstruction`
15
- // message (user) -> user turn: `text`; `inlineData` (base64) for a
16
- // `data:` URL or a storage object's bytes;
17
- // `fileData { fileUri }` for any other URL
18
- // message (assistant) -> model turn, text only
19
- // function_call -> model `functionCall { name, args, id? }`
20
- // function_call_output -> user `functionResponse { name, response, id? }`
21
- // — Gemini keys a response by the function NAME,
22
- // which is read off the matching `function_call`
23
- // earlier in the (full-history) input
24
- // reasoning -> the `thoughtSignature` it carries as provider
25
- // state (`AI_PROVIDER_STATE`), put back on the
26
- // part it came from (see REPLAY below); a
27
- // reasoning item without Gemini state is dropped
28
- // function tools -> one tool of `functionDeclarations`
29
- // (`parametersJsonSchema`)
30
- // toolChoice -> `functionCallingConfig.mode` AUTO | NONE | ANY,
31
- // a named function -> ANY + `allowedFunctionNames`
32
- // reasoning -> `thinkingConfig { includeThoughts: true, ... }`:
33
- // 'level' families `thinkingLevel`, 'budget'
34
- // families (and unclassified models)
35
- // `thinkingBudget` (`GEMINI_THINKING_BUDGETS`)
36
- // structuredOutput -> `responseMimeType: 'application/json'` +
37
- // `responseJsonSchema`, validated with
38
- // `parseStructured`
39
- // maxOutputTokens / temperature -> the same-named config fields
40
- //
41
- // Consecutive items of the same role merge into one turn, so a replayed round
42
- // (a model turn of text + function calls, then a user turn of all their
43
- // responses) is exactly the alternating shape Gemini requires.
44
- //
45
- // THOUGHTS ARE NEVER EXPOSED RAW. With `includeThoughts`, Gemini returns
46
- // thought SUMMARIES (parts marked `thought: true`); their text becomes a
47
- // reasoning item's `summary`. A part's `thoughtSignature` — Gemini's opaque,
48
- // encrypted reasoning state, required back on a function-call turn by Gemini
49
- // 3 — travels only as symbol-keyed provider state, never in a serialisable
50
- // field.
51
- //
52
- // REPLAY. A signature belongs to one PART, so its reasoning item names the
53
- // part (`GeminiReasoningState.target`):
54
- //
55
- // 'thought' a thought summary part: replayed as that thought part;
56
- // 'function_call' the `functionCall` part of `callId`: the signature is put
57
- // back on that part when the function call is replayed;
58
- // 'text' the text part before it: put back on the replayed text.
59
- //
60
- // The signature-only reasoning items ('function_call', 'text') carry an empty
61
- // `summary` — they exist to carry the signature through the tool loop's
62
- // `replayOutput` hop, which keeps reasoning state and nothing else's.
63
- //
64
- // FUNCTION CALL IDS. Gemini 3 names each call; earlier models do not. A call
65
- // without an id gets a synthetic one (`GEMINI_SYNTHETIC_CALL_ID_PREFIX`) so
66
- // the neutral contract's non-empty `callId` holds; a synthetic id is never
67
- // sent back to Gemini (the call and its response are matched by name and
68
- // order, as Gemini itself does for id-less calls).
69
- //
70
- // REFUSED (AI_CAPABILITY_UNSUPPORTED): `previousResponseId` (Gemini stores
71
- // nothing — the facade refuses it first; this is defence in depth), every
72
- // hosted tool (`supportsHostedTools: false`), a reasoning effort on a family
73
- // without thinking, structured output on a family that is not sent a schema,
74
- // and a schema combined with function tools on a family that rejects that
75
- // combination.
76
- //
77
- // NOT MAPPED: `metadata` (the Gemini API has no free-form request metadata) —
78
- // dropped. `providerOptions.gemini` is shallow-merged into the CONFIG last as
79
- // the escape hatch, except `abortSignal`, which belongs to the port.
80
- // =============================================================================
81
- Object.defineProperty(exports, "__esModule", { value: true });
82
- exports.GeminiOutputAssembler = exports.GEMINI_SYNTHETIC_CALL_ID_PREFIX = void 0;
83
- exports.geminiMimeTypeFromUrl = geminiMimeTypeFromUrl;
84
- exports.geminiStorageObjectIds = geminiStorageObjectIds;
85
- exports.geminiReasoningState = geminiReasoningState;
86
- exports.geminiFunctionResponse = geminiFunctionResponse;
87
- exports.toGeminiRequest = toGeminiRequest;
88
- exports.geminiUsage = geminiUsage;
89
- exports.geminiFinishReason = geminiFinishReason;
90
- exports.finishGeminiResponse = finishGeminiResponse;
91
- exports.geminiFallbackResponseId = geminiFallbackResponseId;
92
- exports.fromGeminiResponse = fromGeminiResponse;
93
- const node_crypto_1 = require("node:crypto");
94
- const ai_error_1 = require("../../core/ai-error");
95
- const structured_output_1 = require("../../core/structured-output");
96
- const responses_types_1 = require("../../core/types/responses.types");
97
- const gemini_errors_1 = require("./gemini-errors");
98
- const gemini_model_catalog_1 = require("./gemini-model-catalog");
99
- /** The prefix of a call id this adapter made up for an id-less Gemini call. */
100
- exports.GEMINI_SYNTHETIC_CALL_ID_PREFIX = 'gemini_call_';
101
- function unsupported(message, details = {}) {
102
- return new ai_error_1.AiError('AI_CAPABILITY_UNSUPPORTED', message, {
103
- details: { provider: gemini_errors_1.GEMINI_PROVIDER_ID, ...details },
104
- });
105
- }
106
- function invalid(message, details = {}) {
107
- return new ai_error_1.AiError('AI_INVALID_REQUEST', message, {
108
- details: { provider: gemini_errors_1.GEMINI_PROVIDER_ID, ...details },
109
- });
110
- }
111
- // ---- request: content -----------------------------------------------------------
112
- /** `data:<type>;base64,<payload>` → its parts, or `null` for anything else. */
113
- function parseDataUrl(url) {
114
- const match = /^data:([^;,]+)(?:;[^,]*)?;base64,(.*)$/s.exec(url);
115
- return match ? { mimeType: match[1].trim().toLowerCase(), base64: match[2] } : null;
116
- }
117
- /** File extensions whose MIME type is unambiguous, for a `fileData` URL (Gemini requires one). */
118
- const EXTENSION_MIME_TYPES = {
119
- png: 'image/png',
120
- jpg: 'image/jpeg',
121
- jpeg: 'image/jpeg',
122
- gif: 'image/gif',
123
- webp: 'image/webp',
124
- heic: 'image/heic',
125
- heif: 'image/heif',
126
- pdf: 'application/pdf',
127
- txt: 'text/plain',
128
- md: 'text/md',
129
- html: 'text/html',
130
- csv: 'text/csv',
131
- json: 'application/json',
132
- mp3: 'audio/mp3',
133
- wav: 'audio/wav',
134
- ogg: 'audio/ogg',
135
- flac: 'audio/flac',
136
- mp4: 'video/mp4',
137
- webm: 'video/webm',
138
- mov: 'video/quicktime',
139
- };
140
- /** The MIME type a URL's path extension names, or `undefined` (Gemini then decides). */
141
- function geminiMimeTypeFromUrl(url) {
142
- let path;
143
- try {
144
- path = new URL(url).pathname;
145
- }
146
- catch {
147
- return undefined;
148
- }
149
- const ext = /\.([a-z0-9]+)$/i.exec(path)?.[1]?.toLowerCase();
150
- return ext ? EXTENSION_MIME_TYPES[ext] : undefined;
151
- }
152
- function partFromUrl(url) {
153
- const data = parseDataUrl(url);
154
- if (data)
155
- return { inlineData: { mimeType: data.mimeType, data: data.base64 } };
156
- const mimeType = geminiMimeTypeFromUrl(url);
157
- return { fileData: { fileUri: url, ...(mimeType ? { mimeType } : {}) } };
158
- }
159
- function storagePart(storageObjectId, storage, kind) {
160
- const delivered = storage?.get(storageObjectId);
161
- if (!delivered) {
162
- throw invalid('A storage-object input was not resolved by the runtime.', { part: kind });
163
- }
164
- return { inlineData: { mimeType: delivered.mimeType, data: Buffer.from(delivered.data).toString('base64') } };
165
- }
166
- function toUserPart(part, storage) {
167
- switch (part.type) {
168
- case 'text':
169
- return { text: part.text };
170
- case 'image':
171
- case 'file':
172
- if (part.url)
173
- return partFromUrl(part.url);
174
- if (part.storageObjectId)
175
- return storagePart(part.storageObjectId, storage, part.type);
176
- throw invalid(`An ${part.type} part needs a url or a storageObjectId.`, { part: part.type });
177
- }
178
- }
179
- /** Every storage object id `req` names, in order, each once. */
180
- function geminiStorageObjectIds(req) {
181
- if (!Array.isArray(req.input))
182
- return [];
183
- const ids = new Set();
184
- for (const item of req.input) {
185
- if (item.type !== 'message')
186
- continue;
187
- for (const part of item.content) {
188
- if (part.type !== 'text' && !part.url && part.storageObjectId)
189
- ids.add(part.storageObjectId);
190
- }
191
- }
192
- return [...ids];
193
- }
194
- /** The Gemini state an item carries, or `undefined` for another provider's / none. */
195
- function geminiReasoningState(item) {
196
- const state = item[responses_types_1.AI_PROVIDER_STATE];
197
- if (!state || state.provider !== gemini_errors_1.GEMINI_PROVIDER_ID)
198
- return undefined;
199
- const data = state.data;
200
- if (!data || typeof data.signature !== 'string' || data.signature.length === 0)
201
- return undefined;
202
- if (data.target !== 'thought' && data.target !== 'text' && data.target !== 'function_call')
203
- return undefined;
204
- return {
205
- signature: data.signature,
206
- target: data.target,
207
- ...(typeof data.callId === 'string' ? { callId: data.callId } : {}),
208
- };
209
- }
210
- function isSyntheticCallId(callId) {
211
- return callId.startsWith(exports.GEMINI_SYNTHETIC_CALL_ID_PREFIX);
212
- }
213
- function parseArguments(item) {
214
- let value;
215
- try {
216
- value = JSON.parse(item.arguments || '{}');
217
- }
218
- catch {
219
- throw invalid('A replayed function call carries arguments that are not JSON.', { callId: item.callId });
220
- }
221
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
222
- throw invalid('A replayed function call carries arguments that are not a JSON object.', { callId: item.callId });
223
- }
224
- return value;
225
- }
226
- /**
227
- * A function's output as Gemini's `functionResponse.response` (a JSON object):
228
- * a JSON object output is sent as-is; anything else — JSON or plain text — is
229
- * wrapped as `{ output }`, the key Gemini documents for a function's output.
230
- */
231
- function geminiFunctionResponse(output) {
232
- try {
233
- const value = JSON.parse(output);
234
- if (value && typeof value === 'object' && !Array.isArray(value))
235
- return value;
236
- return { output: value };
237
- }
238
- catch {
239
- return { output };
240
- }
241
- }
242
- /** Appends parts to the conversation, merging into the previous turn when the role matches. */
243
- function push(conv, role, parts) {
244
- if (parts.length === 0)
245
- return;
246
- const last = conv.contents[conv.contents.length - 1];
247
- if (last && last.role === role && last.parts) {
248
- last.parts.push(...parts);
249
- }
250
- else {
251
- conv.contents.push({ role, parts: [...parts] });
252
- }
253
- }
254
- /** The last text part of the current model turn, if the conversation ends in one. */
255
- function lastModelTextPart(conv) {
256
- const last = conv.contents[conv.contents.length - 1];
257
- if (!last || last.role !== 'model' || !last.parts)
258
- return undefined;
259
- for (let i = last.parts.length - 1; i >= 0; i -= 1) {
260
- const part = last.parts[i];
261
- if (typeof part.text === 'string' && part.thought !== true)
262
- return part;
263
- }
264
- return undefined;
265
- }
266
- function toConversation(req, storage) {
267
- const conv = { system: req.instructions ? [req.instructions] : [], contents: [] };
268
- if (typeof req.input === 'string') {
269
- push(conv, 'user', [{ text: req.input }]);
270
- return conv;
271
- }
272
- const items = req.input;
273
- /** Signatures waiting for the `functionCall` part of their call, by call id. */
274
- const callSignatures = new Map();
275
- items.forEach((item, index) => {
276
- switch (item.type) {
277
- case 'message':
278
- if (item.role === 'system' || item.role === 'developer') {
279
- if (item.content.some((part) => part.type !== 'text')) {
280
- throw invalid('A system message may contain only text parts.', { role: item.role });
281
- }
282
- conv.system.push(item.content.map((part) => (part.type === 'text' ? part.text : '')).join(''));
283
- }
284
- else if (item.role === 'assistant') {
285
- if (item.content.some((part) => part.type !== 'text')) {
286
- throw invalid('An assistant message may contain only text parts.', { role: 'assistant' });
287
- }
288
- push(conv, 'model', item.content
289
- .map((part) => (part.type === 'text' ? part.text : ''))
290
- .filter((text) => text.length > 0)
291
- .map((text) => ({ text })));
292
- }
293
- else {
294
- push(conv, 'user', item.content.map((part) => toUserPart(part, storage)));
295
- }
296
- break;
297
- case 'function_call': {
298
- const call = {
299
- name: item.name,
300
- args: parseArguments(item),
301
- ...(isSyntheticCallId(item.callId) ? {} : { id: item.callId }),
302
- };
303
- const signature = callSignatures.get(item.callId);
304
- callSignatures.delete(item.callId);
305
- push(conv, 'model', [{ functionCall: call, ...(signature ? { thoughtSignature: signature } : {}) }]);
306
- break;
307
- }
308
- case 'function_call_output': {
309
- // Gemini matches a response to its call by NAME: find the call.
310
- const call = items
311
- .slice(0, index)
312
- .reverse()
313
- .find((earlier) => earlier.type === 'function_call' && earlier.callId === item.callId);
314
- if (!call) {
315
- throw invalid('A function_call_output needs its function_call earlier in the input; Gemini stores no conversation.', { callId: item.callId });
316
- }
317
- push(conv, 'user', [
318
- {
319
- functionResponse: {
320
- name: call.name,
321
- response: geminiFunctionResponse(item.output),
322
- ...(isSyntheticCallId(item.callId) ? {} : { id: item.callId }),
323
- },
324
- },
325
- ]);
326
- break;
327
- }
328
- case 'reasoning': {
329
- const state = geminiReasoningState(item);
330
- if (!state)
331
- break;
332
- if (state.target === 'function_call' && state.callId) {
333
- callSignatures.set(state.callId, state.signature);
334
- }
335
- else if (state.target === 'text') {
336
- const text = lastModelTextPart(conv);
337
- if (text && !text.thoughtSignature)
338
- text.thoughtSignature = state.signature;
339
- }
340
- else if (state.target === 'thought') {
341
- push(conv, 'model', [{ text: item.summary.join('\n\n'), thought: true, thoughtSignature: state.signature }]);
342
- }
343
- break;
344
- }
345
- }
346
- });
347
- return conv;
348
- }
349
- // ---- request: tools, schema, thinking ---------------------------------------------
350
- function toFunctionDeclaration(tool) {
351
- if (tool.type !== 'function') {
352
- throw unsupported(`Hosted tool "${tool.type}" is not supported by Gemini.`, { tool: tool.type });
353
- }
354
- const schema = (0, structured_output_1.toJsonSchema)(tool.parameters);
355
- if (schema.type !== 'object') {
356
- throw invalid(`Tool "${tool.name}" parameters must be an object schema.`, { tool: tool.name });
357
- }
358
- return { name: tool.name, description: tool.description, parametersJsonSchema: schema };
359
- }
360
- function toToolConfig(choice) {
361
- const mode = (value) => value;
362
- if (choice === 'auto')
363
- return { functionCallingConfig: { mode: mode('AUTO') } };
364
- if (choice === 'none')
365
- return { functionCallingConfig: { mode: mode('NONE') } };
366
- if (choice === 'required')
367
- return { functionCallingConfig: { mode: mode('ANY') } };
368
- return { functionCallingConfig: { mode: mode('ANY'), allowedFunctionNames: [choice.name] } };
369
- }
370
- /** `req.reasoning` as a `thinkingConfig` for this family — or refused. */
371
- function toThinkingConfig(req, profile) {
372
- const effort = req.reasoning?.effort;
373
- const wantsSummary = req.reasoning?.summary !== undefined;
374
- if (effort === undefined && !wantsSummary)
375
- return undefined;
376
- // An unclassified model is asked with a budget, which every thinking
377
- // family accepts; an administrator enabled it knowing more than this table,
378
- // and Gemini's own 400 is then the answer.
379
- const style = profile?.thinking ?? 'budget';
380
- if (style === 'none') {
381
- if (effort !== undefined) {
382
- throw unsupported(`Model "${req.model}" does not support reasoning effort.`, {
383
- model: req.model,
384
- capability: 'reasoning',
385
- });
386
- }
387
- // A summary asked of a model that does not think: nothing to summarise.
388
- return undefined;
389
- }
390
- const config = { includeThoughts: true };
391
- if (effort !== undefined) {
392
- if (style === 'level' && profile?.thinkingLevels) {
393
- config.thinkingLevel = profile.thinkingLevels[effort];
394
- }
395
- else {
396
- config.thinkingBudget = gemini_model_catalog_1.GEMINI_THINKING_BUDGETS[effort];
397
- }
398
- }
399
- return config;
400
- }
401
- /**
402
- * Builds the Gemini request for `req`.
403
- *
404
- * `profile` is the model's classifier profile (or `null` when unclassified);
405
- * `storage` is what the adapter delivered for each storage-object part.
406
- * Throws `AiError` (never an SDK error): see the file header.
407
- */
408
- function toGeminiRequest(req, profile, storage) {
409
- if (req.previousResponseId !== undefined) {
410
- throw unsupported('Gemini does not store responses; send the conversation history as input instead.', {
411
- capability: 'previous_response_id',
412
- });
413
- }
414
- if (profile?.kind === 'embedding') {
415
- throw unsupported(`Model "${req.model}" is an embedding model and cannot generate responses.`, {
416
- model: req.model,
417
- capability: 'responses',
418
- });
419
- }
420
- const conv = toConversation(req, storage);
421
- if (conv.contents.length === 0) {
422
- throw invalid('The request has no user or assistant content.');
423
- }
424
- const declarations = (req.tools ?? []).map(toFunctionDeclaration);
425
- const config = {};
426
- if (conv.system.length > 0)
427
- config.systemInstruction = { parts: [{ text: conv.system.join('\n\n') }] };
428
- if (declarations.length > 0)
429
- config.tools = [{ functionDeclarations: declarations }];
430
- if (req.toolChoice !== undefined)
431
- config.toolConfig = toToolConfig(req.toolChoice);
432
- const thinkingConfig = toThinkingConfig(req, profile);
433
- if (thinkingConfig)
434
- config.thinkingConfig = thinkingConfig;
435
- if (req.structuredOutput) {
436
- if (profile && !profile.structuredOutput) {
437
- throw unsupported(`Model "${req.model}" does not support structured output.`, {
438
- model: req.model,
439
- capability: 'structured_output',
440
- });
441
- }
442
- if (profile && !profile.structuredWithTools && declarations.length > 0) {
443
- throw unsupported(`Model "${req.model}" cannot combine structured output with function tools.`, {
444
- model: req.model,
445
- capability: 'structured_output',
446
- });
447
- }
448
- config.responseMimeType = 'application/json';
449
- config.responseJsonSchema = (0, structured_output_1.toJsonSchema)(req.structuredOutput.schema);
450
- }
451
- if (req.maxOutputTokens !== undefined)
452
- config.maxOutputTokens = req.maxOutputTokens;
453
- if (req.temperature !== undefined)
454
- config.temperature = req.temperature;
455
- const { abortSignal: _signal, ...escapeHatch } = (req.providerOptions?.[gemini_errors_1.GEMINI_PROVIDER_ID] ?? {});
456
- return { model: req.model, contents: conv.contents, config: { ...config, ...escapeHatch } };
457
- }
458
- // ---- response ------------------------------------------------------------------------
459
- function reasoningItem(summary, state) {
460
- if (!state)
461
- return { type: 'reasoning', summary };
462
- const providerState = { provider: gemini_errors_1.GEMINI_PROVIDER_ID, data: { ...state } };
463
- return { type: 'reasoning', summary, [responses_types_1.AI_PROVIDER_STATE]: providerState };
464
- }
465
- /**
466
- * Turns response parts into our output items, in order — incrementally, so
467
- * the stream mapper can emit deltas as parts arrive, and `create` feeds it a
468
- * whole response at once. Consecutive text parts are ONE message item and
469
- * consecutive thought parts ONE reasoning item (a stream splits one logical
470
- * part across many chunks); a function call closes whatever is open.
471
- */
472
- class GeminiOutputAssembler {
473
- items = [];
474
- open = null;
475
- pendingSignature;
476
- /** Consumes one part; returns the deltas and completed items it produced. */
477
- push(part) {
478
- const events = [];
479
- if (part.functionCall) {
480
- events.push(...this.close());
481
- events.push(...this.functionCall(part.functionCall, part.thoughtSignature));
482
- return events;
483
- }
484
- if (typeof part.text !== 'string') {
485
- // A signature-only part, or a part kind this adapter never asks for
486
- // (executable code, inline media output): only its signature matters.
487
- if (part.thoughtSignature)
488
- this.attachSignature(part.thoughtSignature);
489
- return events;
490
- }
491
- const kind = part.thought === true ? 'thought' : 'text';
492
- if (part.text.length === 0) {
493
- if (part.thoughtSignature)
494
- this.attachSignature(part.thoughtSignature);
495
- return events;
496
- }
497
- if (!this.open || this.open.kind !== kind) {
498
- events.push(...this.close());
499
- this.open = { kind, text: '' };
500
- if (this.pendingSignature) {
501
- this.open.signature = this.pendingSignature;
502
- this.pendingSignature = undefined;
503
- }
504
- }
505
- this.open.text += part.text;
506
- if (part.thoughtSignature)
507
- this.open.signature = part.thoughtSignature;
508
- events.push(kind === 'thought'
509
- ? { type: 'reasoning_summary.delta', delta: part.text }
510
- : { type: 'output_text.delta', delta: part.text });
511
- return events;
512
- }
513
- /** Closes whatever is open; call once, after the last part. */
514
- finish() {
515
- const events = this.close();
516
- if (this.pendingSignature) {
517
- events.push(this.done(reasoningItem([], { signature: this.pendingSignature, target: 'text' })));
518
- this.pendingSignature = undefined;
519
- }
520
- return events;
521
- }
522
- attachSignature(signature) {
523
- if (this.open && !this.open.signature) {
524
- this.open.signature = signature;
525
- }
526
- else {
527
- this.pendingSignature = signature;
528
- }
529
- }
530
- done(item) {
531
- this.items.push(item);
532
- return { type: 'output_item.done', item };
533
- }
534
- close() {
535
- const open = this.open;
536
- if (!open)
537
- return [];
538
- this.open = null;
539
- if (open.kind === 'thought') {
540
- return [
541
- this.done(reasoningItem(open.text.length > 0 ? [open.text] : [], open.signature ? { signature: open.signature, target: 'thought' } : undefined)),
542
- ];
543
- }
544
- const events = [this.done({ type: 'message', text: open.text })];
545
- if (open.signature)
546
- events.push(this.done(reasoningItem([], { signature: open.signature, target: 'text' })));
547
- return events;
548
- }
549
- functionCall(call, signature) {
550
- const callId = call.id && call.id.length > 0 ? call.id : `${exports.GEMINI_SYNTHETIC_CALL_ID_PREFIX}${(0, node_crypto_1.randomUUID)()}`;
551
- const args = JSON.stringify(call.args ?? {});
552
- const events = [];
553
- const sig = signature ?? this.pendingSignature;
554
- this.pendingSignature = undefined;
555
- if (sig)
556
- events.push(this.done(reasoningItem([], { signature: sig, target: 'function_call', callId })));
557
- events.push({ type: 'function_call.arguments.delta', callId, delta: args });
558
- events.push(this.done({ type: 'function_call', callId, name: call.name ?? '', arguments: args }));
559
- return events;
560
- }
561
- }
562
- exports.GeminiOutputAssembler = GeminiOutputAssembler;
563
- /** Usage, normalised: output includes thinking tokens, as OpenAI's and Anthropic's do. */
564
- function geminiUsage(usage) {
565
- if (!usage)
566
- return {};
567
- const out = {};
568
- const prompt = usage.promptTokenCount;
569
- const toolPrompt = usage.toolUsePromptTokenCount ?? 0;
570
- const candidates = usage.candidatesTokenCount;
571
- const thoughts = usage.thoughtsTokenCount;
572
- if (typeof prompt === 'number')
573
- out.inputTokens = prompt + toolPrompt;
574
- if (typeof candidates === 'number' || typeof thoughts === 'number') {
575
- out.outputTokens = (candidates ?? 0) + (thoughts ?? 0);
576
- }
577
- if (typeof thoughts === 'number')
578
- out.reasoningTokens = thoughts;
579
- if (typeof usage.cachedContentTokenCount === 'number')
580
- out.cachedInputTokens = usage.cachedContentTokenCount;
581
- return out;
582
- }
583
- const CONTENT_FILTER_REASONS = new Set([
584
- 'SAFETY',
585
- 'RECITATION',
586
- 'LANGUAGE',
587
- 'BLOCKLIST',
588
- 'PROHIBITED_CONTENT',
589
- 'SPII',
590
- 'IMAGE_SAFETY',
591
- 'IMAGE_PROHIBITED_CONTENT',
592
- 'IMAGE_RECITATION',
593
- ]);
594
- const ERROR_REASONS = new Set([
595
- 'MALFORMED_FUNCTION_CALL',
596
- 'UNEXPECTED_TOOL_CALL',
597
- 'TOO_MANY_TOOL_CALLS',
598
- 'OTHER',
599
- 'IMAGE_OTHER',
600
- 'NO_IMAGE',
601
- ]);
602
- /** Our finish reason for Gemini's (a candidate's `finishReason`, or a blocked prompt). */
603
- function geminiFinishReason(finishReason, output, promptBlocked = false) {
604
- if (promptBlocked)
605
- return 'content_filter';
606
- const reason = finishReason ?? '';
607
- if (reason === 'MAX_TOKENS')
608
- return 'length';
609
- if (CONTENT_FILTER_REASONS.has(reason))
610
- return 'content_filter';
611
- if (ERROR_REASONS.has(reason))
612
- return 'error';
613
- // STOP, unspecified, or not yet reported.
614
- return output.some((item) => item.type === 'function_call') ? 'tool_calls' : 'stop';
615
- }
616
- /**
617
- * The `AiResponse` for mapped parts — shared by `create` and the stream. When
618
- * the request asked for structured output and the model finished its turn,
619
- * the text is validated with `parseStructured` (a mismatch throws
620
- * `AI_STRUCTURED_OUTPUT_INVALID`); a filtered answer to a structured request
621
- * throws `AI_CONTENT_FILTERED`, since there is nothing to parse.
622
- */
623
- function finishGeminiResponse(parts, request) {
624
- const outputText = parts.output.map((item) => (item.type === 'message' ? item.text : '')).join('');
625
- const finishReason = geminiFinishReason(parts.finishReason, parts.output, parts.promptBlocked);
626
- const result = {
627
- id: parts.id,
628
- provider: gemini_errors_1.GEMINI_PROVIDER_ID,
629
- model: parts.model,
630
- output: parts.output,
631
- outputText,
632
- usage: parts.usage,
633
- finishReason,
634
- };
635
- if (request.structuredOutput && finishReason !== 'tool_calls') {
636
- if (finishReason === 'content_filter') {
637
- throw new ai_error_1.AiError('AI_CONTENT_FILTERED', 'Gemini blocked the response to a structured-output request.', {
638
- details: { provider: gemini_errors_1.GEMINI_PROVIDER_ID },
639
- });
640
- }
641
- result.parsed = (0, structured_output_1.parseStructured)(request.structuredOutput.schema, outputText);
642
- }
643
- return result;
644
- }
645
- /** An id for a response Gemini did not name. */
646
- function geminiFallbackResponseId() {
647
- return `gemini-${(0, node_crypto_1.randomUUID)()}`;
648
- }
649
- /** Maps a finished `GenerateContentResponse` to an `AiResponse`. */
650
- function fromGeminiResponse(response, request) {
651
- const candidate = response.candidates?.[0];
652
- const assembler = new GeminiOutputAssembler();
653
- for (const part of candidate?.content?.parts ?? [])
654
- assembler.push(part);
655
- assembler.finish();
656
- return finishGeminiResponse({
657
- id: response.responseId || geminiFallbackResponseId(),
658
- model: response.modelVersion || request.model,
659
- output: assembler.items,
660
- finishReason: candidate?.finishReason,
661
- promptBlocked: !candidate && Boolean(response.promptFeedback?.blockReason),
662
- usage: geminiUsage(response.usageMetadata),
663
- }, request);
664
- }
665
- //# sourceMappingURL=gemini-content.mapper.js.map