@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.
- package/dist/Gemini.d.mts +8 -2
- package/dist/Gemini.d.mts.map +1 -1
- package/dist/Gemini.mjs +1 -1
- package/dist/Gemini.mjs.map +1 -1
- package/dist/GeminiEmbedding.d.mts +2 -3
- package/dist/GeminiEmbedding.d.mts.map +1 -1
- package/dist/GeminiEmbedding.mjs +1 -1
- package/dist/GeminiEmbedding.mjs.map +1 -1
- package/dist/GeminiSynthesizer.d.mts +14 -6
- package/dist/GeminiSynthesizer.d.mts.map +1 -1
- package/dist/GeminiSynthesizer.mjs +13 -12
- package/dist/GeminiSynthesizer.mjs.map +1 -1
- package/dist/GeminiSynthesizer.test.d.mts +1 -1
- package/dist/GeminiSynthesizer.test.mjs +1 -1
- package/dist/GeminiSynthesizer.test.mjs.map +1 -1
- package/dist/GeminiTools.d.mts +9 -5
- package/dist/GeminiTools.d.mts.map +1 -1
- package/dist/GeminiTools.mjs +1 -1
- package/dist/GeminiTools.mjs.map +1 -1
- package/dist/GoogleDeepResearch.d.mts +58 -7
- package/dist/GoogleDeepResearch.d.mts.map +1 -1
- package/dist/GoogleDeepResearch.mjs +77 -23
- package/dist/GoogleDeepResearch.mjs.map +1 -1
- package/dist/GoogleDeepResearch.test.d.mts +1 -0
- package/dist/GoogleDeepResearch.test.mjs +66 -0
- package/dist/GoogleDeepResearch.test.mjs.map +1 -0
- package/dist/LyriaGenerator.d.mts +0 -2
- package/dist/LyriaGenerator.d.mts.map +1 -1
- package/dist/LyriaGenerator.mjs +9 -6
- package/dist/LyriaGenerator.mjs.map +1 -1
- package/dist/LyriaGenerator.test.d.mts +1 -1
- package/dist/LyriaGenerator.test.mjs +153 -8
- package/dist/LyriaGenerator.test.mjs.map +1 -1
- package/dist/codec.d.mts +36 -11
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +27 -4
- package/dist/codec.mjs.map +1 -1
- package/dist/geminiSpeechCodec.d.mts +0 -1
- package/dist/geminiSpeechCodec.d.mts.map +1 -1
- package/dist/geminiSpeechCodec.mjs.map +1 -1
- package/dist/geminiSpeechCodec.test.d.mts +1 -1
- package/dist/geminiSpeechCodec.test.mjs +1 -1
- package/dist/geminiSpeechCodec.test.mjs.map +1 -1
- package/dist/geminiTools.test.d.mts +1 -1
- package/dist/geminiTools.test.mjs +44 -19
- package/dist/geminiTools.test.mjs.map +1 -1
- package/dist/{magic-string.es-CaWgF-3p.mjs → magic-string.es-CUAnjKYC.mjs} +14 -13
- package/dist/{magic-string.es-CaWgF-3p.mjs.map → magic-string.es-CUAnjKYC.mjs.map} +1 -1
- package/dist/models.d.mts.map +1 -1
- package/dist/{dist-CWoAuwS9.mjs → test.DNmyFkvJ-N3QqMRwZ.mjs} +257 -381
- package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +1 -0
- package/package.json +5 -5
- package/src/Gemini.ts +4 -7
- package/src/GeminiEmbedding.ts +36 -48
- package/src/GeminiSynthesizer.ts +20 -27
- package/src/GeminiTools.ts +4 -6
- package/src/GoogleDeepResearch.test.ts +80 -0
- package/src/GoogleDeepResearch.ts +127 -37
- package/src/LyriaGenerator.ts +19 -23
- package/src/codec.ts +98 -12
- package/src/geminiTools.test.ts +21 -3
- package/dist/dist-CWoAuwS9.mjs.map +0 -1
- /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",
|
|
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;
|
|
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":"
|
|
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 "./
|
|
2
|
-
import {
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
312
|
+
const genericOps = {
|
|
260
313
|
...ops,
|
|
261
314
|
submit: (request) => ops.submit(request)
|
|
262
|
-
}
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,qBAAb,cAAwC,QAAQ,QAG9C,CAAC,CAAC,0DAA0D,CAAC,CAAC,CAAC;AAajE,MAAM,aAAa,OAAO,OAAO,EAAE,IAAI,OAAO,OAAO,CAAC;AACtD,MAAM,eAAe,OAAO,oBAAoB,UAAU;AAS1D,MAAM,eAAe,OAAO,OAAO;CACjC,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,mBAAmB,OAAO,SAAS,OAAO,OAAO,iBAAiB,CAAC;AACrE,CAAC;AACD,MAAM,WAAW,OAAO,OAAO;CAC7B,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,YAAY,CAAC,CAAC;CAClE,mBAAmB,OAAO,SAAS,OAAO,OAAO,iBAAiB,CAAC;AACrE,CAAC;AACD,MAAa,kBAAkB,OAAO,OAAO;CAC3C,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAChD,QAAQ,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACpD,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,YAAY,CAAC,CAAC;CAClE,OAAO,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;CAC5D,mBAAmB,OAAO,SAAS,OAAO,OAAO,iBAAiB,CAAC;CACnE,OAAO,OAAO,SACZ,OAAO,OACL,OAAO,OAAO;EACZ,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;EACrD,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACpD,CAAC,CACH,CACF;AACF,CAAC;AAED,MAAM,oBAAoB,OAAO,oBAAoB,eAAe;AAEpE,MAAM,eAAe,WAClB,SAAS,CAAC,EAAA,CACR,QAAQ,OAAO,EAAE,QAAQ,YAAY,UAAU,EAAE,QAAQ,IAAI,CAAC,CAC9D,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,KAAK,EAAE;AAEZ,MAAM,cAAc,SAAkC;CACpD,MAAM,cAAc,YAAY,KAAK,OAAO;CAC5C,IAAI,YAAY,SAAS,GAAG,OAAO;CACnC,MAAM,YAAY,KAAK,SAAS,CAAC,EAAA,CAAG,GAAG,EAAE;CACzC,MAAM,WAAW,YAAY,UAAU,OAAO;CAC9C,OAAO,SAAS,SAAS,IAAI,WAAY,UAAU,QAAQ;AAC7D;;;;;;AAOA,MAAM,eAAe,OAAO,OAAO;CACjC,KAAK,OAAO;CACZ,OAAO,OAAO,SAAS,OAAO,MAAM;AACtC,CAAC;;;;;;;;AASD,MAAa,qBAAqB,OAAO,OAAO,EAC9C,OAAO,OAAO,MACZ,OAAO,OAAO;CACZ,MAAM,OAAO;CACb,SAAS,OAAO,MAAM,YAAY;AACpC,CAAC,CACH,EACF,CAAC;AAGD,MAAM,qBAAqB,OAAO,oBAAoB,OAAO,OAAO,EAAE,QAAQ,mBAAmB,CAAC,CAAC;;AAGnG,MAAa,kBAAkB,SAC7B,KACE,mBAAmB,KAAK,YAAY,GACpC,OAAO,MAAM;CACX,YAAY,MAAM,OAAO,KAAK,EAAE,MAAM;CACtC,iBAAiB,OAAO,KAAyB;AACnD,CAAC,CACH;AAEF,MAAM,aAAa,UAChB,MAAM,mBAAmB,CAAC,EAAA,CAAG,SAAS,UACrC,MAAM,KAAK,OAAO,OACd,CAAC,IACD,CAAC;CAAE,KAAK,MAAM,IAAI;CAAK,GAAI,MAAM,IAAI,SAAS,QAAQ,EAAE,OAAO,MAAM,IAAI,MAAM;AAAG,CAAC,CACzF;AAGF,MAAM,iBAAiB,UACpB,KAAK,SAAS,CAAC,EAAA,CAAG,SAAS,SAAS;CACnC,MAAM,OAAO,YAAY,KAAK,OAAO,MAAM,KAAK,QAAQ;CACxD,MAAM,UAAU,CACd,GAAG,UAAU,KAAK,iBAAiB,GACnC,IAAI,KAAK,WAAW,CAAC,EAAA,CAAG,SAAS,MAAM,UAAU,EAAE,iBAAiB,CAAC,CACvE;CACA,OAAO,KAAK,WAAW,KAAK,QAAQ,WAAW,IAAI,CAAC,IAAI,CAAC;EAAE;EAAM;CAAQ,CAAC;AAC5E,CAAC;AAKH,MAAM,0BAA0B,SAC9B,uBACE,KAAK,mBACL,GAAGA,MAAI,IAAI,KAAK,WAAW,CAAC,IAAI,MAAM,EAAE,iBAAiB,GACzD,GAAGA,MAAI,IAAI,KAAK,SAAS,CAAC,IAAI,MAAM,EAAE,iBAAiB,CACzD;AAEF,MAAM,uBAAuB,SAAgC;CAC3D,MAAM,cAAc,uBAAuB,IAAI;CAC/C,OAAO;EACL,OAAO,CACL;GACE,MAAM;GACN,MAAM;GACN,SAAS,CACP;IACE,MAAM;IACN,MAAM,WAAW,IAAI;IACrB,GAAI,YAAY,SAAS,KAAK,EAAE,YAAY;GAC9C,CACF;GACA,cAAc,EAAE,QAAQ,EAAE,OAAO,cAAc,IAAI,EAAE,EAAE;EACzD,CACF;EACA,OAAO,CAAC;EACR,aAAa;CACf;AACF;AAGA,MAAa,cAAc,SACzB,MAAM,MAAM,KAAK,UAAU,EAAE,CAAC,CAAC,KAC7B,MAAM,OAAO,UAAU,kBAAiC,EAAE,MAAM,UAAU,EAAE,GAC5E,MAAM,OAAO,eAAe,WAAW,qBAAoC,EACzE,MAAM,UACR,EAAE,GACF,MAAM,OAAO,aAAa,oBAAmC;CAC3D,MAAM;CACN,QAAQ,oBAAoB,IAAI;AAClC,EAAE,GACF,MAAM,OAAO,UAAU,SAAS,oBAAmC;CACjE,MAAM;CACN,GAAI,KAAK,OAAO,WAAW,QAAQ,EAAE,QAAQ,KAAK,MAAM,QAAQ;CAChE,KAAK;AACP,EAAE,GACF,MAAM,cAA6B,EAAE,MAAM,UAAU,EAAE,CACzD;AAMF,MAAM,WAAW,QACf,IAAI,WAAW;AAEjB,MAAM,cAAc,QAClB,kBAAkB,UAAU,kBAAkB,SAAS,MAAM,IAAI,MAAM,CAAC;AAE1E,MAAM,oBAAoB,UACxB,IAAI,QAAQ,YAAY;CAAE,UAAU;CAAU,KAAK;AAAM,CAAC;AAE5D,MAAM,mBAAmB,QAAgB,SAAkC;CACzE,MAAM,WAAW;CACjB,MAAM,MAAM;CACZ,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,YAAY;EAAE;EAAU;CAAI,CAAC;CACpE,IAAI,WAAW,OAAO,WAAW,KAAK,OAAO,IAAI,QAAQ,QAAQ;EAAE;EAAU;CAAI,CAAC;CAClF,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,WAAW;EAAE;EAAU,SAAS;EAAQ;CAAI,CAAC;CACpF,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,WAAW;EAAE;EAAU,SAAS;EAAc;CAAI,CAAC;CAC1F,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,WAAW;EAAE;EAAU,SAAS;EAAW;CAAI,CAAC;CACvF,IAAI,WAAW,KAAK,OAAO,IAAI,QAAQ,sBAAsB;EAAE;EAAU;CAAI,CAAC;CAC9E,IAAI,UAAU,KAAK,OAAO,IAAI,QAAQ,YAAY;EAAE;EAAU;EAAQ;CAAI,CAAC;CAC3E,OAAO,IAAI,QAAQ,eAAe;EAAE;EAAU;CAAI,CAAC;AACrD;AAIA,MAAM,aAAa,YACjB,QACG,OAAO,MAAM,SAAS,CAAC,CACvB,QAAQ,MAAM,EAAE,SAAS,MAAM,CAAC,CAChC,SAAS,MAAM,EAAE,OAAO,CAAC,CACzB,OAAO,MAAM,WAAW,CAAC,CACzB,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,KAAK,IAAI;AAEd,MAAM,cAAc,aAA6D;CAC/E,OAAO,QAAQ,SAAS;CACxB,OAAO,UAAU,QAAQ,OAAO;CAChC,YAAY;CACZ,OAAO;CACP,cAAc;EAAE,MAAM;EAAiB,oBAAoB;CAAO;CAClE,OAAO,CAAC,EAAE,MAAM,gBAAgB,CAAC;AACnC;AAEA,MAAM,aACJ,KACA,YAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,cAAc,kBAAkB,KAAK,GAAG,QAAQ,GAAG,EAAE,cAAc,CAAC,CAAC,KACzE,WAAW,GAAG,GACd,kBAAkB,eAAe,WAAW,OAAO,CAAC,CACtD;CACA,MAAM,WAAW,OAAO,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAC1F,IAAI,SAAS,UAAU,KAAK;EAC1B,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,oBAAoB,EAAE,CAAC;EACrE,OAAO,OAAO,gBAAgB,SAAS,QAAQ,IAAI;CACrD;CACA,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,gBAAgB,CAAC;CACxE,MAAM,OAAO,OAAO,aAAa,IAAI,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAC7E,OAAO,IAAI,OAAa,UAAU,KAAK,EAAE;AAC3C,CAAC;AAEH,MAAM,WACJ,KACA,QAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,cAAc,kBAAkB,IAAI,GAAG,QAAQ,GAAG,EAAE,gBAAgB,IAAI,IAAI,CAAC,CAAC,KAClF,WAAW,GAAG,CAChB;CACA,MAAM,WAAW,OAAO,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAC1F,IAAI,SAAS,UAAU,KAAK;EAC1B,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,oBAAoB,EAAE,CAAC;EACrE,OAAO,OAAO,gBAAgB,SAAS,QAAQ,IAAI;CACrD;CACA,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAGxE,OAAO,OAAO,SAAS,qCAAqC,EAAE,aAAa,KAAK,CAAC;CACjF,MAAM,OAAO,OAAO,kBAAkB,IAAI,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAClF,OAAO,WAAW,IAAI;AACxB,CAAC;AAGH,MAAM,eAAe,eACnB,IAAI,QAAQ,YAAY;CACtB,UAAU;CACV;CACA,QAAQ;AACV,CAAC;AAWH,MAAM,YAAY,OAAO,OAAO;CAC9B,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CAClD,SAAS,OAAO,SACd,OAAO,OACL,OAAO,OAAO;EACZ,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;EAClD,MAAM,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACpD,CAAC,CACH,CACF;AACF,CAAC;AACD,MAAM,kBAAkB,OAAO,OAAO;CACpC,YAAY,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC;CACxD,OAAO,OAAO,SAAS,OAAO,OAAO,SAAS,CAAC;CAC/C,aAAa,OAAO,SAAS,OAAO,OAAO,eAAe,CAAC;CAC3D,OAAO,OAAO,SACZ,OAAO,OAAO,OAAO,OAAO,EAAE,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CACzF;AACF,CAAC;AAGD,MAAM,mBAAmB,OAAO,oBAAoB,OAAO,eAAe,OAAO,OAAO,CAAC;AACzF,MAAM,oBAAoB,OAAO,oBAAoB,eAAe;AAKpE,MAAM,iBAAiB,MAA0C,aAC/D,QAAQ,QAAQ,WAAW,IAAI,CAAC,CAAC,SAAS,IACtC,oBAAoB,IAAI,IACxB;CACE,OAAO,CACL;EACE,MAAM;EACN,MAAM;EACN,SAAS,CAAC;GAAE,MAAM;GAAe,MAAM;EAAS,CAAC;EACjD,GAAI,QAAQ,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,cAAc,IAAI,EAAE,EAAE,EAAE;CACjF,CACF;CACA,OAAO,CAAC;CACR,aAAa;AACf;AAIN,MAAM,2BACJ,KACA,OAC6D;CAC7D,IAAI,GAAG,eAAe,qBACpB,OAAO,OAAO,KACZ,IAAI,QAAQ,iBAAiB;EAC3B,UAAU;EACV,GAAI,GAAG,OAAO,WAAW,QAAQ,EAAE,SAAS,GAAG,MAAM,QAAQ;EAC7D,KAAK;CACP,CAAC,CACH;CAEF,IAAI,GAAG,eAAe,cAAc;EAClC,MAAM,QAAQ,GAAG;EAGjB,IAAI,OAAO,QAAQ,MAAM;GACvB,MAAM,OAAO,MAAM;GACnB,OAAO,OAAO,GACZ,IAAI,OAAO,MAAM,aAAa,WAAW,IAAI,GAC7C,CAAC,UAAU,UAAU,EAAE,KAAK,CAAC,CAAC,CAChC;EACF;EAGA,IAAI,OAAO,SAAS,QAAQ,MAC1B,OAAO,OAAO,QAAQ,CACpB,UAAU,eAAe;GAAE,MAAM,MAAM,QAAQ;GAAM,MAAM;EAAU,CAAC,CACxE,CAAC;EAEH,OAAO,OAAO,QAAQ,CAAC,CAAC;CAC1B;CACA,IAAI,GAAG,eAAe,yBACpB,OAAO,OAAO,IAAI,IAAI,IAAI,GAAG,IAAI,aAAa,CAC5C,UAAU,aAAa,EAAE,MAAM,cAAc,GAAG,aAAa,QAAQ,EAAE,CAAC,CAC1E,CAAC;CAEH,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC1B;AAIA,MAAM,gBACJ,KACA,YAEA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,MAAM,OAAO,IAAI,KAAK,EAAE;CAC9B,MAAM,cAAc,kBAAkB,KAAK,GAAG,QAAQ,GAAG,EAAE,0BAA0B,CAAC,CAAC,KACrF,WAAW,GAAG,GACd,kBAAkB,eAAe;EAAE,GAAG,WAAW,OAAO;EAAG,QAAQ;CAAK,CAAC,GACzE,kBAAkB,OAAO,mBAAmB,CAC9C;CACA,MAAM,WAAW,OAAO,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,OAAO,SAAS,gBAAgB,CAAC;CAI1F,OAAO,OAAO,SAAS,uCAAuC;EAC5D,QAAQ,SAAS;EACjB,aAAa,SAAS,QAAQ;CAChC,CAAC;CACD,IAAI,SAAS,UAAU,KAAK;EAC1B,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,oBAAoB,EAAE,CAAC;EACrE,OAAO,OAAO,KAAK,gBAAgB,SAAS,QAAQ,IAAI,CAAC;CAC3D;CACA,OAAO,SAAS,OAAO,KACrB,OAAO,SAAS,gBAAgB,GAChC,IAAI,WAGJ,OAAO,KAAK,OACV,OAAO,SAAS,iCAAiC;EAAE,OAAO,GAAG;EAAO,MAAM,GAAG;CAAK,CAAC,CACrF,GAGA,OAAO,QAAQ,OAAO,GAAG,UAAU,UAAU,GAAG,KAAK,KAAK,MAAM,QAAQ,GACxE,OAAO,WAAW,OAChB,iBAAiB,GAAG,IAAI,CAAC,CAAC,KACxB,OAAO,QAAQ,iBAAiB,GAChC,OAAO,SAAS,gBAAgB,GAChC,OAAO,SAAS,SAAS,wBAAwB,KAAK,IAAI,CAAC,CAC7D,CACF,GACA,OAAO,QAAQ,OAAO,YAAY,CACpC;AACF,CAAC,CACH;AAMF,MAAM,UACJ,KACA,WAC0C;CAC1C,MAAM,cAAiB,MACrB,OAAO,eAAe,GAAG,WAAW,YAAY,MAAM;CACxD,OAAO;EACL,SAAS,YAAY,WAAW,UAAU,KAAK,OAAO,CAAC;EACvD,OAAO,QAAQ,WAAW,QAAQ,KAAK,GAAG,CAAC;EAC3C,cAAc,OAAO,KAAK,YAAY,QAAQ,CAAC;CACjD;AACF;AAIA,MAAM,gBACJ,KACA,QACA,KACA,cACmC;CACnC,GAAG,QAAQ,KAAK,IAAI,GAAG;CACvB,iBAAiB,YACf,OAAO,eAAe,aAAa,KAAK,SAAS,OAAO,CAAC,GAAG,WAAW,YAAY,MAAM;AAC7F;;AAGA,MAAa,QACX,QAEA,OAAO,IAAI,WAAW,aAAa,WACjC,aAAa,KAAK,QAAQ,OAAO,KAAK,MAAM,IAAI,YAAY,OAAO,CACrE;;;;;;AAOF,MAAa,SACX,QACiF;CACjF,MAAM,QAAQ,MAAM,OAAO,oBAAoB,KAAK,GAAG,CAAC;CACxD,MAAM,UAAU,MAAM,OACpB,cACA,OAAO,IAAI,WAAW,aAAa,WAAgC;EACjE,MAAM,MAAM,OAAO,KAAK,MAAM;EAC9B,MAAM,aAA8C;GAClD,GAAG;GACH,SAAS,YAAY,IAAI,OAAO,OAAgC;EAClE;EACA,OAAO,aAAa,KAAK,QAAQ,aAAa,YAAY,OAAgC;CAC5F,CAAC,CACH;CACA,OAAO,MAAM,MAAM,OAAO,OAAO;AACnC"}
|
|
@@ -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":"
|
|
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"}
|
package/dist/LyriaGenerator.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as __exportAll } from "./
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
/**
|