@effect-uai/google 0.11.0 → 0.12.1

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 (63) hide show
  1. package/dist/Gemini.d.mts +8 -2
  2. package/dist/Gemini.d.mts.map +1 -1
  3. package/dist/Gemini.mjs +1 -1
  4. package/dist/Gemini.mjs.map +1 -1
  5. package/dist/GeminiEmbedding.d.mts +2 -3
  6. package/dist/GeminiEmbedding.d.mts.map +1 -1
  7. package/dist/GeminiEmbedding.mjs +1 -1
  8. package/dist/GeminiEmbedding.mjs.map +1 -1
  9. package/dist/GeminiSynthesizer.d.mts +14 -6
  10. package/dist/GeminiSynthesizer.d.mts.map +1 -1
  11. package/dist/GeminiSynthesizer.mjs +13 -12
  12. package/dist/GeminiSynthesizer.mjs.map +1 -1
  13. package/dist/GeminiSynthesizer.test.d.mts +1 -1
  14. package/dist/GeminiSynthesizer.test.mjs +1 -1
  15. package/dist/GeminiSynthesizer.test.mjs.map +1 -1
  16. package/dist/GeminiTools.d.mts +9 -5
  17. package/dist/GeminiTools.d.mts.map +1 -1
  18. package/dist/GeminiTools.mjs +1 -1
  19. package/dist/GeminiTools.mjs.map +1 -1
  20. package/dist/GoogleDeepResearch.d.mts +58 -7
  21. package/dist/GoogleDeepResearch.d.mts.map +1 -1
  22. package/dist/GoogleDeepResearch.mjs +77 -23
  23. package/dist/GoogleDeepResearch.mjs.map +1 -1
  24. package/dist/GoogleDeepResearch.test.d.mts +1 -0
  25. package/dist/GoogleDeepResearch.test.mjs +66 -0
  26. package/dist/GoogleDeepResearch.test.mjs.map +1 -0
  27. package/dist/LyriaGenerator.d.mts +0 -2
  28. package/dist/LyriaGenerator.d.mts.map +1 -1
  29. package/dist/LyriaGenerator.mjs +9 -6
  30. package/dist/LyriaGenerator.mjs.map +1 -1
  31. package/dist/LyriaGenerator.test.d.mts +1 -1
  32. package/dist/LyriaGenerator.test.mjs +153 -8
  33. package/dist/LyriaGenerator.test.mjs.map +1 -1
  34. package/dist/codec.d.mts +36 -11
  35. package/dist/codec.d.mts.map +1 -1
  36. package/dist/codec.mjs +27 -4
  37. package/dist/codec.mjs.map +1 -1
  38. package/dist/geminiSpeechCodec.d.mts +0 -1
  39. package/dist/geminiSpeechCodec.d.mts.map +1 -1
  40. package/dist/geminiSpeechCodec.mjs.map +1 -1
  41. package/dist/geminiSpeechCodec.test.d.mts +1 -1
  42. package/dist/geminiSpeechCodec.test.mjs +1 -1
  43. package/dist/geminiSpeechCodec.test.mjs.map +1 -1
  44. package/dist/geminiTools.test.d.mts +1 -1
  45. package/dist/geminiTools.test.mjs +44 -19
  46. package/dist/geminiTools.test.mjs.map +1 -1
  47. package/dist/{magic-string.es-CaWgF-3p.mjs → magic-string.es-CUAnjKYC.mjs} +14 -13
  48. package/dist/{magic-string.es-CaWgF-3p.mjs.map → magic-string.es-CUAnjKYC.mjs.map} +1 -1
  49. package/dist/models.d.mts.map +1 -1
  50. package/dist/{dist-CWoAuwS9.mjs → test.DNmyFkvJ-N3QqMRwZ.mjs} +257 -381
  51. package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +1 -0
  52. package/package.json +5 -5
  53. package/src/Gemini.ts +4 -7
  54. package/src/GeminiEmbedding.ts +36 -48
  55. package/src/GeminiSynthesizer.ts +20 -27
  56. package/src/GeminiTools.ts +4 -6
  57. package/src/GoogleDeepResearch.test.ts +80 -0
  58. package/src/GoogleDeepResearch.ts +127 -37
  59. package/src/LyriaGenerator.ts +19 -23
  60. package/src/codec.ts +98 -12
  61. package/src/geminiTools.test.ts +21 -3
  62. package/dist/dist-CWoAuwS9.mjs.map +0 -1
  63. /package/dist/{chunk-uyGKjUfl.mjs → rolldown-runtime-EAsCxrDo.mjs} +0 -0
@@ -1,13 +1,12 @@
1
1
  import { GoogleDeepResearchModel } from "./models.mjs";
2
- import { Context, Effect, Layer, Redacted, Schema } from "effect";
2
+ import { Context, Effect, Layer, Option, Redacted, Schema } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
4
  import { DeepResearch, DeepResearchServiceShape } from "@effect-uai/core/DeepResearch";
5
+ import * as Items from "@effect-uai/core/Items";
5
6
  import * as Job from "@effect-uai/core/Job";
6
7
  import { ResearchRequest, ResearchState } from "@effect-uai/core/Research";
7
-
8
- //#region src/GoogleDeepResearch.d.ts
9
8
  declare namespace GoogleDeepResearch_d_exports {
10
- export { Config, GoogleDeepResearch, GoogleDeepResearchService, GoogleResearchRequest, WireInteraction, jobStateOf, layer, make };
9
+ export { Config, GeminiResearchData, GoogleDeepResearch, GoogleDeepResearchService, GoogleResearchRequest, WireInteraction, jobStateOf, layer, make, researchDataOf };
11
10
  }
12
11
  /**
13
12
  * Gemini-typed research request. Narrows `model` to the deep-research agent ids.
@@ -17,7 +16,7 @@ type GoogleResearchRequest = Omit<ResearchRequest, "model"> & {
17
16
  readonly model?: GoogleDeepResearchModel;
18
17
  };
19
18
  type GoogleDeepResearchService = DeepResearchServiceShape<GoogleResearchRequest>;
20
- declare const GoogleDeepResearch_base: Context.ServiceClass<GoogleDeepResearch, "@betalyra/effect-uai/providers/google/GoogleDeepResearch", DeepResearchServiceShape<GoogleResearchRequest>>;
19
+ declare const GoogleDeepResearch_base: Context.ServiceClass<GoogleDeepResearch, "@betalyra/effect-uai/providers/google/GoogleDeepResearch", GoogleDeepResearchService>;
21
20
  /**
22
21
  * Provider-typed tag for Gemini deep research over the Interactions API
23
22
  * (`POST /v1beta/interactions`, `agent: deep-research-*`). Yield this for the
@@ -32,7 +31,8 @@ declare const GoogleDeepResearch_base: Context.ServiceClass<GoogleDeepResearch,
32
31
  declare class GoogleDeepResearch extends GoogleDeepResearch_base {}
33
32
  type Config = {
34
33
  readonly apiKey: Redacted.Redacted;
35
- readonly baseUrl?: string; /** Poll cadence / overall timeout for the derived poll loops. */
34
+ readonly baseUrl?: string;
35
+ /** Poll cadence / overall timeout for the derived poll loops. */
36
36
  readonly job?: Job.JobConfig;
37
37
  };
38
38
  declare const WireInteraction: Schema.Struct<{
@@ -41,6 +41,14 @@ declare const WireInteraction: Schema.Struct<{
41
41
  readonly outputs: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
42
42
  readonly type: Schema.optional<Schema.NullOr<Schema.String>>;
43
43
  readonly text: Schema.optional<Schema.NullOr<Schema.String>>;
44
+ readonly groundingMetadata: Schema.optional<Schema.NullOr<Schema.Struct<{
45
+ readonly groundingChunks: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
46
+ readonly web: Schema.optional<Schema.NullOr<Schema.Struct<{
47
+ readonly uri: Schema.optional<Schema.NullOr<Schema.String>>;
48
+ readonly title: Schema.optional<Schema.NullOr<Schema.String>>;
49
+ }>>>;
50
+ }>>>>;
51
+ }>>>;
44
52
  }>>>>;
45
53
  readonly steps: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
46
54
  readonly type: Schema.optional<Schema.NullOr<Schema.String>>;
@@ -48,14 +56,57 @@ declare const WireInteraction: Schema.Struct<{
48
56
  readonly content: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
49
57
  readonly type: Schema.optional<Schema.NullOr<Schema.String>>;
50
58
  readonly text: Schema.optional<Schema.NullOr<Schema.String>>;
59
+ readonly groundingMetadata: Schema.optional<Schema.NullOr<Schema.Struct<{
60
+ readonly groundingChunks: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
61
+ readonly web: Schema.optional<Schema.NullOr<Schema.Struct<{
62
+ readonly uri: Schema.optional<Schema.NullOr<Schema.String>>;
63
+ readonly title: Schema.optional<Schema.NullOr<Schema.String>>;
64
+ }>>>;
65
+ }>>>>;
66
+ }>>>;
51
67
  }>>>>;
68
+ readonly groundingMetadata: Schema.optional<Schema.NullOr<Schema.Struct<{
69
+ readonly groundingChunks: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
70
+ readonly web: Schema.optional<Schema.NullOr<Schema.Struct<{
71
+ readonly uri: Schema.optional<Schema.NullOr<Schema.String>>;
72
+ readonly title: Schema.optional<Schema.NullOr<Schema.String>>;
73
+ }>>>;
74
+ }>>>>;
75
+ }>>>;
52
76
  }>>>>;
77
+ readonly groundingMetadata: Schema.optional<Schema.NullOr<Schema.Struct<{
78
+ readonly groundingChunks: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
79
+ readonly web: Schema.optional<Schema.NullOr<Schema.Struct<{
80
+ readonly uri: Schema.optional<Schema.NullOr<Schema.String>>;
81
+ readonly title: Schema.optional<Schema.NullOr<Schema.String>>;
82
+ }>>>;
83
+ }>>>>;
84
+ }>>>;
53
85
  readonly error: Schema.optional<Schema.NullOr<Schema.Struct<{
54
86
  readonly message: Schema.optional<Schema.NullOr<Schema.String>>;
55
87
  readonly code: Schema.optional<Schema.NullOr<Schema.String>>;
56
88
  }>>>;
57
89
  }>;
58
90
  type WireInteraction = typeof WireInteraction.Type;
91
+ /**
92
+ * The research trace: what the model did at each step and which sources it
93
+ * consulted there. The `Turn` keeps only the final report and the deduped
94
+ * union of all sources, so the per-step breakdown lives here or nowhere.
95
+ *
96
+ * Lands on `providerData.gemini`; read it with {@link researchDataOf}.
97
+ */
98
+ declare const GeminiResearchData: Schema.Struct<{
99
+ readonly steps: Schema.$Array<Schema.Struct<{
100
+ readonly text: Schema.String;
101
+ readonly sources: Schema.$Array<Schema.Struct<{
102
+ readonly url: Schema.String;
103
+ readonly title: Schema.optional<Schema.String>;
104
+ }>>;
105
+ }>>;
106
+ }>;
107
+ type GeminiResearchData = typeof GeminiResearchData.Type;
108
+ /** Read this provider's data off an item, if it is there and ours. */
109
+ declare const researchDataOf: (item: Items.HistoryItem) => Option.Option<GeminiResearchData>;
59
110
  declare const jobStateOf: (wire: WireInteraction) => ResearchState;
60
111
  /** Build a `GoogleDeepResearchService`. For Layer setup, prefer {@link layer}. */
61
112
  declare const make: (cfg: Config) => Effect.Effect<GoogleDeepResearchService, never, HttpClient.HttpClient>;
@@ -66,5 +117,5 @@ declare const make: (cfg: Config) => Effect.Effect<GoogleDeepResearchService, ne
66
117
  */
67
118
  declare const layer: (cfg: Config) => Layer.Layer<GoogleDeepResearch | DeepResearch, never, HttpClient.HttpClient>;
68
119
  //#endregion
69
- export { Config, GoogleDeepResearch, GoogleDeepResearchService, GoogleResearchRequest, WireInteraction, jobStateOf, layer, make, GoogleDeepResearch_d_exports as t };
120
+ export { Config, GeminiResearchData, GoogleDeepResearch, GoogleDeepResearchService, GoogleResearchRequest, WireInteraction, jobStateOf, layer, make, researchDataOf, GoogleDeepResearch_d_exports as t };
70
121
  //# sourceMappingURL=GoogleDeepResearch.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleDeepResearch.d.mts","names":[],"sources":["../src/GoogleDeepResearch.ts"],"mappings":";;;;;;;;;;;;;;;KAyBY,qBAAA,GAAwB,IAAA,CAAK,eAAA;EAAA,SAC9B,KAAA,GAAQ,uBAAA;AAAA;AAAA,KAGP,yBAAA,GAA4B,wBAAA,CAAyB,qBAAA;AAAA,cAAsB,uBAAA;;;;;AAJvF;;;;;;;cAiBa,kBAAA,SAA2B,uBAAA;AAAA,KAK5B,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA,WAvB+B;EAAA,SAyB/B,GAAA,GAAM,GAAA,CAAI,SAAA;AAAA;AAAA,cAwBR,eAAA,EAAe,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;KAchB,eAAA,UAAyB,eAAA,CAAgB,IAAA;AAAA,cA+BxC,UAAA,GAAc,IAAA,EAAM,eAAA,KAAkB,aAAA;;cAyStC,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,yBAAA,SAAkC,UAAA,CAAW,UAAA;;AApX9D;;;;cA8Xa,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,kBAAA,GAAqB,YAAA,SAAqB,UAAA,CAAW,UAAA"}
1
+ {"version":3,"file":"GoogleDeepResearch.d.mts","names":[],"sources":["../src/GoogleDeepResearch.ts"],"mappings":";;;;;;;;;;;;;;KAwCY,wBAAwB,KAAK;WAC9B,QAAQ;;KAGP,4BAA4B,yBAAyB;;;;;;;;;;;;;cAapD,2BAA2B;KAK5B;WACD,QAAQ,SAAS;WACjB;;WAEA,MAAM,IAAI;;cA4BR,iBAAe,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAehB,yBAAyB,gBAAgB;;;;;;;;cAkCxC,oBAAkB,OAAA;;;;;;;;;KAQnB,4BAA4B,mBAAmB;;cAK9C,iBAAc,MAAU,MAAM,gBAAc,OAAO,OAAO;cA4D1D,aAAU,MAAU,oBAAkB;;cAmStC,OAAI,KACV,WACJ,OAAO,OAAO,kCAAkC,WAAW;;;;;;cAUjD,QAAK,KACX,WACJ,MAAM,MAAM,qBAAqB,qBAAqB,WAAW"}
@@ -1,5 +1,6 @@
1
- import { n as __exportAll } from "./chunk-uyGKjUfl.mjs";
2
- import { Context, Effect, Layer, Match, Redacted, Ref, Schema, Stream } from "effect";
1
+ import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
2
+ import { GroundingMetadata, groundingToAnnotations } from "./codec.mjs";
3
+ import { Array, Context, Effect, Layer, Match, Option, Redacted, Ref, Result, Schema, Stream, pipe } from "effect";
3
4
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
4
5
  import * as AiError from "@effect-uai/core/AiError";
5
6
  import * as SSE from "@effect-uai/core/SSE";
@@ -9,11 +10,13 @@ import * as Items from "@effect-uai/core/Items";
9
10
  import * as Job from "@effect-uai/core/Job";
10
11
  //#region src/GoogleDeepResearch.ts
11
12
  var GoogleDeepResearch_exports = /* @__PURE__ */ __exportAll({
13
+ GeminiResearchData: () => GeminiResearchData,
12
14
  GoogleDeepResearch: () => GoogleDeepResearch,
13
15
  WireInteraction: () => WireInteraction,
14
16
  jobStateOf: () => jobStateOf,
15
17
  layer: () => layer,
16
- make: () => make
18
+ make: () => make,
19
+ researchDataOf: () => researchDataOf
17
20
  });
18
21
  /**
19
22
  * Provider-typed tag for Gemini deep research over the Interactions API
@@ -31,18 +34,21 @@ const WireSubmit = Schema.Struct({ id: Schema.String });
31
34
  const decodeSubmit = Schema.decodeUnknownEffect(WireSubmit);
32
35
  const WireTextPart = Schema.Struct({
33
36
  type: Schema.optional(Schema.NullOr(Schema.String)),
34
- text: Schema.optional(Schema.NullOr(Schema.String))
37
+ text: Schema.optional(Schema.NullOr(Schema.String)),
38
+ groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata))
35
39
  });
36
40
  const WireStep = Schema.Struct({
37
41
  type: Schema.optional(Schema.NullOr(Schema.String)),
38
42
  text: Schema.optional(Schema.NullOr(Schema.String)),
39
- content: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart)))
43
+ content: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),
44
+ groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata))
40
45
  });
41
46
  const WireInteraction = Schema.Struct({
42
47
  id: Schema.optional(Schema.NullOr(Schema.String)),
43
48
  status: Schema.optional(Schema.NullOr(Schema.String)),
44
49
  outputs: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),
45
50
  steps: Schema.optional(Schema.NullOr(Schema.Array(WireStep))),
51
+ groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata)),
46
52
  error: Schema.optional(Schema.NullOr(Schema.Struct({
47
53
  message: Schema.optional(Schema.NullOr(Schema.String)),
48
54
  code: Schema.optional(Schema.NullOr(Schema.String))
@@ -57,19 +63,62 @@ const reportText = (wire) => {
57
63
  const fromStep = textOfParts(lastStep?.content);
58
64
  return fromStep.length > 0 ? fromStep : lastStep?.text ?? "";
59
65
  };
60
- const turnFromInteraction = (wire) => ({
61
- items: [{
62
- type: "message",
63
- role: "assistant",
64
- content: [{
65
- type: "output_text",
66
- text: reportText(wire)
67
- }],
68
- providerData: wire
69
- }],
70
- usage: {},
71
- stop_reason: "stop"
66
+ /**
67
+ * The subset of the domain `Citation.Source` this provider can populate.
68
+ * Declared here rather than derived from the wire schema, so the exported
69
+ * shape is ours and a wire change cannot silently alter it.
70
+ */
71
+ const SourceSchema = Schema.Struct({
72
+ url: Schema.String,
73
+ title: Schema.optional(Schema.String)
74
+ });
75
+ /**
76
+ * The research trace: what the model did at each step and which sources it
77
+ * consulted there. The `Turn` keeps only the final report and the deduped
78
+ * union of all sources, so the per-step breakdown lives here or nowhere.
79
+ *
80
+ * Lands on `providerData.gemini`; read it with {@link researchDataOf}.
81
+ */
82
+ const GeminiResearchData = Schema.Struct({ steps: Schema.Array(Schema.Struct({
83
+ text: Schema.String,
84
+ sources: Schema.Array(SourceSchema)
85
+ })) });
86
+ const decodeResearchData = Schema.decodeUnknownResult(Schema.Struct({ gemini: GeminiResearchData }));
87
+ /** Read this provider's data off an item, if it is there and ours. */
88
+ const researchDataOf = (item) => pipe(decodeResearchData(item.providerData), Result.match({
89
+ onSuccess: (d) => Option.some(d.gemini),
90
+ onFailure: () => Option.none()
91
+ }));
92
+ const sourcesOf = (meta) => (meta?.groundingChunks ?? []).flatMap((chunk) => chunk.web?.uri == null ? [] : [{
93
+ url: chunk.web.uri,
94
+ ...chunk.web.title != null && { title: chunk.web.title }
95
+ }]);
96
+ const researchSteps = (wire) => (wire.steps ?? []).flatMap((step) => {
97
+ const text = textOfParts(step.content) || (step.text ?? "");
98
+ const sources = [...sourcesOf(step.groundingMetadata), ...(step.content ?? []).flatMap((p) => sourcesOf(p.groundingMetadata))];
99
+ return text.length === 0 && sources.length === 0 ? [] : [{
100
+ text,
101
+ sources
102
+ }];
72
103
  });
104
+ const interactionAnnotations = (wire) => groundingToAnnotations(wire.groundingMetadata, ...Array.map(wire.outputs ?? [], (o) => o.groundingMetadata), ...Array.map(wire.steps ?? [], (s) => s.groundingMetadata));
105
+ const turnFromInteraction = (wire) => {
106
+ const annotations = interactionAnnotations(wire);
107
+ return {
108
+ items: [{
109
+ type: "message",
110
+ role: "assistant",
111
+ content: [{
112
+ type: "output_text",
113
+ text: reportText(wire),
114
+ ...annotations.length > 0 && { annotations }
115
+ }],
116
+ providerData: { gemini: { steps: researchSteps(wire) } }
117
+ }],
118
+ usage: {},
119
+ stop_reason: "stop"
120
+ };
121
+ };
73
122
  const jobStateOf = (wire) => Match.value(wire.status ?? "").pipe(Match.whenOr("queued", "pending", () => ({ _tag: "Pending" })), Match.whenOr("in_progress", "running", "processing", () => ({ _tag: "Running" })), Match.whenOr("completed", "succeeded", () => ({
74
123
  _tag: "Succeeded",
75
124
  result: turnFromInteraction(wire)
@@ -144,7 +193,8 @@ const submitJob = (cfg, request) => Effect.gen(function* () {
144
193
  const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
145
194
  return yield* httpStatusError(response.status, text);
146
195
  }
147
- const wire = yield* decodeSubmit(yield* response.json.pipe(Effect.mapError(transportFailure))).pipe(Effect.mapError(transportFailure));
196
+ const json = yield* response.json.pipe(Effect.mapError(transportFailure));
197
+ const wire = yield* decodeSubmit(json).pipe(Effect.mapError(transportFailure));
148
198
  return Job.jobRef("google", wire.id);
149
199
  });
150
200
  const pollJob = (cfg, ref) => Effect.gen(function* () {
@@ -155,7 +205,10 @@ const pollJob = (cfg, ref) => Effect.gen(function* () {
155
205
  const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
156
206
  return yield* httpStatusError(response.status, text);
157
207
  }
158
- return jobStateOf(yield* decodeInteraction(yield* response.json.pipe(Effect.mapError(transportFailure))).pipe(Effect.mapError(transportFailure)));
208
+ const json = yield* response.json.pipe(Effect.mapError(transportFailure));
209
+ yield* Effect.logDebug("google interactions poll response", { interaction: json });
210
+ const wire = yield* decodeInteraction(json).pipe(Effect.mapError(transportFailure));
211
+ return jobStateOf(wire);
159
212
  });
160
213
  const unsupported = (capability) => new AiError.Unsupported({
161
214
  provider: "google",
@@ -186,7 +239,7 @@ const completedTurn = (wire, streamed) => wire != null && reportText(wire).lengt
186
239
  type: "output_text",
187
240
  text: streamed
188
241
  }],
189
- ...wire != null && { providerData: wire }
242
+ ...wire != null && { providerData: { gemini: { steps: researchSteps(wire) } } }
190
243
  }],
191
244
  usage: {},
192
245
  stop_reason: "stop"
@@ -256,14 +309,15 @@ const layer = (cfg) => {
256
309
  const typed = Layer.effect(GoogleDeepResearch, make(cfg));
257
310
  const generic = Layer.effect(DeepResearch, Effect.map(HttpClient.HttpClient, (client) => {
258
311
  const ops = jobOps(cfg, client);
259
- return buildService(cfg, client, {
312
+ const genericOps = {
260
313
  ...ops,
261
314
  submit: (request) => ops.submit(request)
262
- }, (request) => request);
315
+ };
316
+ return buildService(cfg, client, genericOps, (request) => request);
263
317
  }));
264
318
  return Layer.merge(typed, generic);
265
319
  };
266
320
  //#endregion
267
- export { GoogleDeepResearch, WireInteraction, jobStateOf, layer, make, GoogleDeepResearch_exports as t };
321
+ export { GeminiResearchData, GoogleDeepResearch, WireInteraction, jobStateOf, layer, make, researchDataOf, GoogleDeepResearch_exports as t };
268
322
 
269
323
  //# sourceMappingURL=GoogleDeepResearch.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleDeepResearch.mjs","names":[],"sources":["../src/GoogleDeepResearch.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Redacted, Ref, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport type { GoogleDeepResearchModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Gemini-typed research request. Narrows `model` to the deep-research agent ids.\n * Provider-portable code uses the generic `DeepResearch` tag instead.\n */\nexport type GoogleResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: GoogleDeepResearchModel\n}\n\nexport type GoogleDeepResearchService = DeepResearchServiceShape<GoogleResearchRequest>\n\n/**\n * Provider-typed tag for Gemini deep research over the Interactions API\n * (`POST /v1beta/interactions`, `agent: deep-research-*`). Yield this for the\n * agent-id knob; yield the generic `DeepResearch` tag for portable code.\n *\n * `research` / `submit` / `collect` drive a background job by polling;\n * `researchStream` consumes the real Interactions SSE (`step.delta` text /\n * thought, terminating in `interaction.completed`). Preview surface: the SSE\n * event shapes and the completed-interaction citation shape are decoded\n * defensively and best confirmed against a live response.\n */\nexport class GoogleDeepResearch extends Context.Service<\n GoogleDeepResearch,\n GoogleDeepResearchService\n>()(\"@betalyra/effect-uai/providers/google/GoogleDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst WireSubmit = Schema.Struct({ id: Schema.String })\nconst decodeSubmit = Schema.decodeUnknownEffect(WireSubmit)\n\n// A completed interaction's text lives at `outputs[].text` (SDK `outputs`) or,\n// in some doc examples, `steps[].content[].text`. Decode both defensively and\n// extract from whichever is populated. Citations are undocumented on this\n// preview surface, so none are mapped yet (the report text carries inline\n// `[n]` markers); enrich once a live response confirms the shape.\nconst WireTextPart = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n})\nconst WireStep = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n content: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),\n})\nexport const WireInteraction = Schema.Struct({\n id: Schema.optional(Schema.NullOr(Schema.String)),\n status: Schema.optional(Schema.NullOr(Schema.String)),\n outputs: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),\n steps: Schema.optional(Schema.NullOr(Schema.Array(WireStep))),\n error: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n message: Schema.optional(Schema.NullOr(Schema.String)),\n code: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nexport type WireInteraction = typeof WireInteraction.Type\nconst decodeInteraction = Schema.decodeUnknownEffect(WireInteraction)\n\nconst textOfParts = (parts: ReadonlyArray<typeof WireTextPart.Type> | null | undefined): string =>\n (parts ?? [])\n .filter((p) => (p.type ?? \"text\") === \"text\" && p.text != null)\n .map((p) => p.text)\n .join(\"\")\n\nconst reportText = (wire: WireInteraction): string => {\n const fromOutputs = textOfParts(wire.outputs)\n if (fromOutputs.length > 0) return fromOutputs\n const lastStep = (wire.steps ?? []).at(-1)\n const fromStep = textOfParts(lastStep?.content)\n return fromStep.length > 0 ? fromStep : (lastStep?.text ?? \"\")\n}\n\nconst turnFromInteraction = (wire: WireInteraction): Turn => ({\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: reportText(wire) }],\n providerData: wire,\n },\n ],\n usage: {},\n stop_reason: \"stop\",\n})\n\n// Map the interaction status onto the generic `JobState`.\nexport const jobStateOf = (wire: WireInteraction): ResearchState =>\n Match.value(wire.status ?? \"\").pipe(\n Match.whenOr(\"queued\", \"pending\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.whenOr(\n \"in_progress\",\n \"running\",\n \"processing\",\n (): ResearchState => ({ _tag: \"Running\" }),\n ),\n Match.whenOr(\n \"completed\",\n \"succeeded\",\n (): ResearchState => ({ _tag: \"Succeeded\", result: turnFromInteraction(wire) }),\n ),\n Match.whenOr(\n \"failed\",\n \"error\",\n \"cancelled\",\n (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error?.message != null && { reason: wire.error.message }),\n raw: wire,\n }),\n ),\n Match.orElse((): ResearchState => ({ _tag: \"Running\" })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string =>\n cfg.baseUrl ?? \"https://generativelanguage.googleapis.com/v1beta\"\n\nconst authHeader = (cfg: Config) =>\n HttpClientRequest.setHeader(\"x-goog-api-key\", Redacted.value(cfg.apiKey))\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"google\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"google\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n// Deep research takes one question. Concatenate the user turns' text into the\n// Interactions `input` string.\nconst inputText = (history: ReadonlyArray<Items.HistoryItem>): string =>\n history\n .filter(Items.isMessage)\n .filter((m) => m.role === \"user\")\n .flatMap((m) => m.content)\n .filter(Items.isInputText)\n .map((b) => b.text)\n .join(\"\\n\")\n\nconst submitBody = (request: GoogleResearchRequest): Record<string, unknown> => ({\n agent: request.model ?? \"deep-research-preview-04-2026\",\n input: inputText(request.history),\n background: true,\n store: true,\n agent_config: { type: \"deep-research\", thinking_summaries: \"auto\" },\n tools: [{ type: \"google_search\" }],\n})\n\nconst submitJob = (\n cfg: Config,\n request: GoogleResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/interactions`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe(submitBody(request)),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeSubmit(json).pipe(Effect.mapError(transportFailure))\n return Job.jobRef<Turn>(\"google\", wire.id)\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/interactions/${ref.id}`).pipe(\n authHeader(cfg),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeInteraction(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\n// No verified cancel endpoint on the preview Interactions surface.\nconst unsupported = (capability: string): AiError.Unsupported =>\n new AiError.Unsupported({\n provider: \"google\",\n capability,\n reason: \"Gemini deep research (preview) exposes no verified cancel endpoint.\",\n })\n\n// ---------------------------------------------------------------------------\n// Streaming (Interactions SSE)\n// ---------------------------------------------------------------------------\n\n// The Interactions stream emits `step.delta` events in two shapes: the report\n// body arrives as `delta: { type: \"text\", text }`, while a thinking summary\n// arrives as `delta: { type: \"thought_summary\", content: { text } }` (and a\n// content-free `thought_signature` we skip). Terminates in `interaction.completed`\n// / `interaction.error`, then an OpenAI-style `[DONE]` sentinel we drop upstream.\nconst WireDelta = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n content: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nconst WireStreamEvent = Schema.Struct({\n event_type: Schema.optional(Schema.NullOr(Schema.String)),\n delta: Schema.optional(Schema.NullOr(WireDelta)),\n interaction: Schema.optional(Schema.NullOr(WireInteraction)),\n error: Schema.optional(\n Schema.NullOr(Schema.Struct({ message: Schema.optional(Schema.NullOr(Schema.String)) })),\n ),\n})\ntype WireStreamEvent = typeof WireStreamEvent.Type\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\nconst decodeStreamEvent = Schema.decodeUnknownEffect(WireStreamEvent)\n\n// Prefer the completed interaction's decoded text; when the terminal event is\n// bare (or its text decodes empty) fall back to the accumulated stream deltas,\n// so `TurnComplete.turn` always carries the report that streamed.\nconst completedTurn = (wire: WireInteraction | null | undefined, streamed: string): Turn =>\n wire != null && reportText(wire).length > 0\n ? turnFromInteraction(wire)\n : {\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: streamed }],\n ...(wire != null && { providerData: wire }),\n },\n ],\n usage: {},\n stop_reason: \"stop\",\n }\n\n// Map one wire event onto `TurnEvent`s, appending text deltas to `acc` (the\n// `completedTurn` fallback) and lifting `interaction.error` to a typed failure.\nconst streamEventToTurnEvents = (\n acc: Ref.Ref<string>,\n ev: WireStreamEvent,\n): Effect.Effect<ReadonlyArray<TurnEvent>, AiError.AiError> => {\n if (ev.event_type === \"interaction.error\") {\n return Effect.fail(\n new AiError.GenerationFailed({\n provider: \"google\",\n ...(ev.error?.message != null && { message: ev.error.message }),\n raw: ev,\n }),\n )\n }\n if (ev.event_type === \"step.delta\") {\n const delta = ev.delta\n // Report body: `delta.text`. Accumulate it so the terminal `TurnComplete`\n // carries the report even when `interaction.completed` arrives text-free.\n if (delta?.text != null) {\n const text = delta.text\n return Effect.as(\n Ref.update(acc, (streamed) => streamed + text),\n [TurnEvent.TextDelta({ text })],\n )\n }\n // Thinking summary: nested under `delta.content.text`. Surfaced as reasoning,\n // not accumulated into the report.\n if (delta?.content?.text != null) {\n return Effect.succeed([\n TurnEvent.ReasoningDelta({ text: delta.content.text, kind: \"summary\" }),\n ])\n }\n return Effect.succeed([])\n }\n if (ev.event_type === \"interaction.completed\") {\n return Effect.map(Ref.get(acc), (streamed) => [\n TurnEvent.TurnComplete({ turn: completedTurn(ev.interaction, streamed) }),\n ])\n }\n return Effect.succeed([])\n}\n\n// A streaming interaction create (`?stream=true`): consume the SSE directly,\n// mapping each event onto `TurnEvent`s via {@link streamEventToTurnEvents}.\nconst streamCreate = (\n cfg: Config,\n request: GoogleResearchRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const acc = yield* Ref.make(\"\")\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/interactions?stream=true`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe({ ...submitBody(request), stream: true }),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n // Preview surface: log the response status + content-type so an\n // unexpected `application/json` (i.e. `?stream=true` not triggering SSE)\n // is visible at LOG_LEVEL=Debug rather than presenting as a silent hang.\n yield* Effect.logDebug(\"google interactions stream response\", {\n status: response.status,\n contentType: response.headers[\"content-type\"],\n })\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n SSE.fromBytes,\n // Log each raw frame before decode: the ground truth for the (unverified)\n // event_type / delta shapes this codec keys on.\n Stream.tap((ev) =>\n Effect.logDebug(\"google interactions sse frame\", { event: ev.event, data: ev.data }),\n ),\n // The stream ends with an OpenAI-style `[DONE]` sentinel that is not JSON;\n // drop it (and any blank frame) before the JSON decode.\n Stream.filter((ev) => ev.event !== \"done\" && ev.data.trim() !== \"[DONE]\"),\n Stream.mapEffect((ev) =>\n parseJsonUnknown(ev.data).pipe(\n Effect.flatMap(decodeStreamEvent),\n Effect.mapError(transportFailure),\n Effect.flatMap((wire) => streamEventToTurnEvents(acc, wire)),\n ),\n ),\n Stream.flatMap(Stream.fromIterable),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<GoogleResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: () => Effect.fail(unsupported(\"cancel\")),\n }\n}\n\n// fromJob's default surface, with `researchStream` overridden to the real\n// Interactions SSE (its submit-then-attach default only synthesizes progress).\nconst buildService = <Req extends ResearchRequest>(\n cfg: Config,\n client: HttpClient.HttpClient,\n ops: ResearchJobOps<Req>,\n toGoogle: (request: Req) => GoogleResearchRequest,\n): DeepResearchServiceShape<Req> => ({\n ...fromJob(ops, cfg.job),\n researchStream: (request) =>\n Stream.provideService(streamCreate(cfg, toGoogle(request)), HttpClient.HttpClient, client),\n})\n\n/** Build a `GoogleDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<GoogleDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) =>\n buildService(cfg, client, jobOps(cfg, client), (request) => request),\n )\n\n/**\n * Layer registering the provider-typed `GoogleDeepResearch` tag and the generic\n * `DeepResearch` tag. No capability markers: the surface (poll, collect,\n * synthesized stream) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<GoogleDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(GoogleDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n const genericOps: ResearchJobOps<ResearchRequest> = {\n ...ops,\n submit: (request) => ops.submit(request as GoogleResearchRequest),\n }\n return buildService(cfg, client, genericOps, (request) => request as GoogleResearchRequest)\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"GoogleDeepResearch.mjs","names":["Arr"],"sources":["../src/GoogleDeepResearch.ts"],"sourcesContent":["import {\n Array as Arr,\n Context,\n Effect,\n Layer,\n Match,\n Option,\n Redacted,\n Ref,\n Result,\n Schema,\n Stream,\n pipe,\n} from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { Source } from \"@effect-uai/core/Citation\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport { type Turn, TurnEvent } from \"@effect-uai/core/Turn\"\nimport { GroundingMetadata, groundingToAnnotations } from \"./codec.js\"\nimport type { GoogleDeepResearchModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Gemini-typed research request. Narrows `model` to the deep-research agent ids.\n * Provider-portable code uses the generic `DeepResearch` tag instead.\n */\nexport type GoogleResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: GoogleDeepResearchModel\n}\n\nexport type GoogleDeepResearchService = DeepResearchServiceShape<GoogleResearchRequest>\n\n/**\n * Provider-typed tag for Gemini deep research over the Interactions API\n * (`POST /v1beta/interactions`, `agent: deep-research-*`). Yield this for the\n * agent-id knob; yield the generic `DeepResearch` tag for portable code.\n *\n * `research` / `submit` / `collect` drive a background job by polling;\n * `researchStream` consumes the real Interactions SSE (`step.delta` text /\n * thought, terminating in `interaction.completed`). Preview surface: the SSE\n * event shapes and the completed-interaction citation shape are decoded\n * defensively and best confirmed against a live response.\n */\nexport class GoogleDeepResearch extends Context.Service<\n GoogleDeepResearch,\n GoogleDeepResearchService\n>()(\"@betalyra/effect-uai/providers/google/GoogleDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst WireSubmit = Schema.Struct({ id: Schema.String })\nconst decodeSubmit = Schema.decodeUnknownEffect(WireSubmit)\n\n// A completed interaction's text lives at `outputs[].text` (SDK `outputs`) or,\n// in some doc examples, `steps[].content[].text`. Decode both defensively and\n// extract from whichever is populated. Grounding sources ride Google's\n// `groundingMetadata.groundingChunks[]`; this preview surface is undocumented,\n// so decode it at every plausible level (interaction / output / step) and map\n// whichever is populated onto `url_citation` annotations. The report text also\n// carries the sources as an inline `[n]` / `**Sources:**` list.\nconst WireTextPart = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata)),\n})\nconst WireStep = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n content: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),\n groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata)),\n})\nexport const WireInteraction = Schema.Struct({\n id: Schema.optional(Schema.NullOr(Schema.String)),\n status: Schema.optional(Schema.NullOr(Schema.String)),\n outputs: Schema.optional(Schema.NullOr(Schema.Array(WireTextPart))),\n steps: Schema.optional(Schema.NullOr(Schema.Array(WireStep))),\n groundingMetadata: Schema.optional(Schema.NullOr(GroundingMetadata)),\n error: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n message: Schema.optional(Schema.NullOr(Schema.String)),\n code: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nexport type WireInteraction = typeof WireInteraction.Type\nconst decodeInteraction = Schema.decodeUnknownEffect(WireInteraction)\n\nconst textOfParts = (parts: ReadonlyArray<typeof WireTextPart.Type> | null | undefined): string =>\n (parts ?? [])\n .filter((p) => (p.type ?? \"text\") === \"text\" && p.text != null)\n .map((p) => p.text)\n .join(\"\")\n\nconst reportText = (wire: WireInteraction): string => {\n const fromOutputs = textOfParts(wire.outputs)\n if (fromOutputs.length > 0) return fromOutputs\n const lastStep = (wire.steps ?? []).at(-1)\n const fromStep = textOfParts(lastStep?.content)\n return fromStep.length > 0 ? fromStep : (lastStep?.text ?? \"\")\n}\n\n/**\n * The subset of the domain `Citation.Source` this provider can populate.\n * Declared here rather than derived from the wire schema, so the exported\n * shape is ours and a wire change cannot silently alter it.\n */\nconst SourceSchema = Schema.Struct({\n url: Schema.String,\n title: Schema.optional(Schema.String),\n})\n\n/**\n * The research trace: what the model did at each step and which sources it\n * consulted there. The `Turn` keeps only the final report and the deduped\n * union of all sources, so the per-step breakdown lives here or nowhere.\n *\n * Lands on `providerData.gemini`; read it with {@link researchDataOf}.\n */\nexport const GeminiResearchData = Schema.Struct({\n steps: Schema.Array(\n Schema.Struct({\n text: Schema.String,\n sources: Schema.Array(SourceSchema),\n }),\n ),\n})\nexport type GeminiResearchData = typeof GeminiResearchData.Type\n\nconst decodeResearchData = Schema.decodeUnknownResult(Schema.Struct({ gemini: GeminiResearchData }))\n\n/** Read this provider's data off an item, if it is there and ours. */\nexport const researchDataOf = (item: Items.HistoryItem): Option.Option<GeminiResearchData> =>\n pipe(\n decodeResearchData(item.providerData),\n Result.match({\n onSuccess: (d) => Option.some(d.gemini),\n onFailure: () => Option.none<GeminiResearchData>(),\n }),\n )\n\nconst sourcesOf = (meta: GroundingMetadata | null | undefined): ReadonlyArray<Source> =>\n (meta?.groundingChunks ?? []).flatMap((chunk) =>\n chunk.web?.uri == null\n ? []\n : [{ url: chunk.web.uri, ...(chunk.web.title != null && { title: chunk.web.title }) }],\n )\n\n// A step contributes to the trace if it said something or consulted something.\nconst researchSteps = (wire: WireInteraction): GeminiResearchData[\"steps\"] =>\n (wire.steps ?? []).flatMap((step) => {\n const text = textOfParts(step.content) || (step.text ?? \"\")\n const sources = [\n ...sourcesOf(step.groundingMetadata),\n ...(step.content ?? []).flatMap((p) => sourcesOf(p.groundingMetadata)),\n ]\n return text.length === 0 && sources.length === 0 ? [] : [{ text, sources }]\n })\n\n// Grounding can live at any level of the interaction envelope; gather it from\n// all three (interaction / output part / step) and let `groundingToAnnotations`\n// de-dupe by url.\nconst interactionAnnotations = (wire: WireInteraction): ReadonlyArray<Items.Annotation> =>\n groundingToAnnotations(\n wire.groundingMetadata,\n ...Arr.map(wire.outputs ?? [], (o) => o.groundingMetadata),\n ...Arr.map(wire.steps ?? [], (s) => s.groundingMetadata),\n )\n\nconst turnFromInteraction = (wire: WireInteraction): Turn => {\n const annotations = interactionAnnotations(wire)\n return {\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [\n {\n type: \"output_text\",\n text: reportText(wire),\n ...(annotations.length > 0 && { annotations }),\n },\n ],\n providerData: { gemini: { steps: researchSteps(wire) } },\n },\n ],\n usage: {},\n stop_reason: \"stop\",\n }\n}\n\n// Map the interaction status onto the generic `JobState`.\nexport const jobStateOf = (wire: WireInteraction): ResearchState =>\n Match.value(wire.status ?? \"\").pipe(\n Match.whenOr(\"queued\", \"pending\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.whenOr(\"in_progress\", \"running\", \"processing\", (): ResearchState => ({\n _tag: \"Running\",\n })),\n Match.whenOr(\"completed\", \"succeeded\", (): ResearchState => ({\n _tag: \"Succeeded\",\n result: turnFromInteraction(wire),\n })),\n Match.whenOr(\"failed\", \"error\", \"cancelled\", (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error?.message != null && { reason: wire.error.message }),\n raw: wire,\n })),\n Match.orElse((): ResearchState => ({ _tag: \"Running\" })),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string =>\n cfg.baseUrl ?? \"https://generativelanguage.googleapis.com/v1beta\"\n\nconst authHeader = (cfg: Config) =>\n HttpClientRequest.setHeader(\"x-goog-api-key\", Redacted.value(cfg.apiKey))\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"google\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"google\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n// Deep research takes one question. Concatenate the user turns' text into the\n// Interactions `input` string.\nconst inputText = (history: ReadonlyArray<Items.HistoryItem>): string =>\n history\n .filter(Items.isMessage)\n .filter((m) => m.role === \"user\")\n .flatMap((m) => m.content)\n .filter(Items.isInputText)\n .map((b) => b.text)\n .join(\"\\n\")\n\nconst submitBody = (request: GoogleResearchRequest): Record<string, unknown> => ({\n agent: request.model ?? \"deep-research-preview-04-2026\",\n input: inputText(request.history),\n background: true,\n store: true,\n agent_config: { type: \"deep-research\", thinking_summaries: \"auto\" },\n tools: [{ type: \"google_search\" }],\n})\n\nconst submitJob = (\n cfg: Config,\n request: GoogleResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/interactions`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe(submitBody(request)),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeSubmit(json).pipe(Effect.mapError(transportFailure))\n return Job.jobRef<Turn>(\"google\", wire.id)\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/interactions/${ref.id}`).pipe(\n authHeader(cfg),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n // Preview surface: log the raw interaction so the (undocumented) grounding\n // path can be confirmed against a live completed job at LOG_LEVEL=Debug.\n yield* Effect.logDebug(\"google interactions poll response\", { interaction: json })\n const wire = yield* decodeInteraction(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\n// No verified cancel endpoint on the preview Interactions surface.\nconst unsupported = (capability: string): AiError.Unsupported =>\n new AiError.Unsupported({\n provider: \"google\",\n capability,\n reason: \"Gemini deep research (preview) exposes no verified cancel endpoint.\",\n })\n\n// ---------------------------------------------------------------------------\n// Streaming (Interactions SSE)\n// ---------------------------------------------------------------------------\n\n// The Interactions stream emits `step.delta` events in two shapes: the report\n// body arrives as `delta: { type: \"text\", text }`, while a thinking summary\n// arrives as `delta: { type: \"thought_summary\", content: { text } }` (and a\n// content-free `thought_signature` we skip). Terminates in `interaction.completed`\n// / `interaction.error`, then an OpenAI-style `[DONE]` sentinel we drop upstream.\nconst WireDelta = Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n content: Schema.optional(\n Schema.NullOr(\n Schema.Struct({\n type: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n }),\n ),\n ),\n})\nconst WireStreamEvent = Schema.Struct({\n event_type: Schema.optional(Schema.NullOr(Schema.String)),\n delta: Schema.optional(Schema.NullOr(WireDelta)),\n interaction: Schema.optional(Schema.NullOr(WireInteraction)),\n error: Schema.optional(\n Schema.NullOr(Schema.Struct({ message: Schema.optional(Schema.NullOr(Schema.String)) })),\n ),\n})\ntype WireStreamEvent = typeof WireStreamEvent.Type\n\nconst parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))\nconst decodeStreamEvent = Schema.decodeUnknownEffect(WireStreamEvent)\n\n// Prefer the completed interaction's decoded text; when the terminal event is\n// bare (or its text decodes empty) fall back to the accumulated stream deltas,\n// so `TurnComplete.turn` always carries the report that streamed.\nconst completedTurn = (wire: WireInteraction | null | undefined, streamed: string): Turn =>\n wire != null && reportText(wire).length > 0\n ? turnFromInteraction(wire)\n : {\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: streamed }],\n ...(wire != null && { providerData: { gemini: { steps: researchSteps(wire) } } }),\n },\n ],\n usage: {},\n stop_reason: \"stop\",\n }\n\n// Map one wire event onto `TurnEvent`s, appending text deltas to `acc` (the\n// `completedTurn` fallback) and lifting `interaction.error` to a typed failure.\nconst streamEventToTurnEvents = (\n acc: Ref.Ref<string>,\n ev: WireStreamEvent,\n): Effect.Effect<ReadonlyArray<TurnEvent>, AiError.AiError> => {\n if (ev.event_type === \"interaction.error\") {\n return Effect.fail(\n new AiError.GenerationFailed({\n provider: \"google\",\n ...(ev.error?.message != null && { message: ev.error.message }),\n raw: ev,\n }),\n )\n }\n if (ev.event_type === \"step.delta\") {\n const delta = ev.delta\n // Report body: `delta.text`. Accumulate it so the terminal `TurnComplete`\n // carries the report even when `interaction.completed` arrives text-free.\n if (delta?.text != null) {\n const text = delta.text\n return Effect.as(\n Ref.update(acc, (streamed) => streamed + text),\n [TurnEvent.TextDelta({ text })],\n )\n }\n // Thinking summary: nested under `delta.content.text`. Surfaced as reasoning,\n // not accumulated into the report.\n if (delta?.content?.text != null) {\n return Effect.succeed([\n TurnEvent.ReasoningDelta({ text: delta.content.text, kind: \"summary\" }),\n ])\n }\n return Effect.succeed([])\n }\n if (ev.event_type === \"interaction.completed\") {\n return Effect.map(Ref.get(acc), (streamed) => [\n TurnEvent.TurnComplete({ turn: completedTurn(ev.interaction, streamed) }),\n ])\n }\n return Effect.succeed([])\n}\n\n// A streaming interaction create (`?stream=true`): consume the SSE directly,\n// mapping each event onto `TurnEvent`s via {@link streamEventToTurnEvents}.\nconst streamCreate = (\n cfg: Config,\n request: GoogleResearchRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const acc = yield* Ref.make(\"\")\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/interactions?stream=true`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe({ ...submitBody(request), stream: true }),\n HttpClientRequest.accept(\"text/event-stream\"),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n // Preview surface: log the response status + content-type so an\n // unexpected `application/json` (i.e. `?stream=true` not triggering SSE)\n // is visible at LOG_LEVEL=Debug rather than presenting as a silent hang.\n yield* Effect.logDebug(\"google interactions stream response\", {\n status: response.status,\n contentType: response.headers[\"content-type\"],\n })\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n SSE.fromBytes,\n // Log each raw frame before decode: the ground truth for the (unverified)\n // event_type / delta shapes this codec keys on.\n Stream.tap((ev) =>\n Effect.logDebug(\"google interactions sse frame\", { event: ev.event, data: ev.data }),\n ),\n // The stream ends with an OpenAI-style `[DONE]` sentinel that is not JSON;\n // drop it (and any blank frame) before the JSON decode.\n Stream.filter((ev) => ev.event !== \"done\" && ev.data.trim() !== \"[DONE]\"),\n Stream.mapEffect((ev) =>\n parseJsonUnknown(ev.data).pipe(\n Effect.flatMap(decodeStreamEvent),\n Effect.mapError(transportFailure),\n Effect.flatMap((wire) => streamEventToTurnEvents(acc, wire)),\n ),\n ),\n Stream.flatMap(Stream.fromIterable),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<GoogleResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: () => Effect.fail(unsupported(\"cancel\")),\n }\n}\n\n// fromJob's default surface, with `researchStream` overridden to the real\n// Interactions SSE (its submit-then-attach default only synthesizes progress).\nconst buildService = <Req extends ResearchRequest>(\n cfg: Config,\n client: HttpClient.HttpClient,\n ops: ResearchJobOps<Req>,\n toGoogle: (request: Req) => GoogleResearchRequest,\n): DeepResearchServiceShape<Req> => ({\n ...fromJob(ops, cfg.job),\n researchStream: (request) =>\n Stream.provideService(streamCreate(cfg, toGoogle(request)), HttpClient.HttpClient, client),\n})\n\n/** Build a `GoogleDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<GoogleDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) =>\n buildService(cfg, client, jobOps(cfg, client), (request) => request),\n )\n\n/**\n * Layer registering the provider-typed `GoogleDeepResearch` tag and the generic\n * `DeepResearch` tag. No capability markers: the surface (poll, collect,\n * synthesized stream) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<GoogleDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(GoogleDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n const genericOps: ResearchJobOps<ResearchRequest> = {\n ...ops,\n submit: (request) => ops.submit(request as GoogleResearchRequest),\n }\n return buildService(cfg, client, genericOps, (request) => request as GoogleResearchRequest)\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,66 @@
1
+ import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
2
+ import { WireInteraction, jobStateOf } from "./GoogleDeepResearch.mjs";
3
+ import { Schema } from "effect";
4
+ import * as Turn$1 from "@effect-uai/core/Turn";
5
+ //#region src/GoogleDeepResearch.test.ts
6
+ const decode = Schema.decodeUnknownSync(WireInteraction);
7
+ const turnOf = (raw) => {
8
+ const state = jobStateOf(decode(raw));
9
+ if (state._tag !== "Succeeded") throw new Error(`expected Succeeded, got ${state._tag}`);
10
+ return state.result;
11
+ };
12
+ describe("GoogleDeepResearch grounding", () => {
13
+ it("maps grounding chunks on a completed interaction to url_citation annotations", () => {
14
+ const turn = turnOf({
15
+ id: "int_1",
16
+ status: "completed",
17
+ outputs: [{
18
+ type: "text",
19
+ text: "Report body [1][2].",
20
+ groundingMetadata: { groundingChunks: [{ web: {
21
+ uri: "https://vertexaisearch.cloud.google.com/grounding-api-redirect/abc",
22
+ title: "example.com"
23
+ } }, { web: { uri: "https://vertexaisearch.cloud.google.com/grounding-api-redirect/def" } }] }
24
+ }]
25
+ });
26
+ globalExpect(Turn$1.assistantText(turn)).toBe("Report body [1][2].");
27
+ globalExpect(Turn$1.citations(turn)).toEqual([{
28
+ type: "url_citation",
29
+ url: "https://vertexaisearch.cloud.google.com/grounding-api-redirect/abc",
30
+ title: "example.com"
31
+ }, {
32
+ type: "url_citation",
33
+ url: "https://vertexaisearch.cloud.google.com/grounding-api-redirect/def",
34
+ title: "https://vertexaisearch.cloud.google.com/grounding-api-redirect/def"
35
+ }]);
36
+ });
37
+ it("gathers and de-dupes grounding across interaction / output / step levels", () => {
38
+ const turn = turnOf({
39
+ id: "int_2",
40
+ status: "completed",
41
+ groundingMetadata: { groundingChunks: [{ web: {
42
+ uri: "https://a.example",
43
+ title: "a"
44
+ } }] },
45
+ outputs: [{
46
+ type: "text",
47
+ text: "Body."
48
+ }],
49
+ steps: [{
50
+ type: "research",
51
+ groundingMetadata: { groundingChunks: [{ web: {
52
+ uri: "https://a.example",
53
+ title: "a"
54
+ } }, { web: {
55
+ uri: "https://b.example",
56
+ title: "b"
57
+ } }] }
58
+ }]
59
+ });
60
+ globalExpect(Turn$1.citations(turn).map((c) => c.type === "url_citation" && c.url)).toEqual(["https://a.example", "https://b.example"]);
61
+ });
62
+ });
63
+ //#endregion
64
+ export {};
65
+
66
+ //# sourceMappingURL=GoogleDeepResearch.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleDeepResearch.test.mjs","names":["Turn"],"sources":["../src/GoogleDeepResearch.test.ts"],"sourcesContent":["import { Schema } from \"effect\"\nimport * as Turn from \"@effect-uai/core/Turn\"\nimport { describe, expect, it } from \"vitest\"\nimport { WireInteraction, jobStateOf } from \"./GoogleDeepResearch.js\"\n\nconst decode = Schema.decodeUnknownSync(WireInteraction)\n\n// Extract the `Turn` from a completed interaction the way the service does.\nconst turnOf = (raw: unknown): Turn.Turn => {\n const state = jobStateOf(decode(raw))\n if (state._tag !== \"Succeeded\") throw new Error(`expected Succeeded, got ${state._tag}`)\n return state.result\n}\n\ndescribe(\"GoogleDeepResearch grounding\", () => {\n it(\"maps grounding chunks on a completed interaction to url_citation annotations\", () => {\n const turn = turnOf({\n id: \"int_1\",\n status: \"completed\",\n outputs: [\n {\n type: \"text\",\n text: \"Report body [1][2].\",\n groundingMetadata: {\n groundingChunks: [\n {\n web: {\n uri: \"https://vertexaisearch.cloud.google.com/grounding-api-redirect/abc\",\n title: \"example.com\",\n },\n },\n {\n web: { uri: \"https://vertexaisearch.cloud.google.com/grounding-api-redirect/def\" },\n },\n ],\n },\n },\n ],\n })\n expect(Turn.assistantText(turn)).toBe(\"Report body [1][2].\")\n expect(Turn.citations(turn)).toEqual([\n {\n type: \"url_citation\",\n url: \"https://vertexaisearch.cloud.google.com/grounding-api-redirect/abc\",\n title: \"example.com\",\n },\n {\n type: \"url_citation\",\n url: \"https://vertexaisearch.cloud.google.com/grounding-api-redirect/def\",\n title: \"https://vertexaisearch.cloud.google.com/grounding-api-redirect/def\",\n },\n ])\n })\n\n it(\"gathers and de-dupes grounding across interaction / output / step levels\", () => {\n const turn = turnOf({\n id: \"int_2\",\n status: \"completed\",\n groundingMetadata: {\n groundingChunks: [{ web: { uri: \"https://a.example\", title: \"a\" } }],\n },\n outputs: [{ type: \"text\", text: \"Body.\" }],\n steps: [\n {\n type: \"research\",\n groundingMetadata: {\n groundingChunks: [\n { web: { uri: \"https://a.example\", title: \"a\" } },\n { web: { uri: \"https://b.example\", title: \"b\" } },\n ],\n },\n },\n ],\n })\n expect(Turn.citations(turn).map((c) => c.type === \"url_citation\" && c.url)).toEqual([\n \"https://a.example\",\n \"https://b.example\",\n ])\n })\n})\n"],"mappings":";;;;;AAKA,MAAM,SAAS,OAAO,kBAAkB,eAAe;AAGvD,MAAM,UAAU,QAA4B;CAC1C,MAAM,QAAQ,WAAW,OAAO,GAAG,CAAC;CACpC,IAAI,MAAM,SAAS,aAAa,MAAM,IAAI,MAAM,2BAA2B,MAAM,MAAM;CACvF,OAAO,MAAM;AACf;AAEA,SAAS,sCAAsC;CAC7C,GAAG,sFAAsF;EACvF,MAAM,OAAO,OAAO;GAClB,IAAI;GACJ,QAAQ;GACR,SAAS,CACP;IACE,MAAM;IACN,MAAM;IACN,mBAAmB,EACjB,iBAAiB,CACf,EACE,KAAK;KACH,KAAK;KACL,OAAO;IACT,EACF,GACA,EACE,KAAK,EAAE,KAAK,qEAAqE,EACnF,CACF,EACF;GACF,CACF;EACF,CAAC;EACD,aAAOA,OAAK,cAAc,IAAI,CAAC,CAAC,CAAC,KAAK,qBAAqB;EAC3D,aAAOA,OAAK,UAAU,IAAI,CAAC,CAAC,CAAC,QAAQ,CACnC;GACE,MAAM;GACN,KAAK;GACL,OAAO;EACT,GACA;GACE,MAAM;GACN,KAAK;GACL,OAAO;EACT,CACF,CAAC;CACH,CAAC;CAED,GAAG,kFAAkF;EACnF,MAAM,OAAO,OAAO;GAClB,IAAI;GACJ,QAAQ;GACR,mBAAmB,EACjB,iBAAiB,CAAC,EAAE,KAAK;IAAE,KAAK;IAAqB,OAAO;GAAI,EAAE,CAAC,EACrE;GACA,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;GAAQ,CAAC;GACzC,OAAO,CACL;IACE,MAAM;IACN,mBAAmB,EACjB,iBAAiB,CACf,EAAE,KAAK;KAAE,KAAK;KAAqB,OAAO;IAAI,EAAE,GAChD,EAAE,KAAK;KAAE,KAAK;KAAqB,OAAO;IAAI,EAAE,CAClD,EACF;GACF,CACF;EACF,CAAC;EACD,aAAOA,OAAK,UAAU,IAAI,CAAC,CAAC,KAAK,MAAM,EAAE,SAAS,kBAAkB,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAClF,qBACA,mBACF,CAAC;CACH,CAAC;AACH,CAAC"}
@@ -5,8 +5,6 @@ import * as AiError from "@effect-uai/core/AiError";
5
5
  import { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, GenerateResult, MusicSessionInput, MusicStreamEvent } from "@effect-uai/core/Music";
6
6
  import { MusicGenerator } from "@effect-uai/core/MusicGenerator";
7
7
  import { AudioChunk, AudioFormat } from "@effect-uai/core/Audio";
8
-
9
- //#region src/LyriaGenerator.d.ts
10
8
  declare namespace LyriaGenerator_d_exports {
11
9
  export { Config, LyriaGenerateRequest, LyriaGenerator, LyriaGeneratorService, LyriaResponseMimeType, containerToMimeType, layer, make, realizedFormat };
12
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LyriaGenerator.d.mts","names":[],"sources":["../src/LyriaGenerator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;KA8CY,oBAAA,GAAuB,IAAA,CAAK,0BAAA;EAAA,SAC7B,KAAA,EAAO,UAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACD,QAAA,GACP,OAAA,EAAS,oBAAA,KACN,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA,CAAQ,OAAA;EAAA,SAClC,gBAAA,GACP,OAAA,EAAS,oBAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,CAAQ,OAAA;EAAA,SAC9B,oBAAA,SACP,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,GAC3C,OAAA,EAAS,gCAAA,KACN,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,CAAQ,OAAA,GAAU,CAAA,EAAG,CAAA;AAAA;AAAA,cAC3D,mBAAA;cAEY,cAAA,SAAuB,mBAAA;AAAA,KAIxB,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;AAAA;AAAA,KAOC,qBAAA;AAAA,cAEC,mBAAA,GACX,SAAA,EAAW,WAAA,kBACR,MAAA,CAAO,MAAA,CAAO,qBAAA,EAAuB,OAAA,CAAQ,OAAA;AAAA,cAyBrC,cAAA,GAAiB,IAAA,EAAM,qBAAA,KAA0B,WAAA;AAAA,cAyQjD,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,qBAAA,SAA8B,UAAA,CAAW,UAAA;;;;;;;;;;cAkB7C,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,cAAA,GAAiB,cAAA,SAAuB,UAAA,CAAW,UAAA"}
1
+ {"version":3,"file":"LyriaGenerator.d.mts","names":[],"sources":["../src/LyriaGenerator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;KA8CY,uBAAuB,KAAK;WAC7B,OAAO;;KAGN;WACD,WACP,SAAS,yBACN,OAAO,OAAO,gBAAgB,QAAQ;WAClC,mBACP,SAAS,yBACN,OAAO,OAAO,YAAY,QAAQ;WAC9B,uBAAuB,GAAG,GACjC,OAAO,OAAO,OAAO,mBAAmB,GAAG,IAC3C,SAAS,qCACN,OAAO,OAAO,kBAAkB,QAAQ,UAAU,GAAG;;;cAG/C,uBAAuB;KAIxB;WACD,QAAQ,SAAS;WACjB;;KAOC;cAEC,sBACX,WAAW,6BACR,OAAO,OAAO,uBAAuB,QAAQ;cAyBrC,iBAAiB,MAAM,0BAA0B;cAwQjD,OAAI,KACV,WACJ,OAAO,OAAO,8BAA8B,WAAW;;;;;;;;;;cAkB7C,QAAK,KACX,WACJ,MAAM,MAAM,iBAAiB,uBAAuB,WAAW"}
@@ -1,4 +1,4 @@
1
- import { n as __exportAll } from "./chunk-uyGKjUfl.mjs";
1
+ import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
2
2
  import { Context, Duration, Effect, Encoding, Layer, Match, Redacted, Result, Schema, Stream } from "effect";
3
3
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
@@ -132,7 +132,8 @@ const extractParts = (wire) => {
132
132
  };
133
133
  const generateImpl = (cfg) => (request) => Effect.gen(function* () {
134
134
  const client = yield* HttpClient.HttpClient;
135
- const requestedMime = yield* containerToMimeType(request.outputFormat?.container ?? "mp3");
135
+ const requestedContainer = request.outputFormat?.container ?? "mp3";
136
+ const requestedMime = yield* containerToMimeType(requestedContainer);
136
137
  const mimeType = isClipModel(request.model) ? "audio/mp3" : requestedMime;
137
138
  yield* warnDroppedHints(request);
138
139
  const body = isClipModel(request.model) ? { contents: [{ parts: [{ text: request.prompt }] }] } : {
@@ -149,10 +150,11 @@ const generateImpl = (cfg) => (request) => Effect.gen(function* () {
149
150
  return yield* httpStatusError(response.status, text);
150
151
  }
151
152
  const json = yield* response.json.pipe(Effect.mapError(transportFailure));
152
- const { inline, text } = extractParts(yield* decodeWire(json).pipe(Effect.mapError((cause) => new AiError.GenerationFailed({
153
+ const wire = yield* decodeWire(json).pipe(Effect.mapError((cause) => new AiError.GenerationFailed({
153
154
  provider: "lyria",
154
155
  raw: cause
155
- }))));
156
+ })));
157
+ const { inline, text } = extractParts(wire);
156
158
  if (inline === void 0) return yield* new AiError.GenerationFailed({
157
159
  provider: "lyria",
158
160
  raw: {
@@ -163,7 +165,7 @@ const generateImpl = (cfg) => (request) => Effect.gen(function* () {
163
165
  }
164
166
  });
165
167
  const bytes = yield* decodeBase64ToBytes(inline.data);
166
- return singleVariant({
168
+ const result = {
167
169
  audio: {
168
170
  format: realizedFormat(mimeType),
169
171
  bytes
@@ -171,7 +173,8 @@ const generateImpl = (cfg) => (request) => Effect.gen(function* () {
171
173
  provider: "lyria",
172
174
  ...text !== void 0 && { lyrics: text },
173
175
  watermark: "synthid"
174
- });
176
+ };
177
+ return singleVariant(result);
175
178
  });
176
179
  const streamGenerationImpl = (cfg) => (request) => Stream.unwrap(Effect.map(generateImpl(cfg)(request), (result) => Stream.succeed({ bytes: result.primary.audio.bytes })));
177
180
  /**