@effect-uai/google 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/Gemini.d.mts +0 -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 +1 -3
  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 +0 -2
  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 +2 -3
  21. package/dist/GoogleDeepResearch.d.mts.map +1 -1
  22. package/dist/GoogleDeepResearch.mjs +8 -5
  23. package/dist/GoogleDeepResearch.mjs.map +1 -1
  24. package/dist/GoogleDeepResearch.test.d.mts +1 -1
  25. package/dist/GoogleDeepResearch.test.mjs +1 -1
  26. package/dist/GoogleDeepResearch.test.mjs.map +1 -1
  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 +14 -9
  35. package/dist/codec.d.mts.map +1 -1
  36. package/dist/codec.mjs +2 -2
  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 +17 -11
  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.ts +12 -21
  58. package/src/LyriaGenerator.ts +19 -23
  59. package/src/codec.ts +9 -11
  60. package/dist/dist-CWoAuwS9.mjs.map +0 -1
  61. /package/dist/{chunk-uyGKjUfl.mjs → rolldown-runtime-EAsCxrDo.mjs} +0 -0
@@ -1 +1 @@
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(\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 // 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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"}
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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"}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,4 +1,4 @@
1
- import { i as it, n as globalExpect, r as describe } from "./dist-CWoAuwS9.mjs";
1
+ import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
2
2
  import { WireInteraction, jobStateOf } from "./GoogleDeepResearch.mjs";
3
3
  import { Schema } from "effect";
4
4
  import * as Turn$1 from "@effect-uai/core/Turn";
@@ -1 +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,gBAAgB;AAGxD,MAAM,UAAU,QAA4B;CAC1C,MAAM,QAAQ,WAAW,OAAO,IAAI,CAAC;AACrC,KAAI,MAAM,SAAS,YAAa,OAAM,IAAI,MAAM,2BAA2B,MAAM,OAAO;AACxF,QAAO,MAAM;;AAGf,SAAS,sCAAsC;AAC7C,IAAG,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;KACR,EACF,EACD,EACE,KAAK,EAAE,KAAK,sEAAsE,EACnF,CACF,EACF;IACF,CACF;GACF,CAAC;AACF,eAAOA,OAAK,cAAc,KAAK,CAAC,CAAC,KAAK,sBAAsB;AAC5D,eAAOA,OAAK,UAAU,KAAK,CAAC,CAAC,QAAQ,CACnC;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACR,EACD;GACE,MAAM;GACN,KAAK;GACL,OAAO;GACR,CACF,CAAC;GACF;AAEF,IAAG,kFAAkF;EACnF,MAAM,OAAO,OAAO;GAClB,IAAI;GACJ,QAAQ;GACR,mBAAmB,EACjB,iBAAiB,CAAC,EAAE,KAAK;IAAE,KAAK;IAAqB,OAAO;IAAK,EAAE,CAAC,EACrE;GACD,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAS,CAAC;GAC1C,OAAO,CACL;IACE,MAAM;IACN,mBAAmB,EACjB,iBAAiB,CACf,EAAE,KAAK;KAAE,KAAK;KAAqB,OAAO;KAAK,EAAE,EACjD,EAAE,KAAK;KAAE,KAAK;KAAqB,OAAO;KAAK,EAAE,CAClD,EACF;IACF,CACF;GACF,CAAC;AACF,eAAOA,OAAK,UAAU,KAAK,CAAC,KAAK,MAAM,EAAE,SAAS,kBAAkB,EAAE,IAAI,CAAC,CAAC,QAAQ,CAClF,qBACA,oBACD,CAAC;GACF;EACF"}
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
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LyriaGenerator.mjs","names":[],"sources":["../src/LyriaGenerator.ts"],"sourcesContent":["import {\n Context,\n Duration,\n Effect,\n Encoding,\n Layer,\n Match,\n Redacted,\n Result,\n Schema,\n Stream,\n} from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioChunk, AudioFormat } from \"@effect-uai/core/Audio\"\nimport { warnDroppedWhen } from \"@effect-uai/core/Capabilities\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"@effect-uai/core/Music\"\nimport { singleVariant } from \"@effect-uai/core/Music\"\nimport { MusicGenerator, type MusicGeneratorService } from \"@effect-uai/core/MusicGenerator\"\nimport type { LyriaModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Lyria-typed generate request. Narrows `model` to `LyriaModel`.\n *\n * Lyria 3 sync `generateContent` has no structured wire fields for\n * `lyrics` / `duration` / `seed` / `instrumental` / `bpm` / `scale` /\n * etc. Those fields on the Common request are logged as\n * dropped-bucket-2 hints; the adapter never rewrites or augments the\n * caller's prompt on their behalf. If you want lyrics or vocal\n * suppression, include them in your `prompt` text yourself.\n *\n * Output format: only `audio/mp3` and `audio/wav` are accepted; other\n * containers fail `Unsupported`. The Clip model variant is fixed at\n * `audio/mp3`.\n */\nexport type LyriaGenerateRequest = Omit<CommonGenerateMusicRequest, \"model\"> & {\n readonly model: LyriaModel\n}\n\nexport type LyriaGeneratorService = {\n readonly generate: (\n request: LyriaGenerateRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n readonly streamGeneration: (\n request: LyriaGenerateRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamGenerationFrom: <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<MusicStreamEvent, AiError.AiError | E, R>\n}\n\nexport class LyriaGenerator extends Context.Service<LyriaGenerator, LyriaGeneratorService>()(\n \"@betalyra/effect-uai/providers/google/LyriaGenerator\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec: AudioFormat → Lyria mimeType\n// ---------------------------------------------------------------------------\n\nexport type LyriaResponseMimeType = \"audio/mp3\" | \"audio/wav\"\n\nexport const containerToMimeType: (\n container: AudioFormat[\"container\"],\n) => Effect.Effect<LyriaResponseMimeType, AiError.AiError> = Match.type<\n AudioFormat[\"container\"]\n>().pipe(\n Match.when(\"mp3\", () => Effect.succeed<LyriaResponseMimeType>(\"audio/mp3\")),\n Match.when(\"wav\", () => Effect.succeed<LyriaResponseMimeType>(\"audio/wav\")),\n Match.whenOr(\"ogg\", \"opus\", \"flac\", \"aac\", \"webm\", \"raw\", (c) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"lyria\",\n capability: \"outputFormat\",\n reason: `Lyria 3 only produces audio/mp3 or audio/wav; ${c} is not available. Use container: \"mp3\" or \"wav\".`,\n }),\n ),\n ),\n Match.exhaustive,\n)\n\n/**\n * Is this a Lyria 3 Clip model variant? Clip is fixed at 30 s MP3, it\n * does not accept a `responseFormat` on `generationConfig` (the live\n * API rejects unknown fields). Pro accepts the documented\n * `responseFormat` knob.\n */\nconst isClipModel = (model: string): boolean => model.includes(\"clip\")\n\nexport const realizedFormat: (mime: LyriaResponseMimeType) => AudioFormat =\n Match.type<LyriaResponseMimeType>().pipe(\n Match.when(\n \"audio/mp3\",\n (): AudioFormat => ({ container: \"mp3\", encoding: \"mp3\", sampleRate: 44100, channels: 2 }),\n ),\n Match.when(\n \"audio/wav\",\n (): AudioFormat => ({\n container: \"wav\",\n encoding: \"pcm_s16le\",\n sampleRate: 44100,\n channels: 2,\n }),\n ),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// Bucket-2 warn-and-drop for Common fields Lyria 3 can't honor\n// ---------------------------------------------------------------------------\n\n/**\n * Warn (don't fail) when the caller passed Common fields Lyria 3 has\n * no wire field for. Bucket-2 per capabilities policy: provider has\n * no structured interpretation; the output is still valid music, just\n * less aligned with the caller's hint. We never embed these into the\n * prompt on the caller's behalf, prompt construction is the\n * developer's job.\n */\nconst warnDroppedHints = (request: LyriaGenerateRequest): Effect.Effect<void> =>\n Effect.all(\n [\n warnDroppedWhen(request.lyrics, {\n provider: \"lyria\",\n capability: \"lyrics\",\n field: \"lyrics\",\n reason:\n \"Lyria 3 sync has no `lyrics` wire field. If you want vocals to follow specific lyrics, embed them in your prompt yourself (e.g. with `[Verse]` / `[Chorus]` tags).\",\n }),\n warnDroppedWhen(\n request.duration !== undefined ? Duration.toMillis(request.duration) : undefined,\n {\n provider: \"lyria\",\n capability: \"duration\",\n field: \"duration\",\n reason:\n \"Lyria 3 clip is fixed at 30 s; pro derives duration from prompt content. There is no wire field for duration. If you want to steer length, mention it in your prompt.\",\n },\n ),\n warnDroppedWhen(request.seed, {\n provider: \"lyria\",\n capability: \"seed\",\n field: \"seed\",\n reason:\n \"Lyria 3 (Gemini surface) does not expose a seed parameter. Output is non-deterministic across identical prompts. Use Lyria 2 (Vertex) for seeded generation.\",\n }),\n ],\n { discard: true },\n )\n\n// ---------------------------------------------------------------------------\n// Wire response schema\n// ---------------------------------------------------------------------------\n\nconst InlineData = Schema.Struct({\n mimeType: Schema.optional(Schema.String),\n data: Schema.String,\n})\n\nconst Part = Schema.Struct({\n text: Schema.optional(Schema.String),\n inlineData: Schema.optional(InlineData),\n})\n\nconst Content = Schema.Struct({\n parts: Schema.optional(Schema.Array(Part)),\n})\n\nconst Candidate = Schema.Struct({\n content: Schema.optional(Content),\n})\n\nconst Wire = Schema.Struct({\n candidates: Schema.optional(Schema.Array(Candidate)),\n})\n\nconst decodeWire = Schema.decodeUnknownEffect(Wire)\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string =>\n cfg.baseUrl ?? \"https://generativelanguage.googleapis.com/v1beta\"\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"lyria\", raw: cause })\n\nconst httpStatusError: (status: number, body: string) => AiError.AiError = (status, body) =>\n Match.value(status).pipe(\n Match.when(\n 429,\n (): AiError.AiError => new AiError.RateLimited({ provider: \"lyria\", raw: body }),\n ),\n Match.whenOr(\n 408,\n 504,\n (): AiError.AiError => new AiError.Timeout({ provider: \"lyria\", raw: body }),\n ),\n Match.when(\n 401,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"billing\", raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError => new AiError.Unavailable({ provider: \"lyria\", status: n, raw: body }),\n ),\n Match.orElse(\n (): AiError.AiError => new AiError.InvalidRequest({ provider: \"lyria\", raw: body }),\n ),\n )\n\nconst decodeBase64ToBytes = (b64: string): Effect.Effect<Uint8Array, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onSuccess: Effect.succeed,\n onFailure: (cause) =>\n Effect.fail(new AiError.InvalidRequest({ provider: \"lyria\", param: \"audio\", raw: cause })),\n })\n\n/**\n * Find the first inline-data audio part and the concatenated text parts\n * from a Wire response.\n */\nconst extractParts = (\n wire: typeof Wire.Type,\n): { readonly inline?: typeof InlineData.Type; readonly text?: string } => {\n const allParts = (wire.candidates ?? []).flatMap((c) => c.content?.parts ?? [])\n const inline = allParts.find((p) => p.inlineData !== undefined)?.inlineData\n const textParts = allParts.flatMap((p) => (p.text !== undefined ? [p.text] : []))\n const text = textParts.length === 0 ? undefined : textParts.join(\"\\n\")\n return {\n ...(inline !== undefined && { inline }),\n ...(text !== undefined && { text }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst generateImpl =\n (cfg: Config) =>\n (\n request: LyriaGenerateRequest,\n ): Effect.Effect<GenerateResult, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const requestedContainer = (request.outputFormat?.container ??\n \"mp3\") satisfies AudioFormat[\"container\"]\n const requestedMime = yield* containerToMimeType(requestedContainer)\n // lyria-3-clip-preview has no `responseFormat` wire field and always\n // emits mp3, so there is no format to send and no 400 to surface. Rather\n // than keep a per-model table that rejects `wav`, report the format the\n // model actually produces (mp3) so the result is never mislabeled.\n // lyria-3-pro honours the requested container.\n const mimeType = isClipModel(request.model) ? (\"audio/mp3\" as const) : requestedMime\n yield* warnDroppedHints(request)\n const body = isClipModel(request.model)\n ? { contents: [{ parts: [{ text: request.prompt }] }] }\n : {\n contents: [{ parts: [{ text: request.prompt }] }],\n generationConfig: {\n responseModalities: [\"AUDIO\", \"TEXT\"],\n responseFormat: { audio: { mimeType } },\n },\n }\n const httpRequest = HttpClientRequest.post(\n `${baseUrl(cfg)}/models/${request.model}:generateContent`,\n ).pipe(\n HttpClientRequest.setHeader(\"x-goog-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\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* decodeWire(json).pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.GenerationFailed({ provider: \"lyria\", raw: cause }),\n ),\n )\n const { inline, text } = extractParts(wire)\n if (inline === undefined) {\n return yield* new AiError.GenerationFailed({\n provider: \"lyria\",\n raw: {\n message: \"Lyria response had no inlineData audio part.\",\n hint: \"Likely a prompt-filter rejection. Lyria filters references to real artists / copyrighted song lyrics. Rephrase with descriptive style language instead of artist names.\",\n textParts: text,\n response: json,\n },\n })\n }\n const bytes = yield* decodeBase64ToBytes(inline.data)\n const result: MusicResult = {\n audio: { format: realizedFormat(mimeType), bytes },\n provider: \"lyria\",\n ...(text !== undefined && { lyrics: text }),\n watermark: \"synthid\",\n }\n return singleVariant(result)\n })\n\nconst streamGenerationImpl =\n (cfg: Config) =>\n (\n request: LyriaGenerateRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.map(generateImpl(cfg)(request), (result) =>\n Stream.succeed<AudioChunk>({ bytes: result.primary.audio.bytes }),\n ),\n )\n\n/**\n * Lyria 3 sync has no bidirectional session at the wire level, that's\n * Lyria RealTime (separate Layer). The provider Layer here also does\n * NOT register `MusicInteractiveSession`, so callers using\n * `MusicGenerator.streamGenerationFrom` against only this Layer get a\n * compile-time error before this runtime fallback ever fires.\n */\nconst streamGenerationFromUnsupported = <E, R>(\n _input: Stream.Stream<MusicSessionInput, E, R>,\n _request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<MusicStreamEvent, AiError.AiError | E, R> => {\n const fail: Stream.Stream<MusicStreamEvent, AiError.AiError | E, R> = Stream.fail(\n new AiError.Unsupported({\n provider: \"lyria\",\n capability: \"streamGenerationFrom\",\n reason:\n \"Lyria 3 sync does not support bidirectional sessions. Use the Lyria RealTime Layer (WebSocket BidiGenerateMusic) for interactive mid-session prompts.\",\n }),\n )\n return fail\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<LyriaGeneratorService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n generate: (request) =>\n generateImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamGeneration: (request) =>\n streamGenerationImpl(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamGenerationFrom: streamGenerationFromUnsupported,\n }))\n\n/**\n * Layer that registers both the provider-specific `LyriaGenerator` tag\n * and the generic `MusicGenerator` tag.\n *\n * Does NOT register the `MusicInteractiveSession` capability marker,\n * Lyria 3 sync has no bidirectional session at the wire level. Code\n * calling `MusicGenerator.streamGenerationFrom` will fail to typecheck\n * against this Layer alone, which is the intended UX.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<LyriaGenerator | MusicGenerator, never, HttpClient.HttpClient> =>\n Layer.merge(\n Layer.effect(LyriaGenerator, make(cfg)),\n Layer.effect(\n MusicGenerator,\n Effect.map(\n make(cfg),\n (s): MusicGeneratorService => ({\n generate: (req: CommonGenerateMusicRequest) =>\n s.generate({ ...req, model: req.model as LyriaModel }),\n streamGeneration: (req: CommonStreamGenerateMusicRequest) =>\n s.streamGeneration({ ...req, model: req.model as LyriaModel }),\n streamGenerationFrom: s.streamGenerationFrom,\n }),\n ),\n ),\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"LyriaGenerator.mjs","names":[],"sources":["../src/LyriaGenerator.ts"],"sourcesContent":["import {\n Context,\n Duration,\n Effect,\n Encoding,\n Layer,\n Match,\n Redacted,\n Result,\n Schema,\n Stream,\n} from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioChunk, AudioFormat } from \"@effect-uai/core/Audio\"\nimport { warnDroppedWhen } from \"@effect-uai/core/Capabilities\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"@effect-uai/core/Music\"\nimport { singleVariant } from \"@effect-uai/core/Music\"\nimport { MusicGenerator, type MusicGeneratorService } from \"@effect-uai/core/MusicGenerator\"\nimport type { LyriaModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Lyria-typed generate request. Narrows `model` to `LyriaModel`.\n *\n * Lyria 3 sync `generateContent` has no structured wire fields for\n * `lyrics` / `duration` / `seed` / `instrumental` / `bpm` / `scale` /\n * etc. Those fields on the Common request are logged as\n * dropped-bucket-2 hints; the adapter never rewrites or augments the\n * caller's prompt on their behalf. If you want lyrics or vocal\n * suppression, include them in your `prompt` text yourself.\n *\n * Output format: only `audio/mp3` and `audio/wav` are accepted; other\n * containers fail `Unsupported`. The Clip model variant is fixed at\n * `audio/mp3`.\n */\nexport type LyriaGenerateRequest = Omit<CommonGenerateMusicRequest, \"model\"> & {\n readonly model: LyriaModel\n}\n\nexport type LyriaGeneratorService = {\n readonly generate: (\n request: LyriaGenerateRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n readonly streamGeneration: (\n request: LyriaGenerateRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamGenerationFrom: <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<MusicStreamEvent, AiError.AiError | E, R>\n}\n\nexport class LyriaGenerator extends Context.Service<LyriaGenerator, LyriaGeneratorService>()(\n \"@betalyra/effect-uai/providers/google/LyriaGenerator\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec: AudioFormat → Lyria mimeType\n// ---------------------------------------------------------------------------\n\nexport type LyriaResponseMimeType = \"audio/mp3\" | \"audio/wav\"\n\nexport const containerToMimeType: (\n container: AudioFormat[\"container\"],\n) => Effect.Effect<LyriaResponseMimeType, AiError.AiError> = Match.type<\n AudioFormat[\"container\"]\n>().pipe(\n Match.when(\"mp3\", () => Effect.succeed<LyriaResponseMimeType>(\"audio/mp3\")),\n Match.when(\"wav\", () => Effect.succeed<LyriaResponseMimeType>(\"audio/wav\")),\n Match.whenOr(\"ogg\", \"opus\", \"flac\", \"aac\", \"webm\", \"raw\", (c) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"lyria\",\n capability: \"outputFormat\",\n reason: `Lyria 3 only produces audio/mp3 or audio/wav; ${c} is not available. Use container: \"mp3\" or \"wav\".`,\n }),\n ),\n ),\n Match.exhaustive,\n)\n\n/**\n * Is this a Lyria 3 Clip model variant? Clip is fixed at 30 s MP3, it\n * does not accept a `responseFormat` on `generationConfig` (the live\n * API rejects unknown fields). Pro accepts the documented\n * `responseFormat` knob.\n */\nconst isClipModel = (model: string): boolean => model.includes(\"clip\")\n\nexport const realizedFormat: (mime: LyriaResponseMimeType) => AudioFormat =\n Match.type<LyriaResponseMimeType>().pipe(\n Match.when(\"audio/mp3\", (): AudioFormat => ({\n container: \"mp3\",\n encoding: \"mp3\",\n sampleRate: 44100,\n channels: 2,\n })),\n Match.when(\"audio/wav\", (): AudioFormat => ({\n container: \"wav\",\n encoding: \"pcm_s16le\",\n sampleRate: 44100,\n channels: 2,\n })),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// Bucket-2 warn-and-drop for Common fields Lyria 3 can't honor\n// ---------------------------------------------------------------------------\n\n/**\n * Warn (don't fail) when the caller passed Common fields Lyria 3 has\n * no wire field for. Bucket-2 per capabilities policy: provider has\n * no structured interpretation; the output is still valid music, just\n * less aligned with the caller's hint. We never embed these into the\n * prompt on the caller's behalf, prompt construction is the\n * developer's job.\n */\nconst warnDroppedHints = (request: LyriaGenerateRequest): Effect.Effect<void> =>\n Effect.all(\n [\n warnDroppedWhen(request.lyrics, {\n provider: \"lyria\",\n capability: \"lyrics\",\n field: \"lyrics\",\n reason:\n \"Lyria 3 sync has no `lyrics` wire field. If you want vocals to follow specific lyrics, embed them in your prompt yourself (e.g. with `[Verse]` / `[Chorus]` tags).\",\n }),\n warnDroppedWhen(\n request.duration !== undefined ? Duration.toMillis(request.duration) : undefined,\n {\n provider: \"lyria\",\n capability: \"duration\",\n field: \"duration\",\n reason:\n \"Lyria 3 clip is fixed at 30 s; pro derives duration from prompt content. There is no wire field for duration. If you want to steer length, mention it in your prompt.\",\n },\n ),\n warnDroppedWhen(request.seed, {\n provider: \"lyria\",\n capability: \"seed\",\n field: \"seed\",\n reason:\n \"Lyria 3 (Gemini surface) does not expose a seed parameter. Output is non-deterministic across identical prompts. Use Lyria 2 (Vertex) for seeded generation.\",\n }),\n ],\n { discard: true },\n )\n\n// ---------------------------------------------------------------------------\n// Wire response schema\n// ---------------------------------------------------------------------------\n\nconst InlineData = Schema.Struct({\n mimeType: Schema.optional(Schema.String),\n data: Schema.String,\n})\n\nconst Part = Schema.Struct({\n text: Schema.optional(Schema.String),\n inlineData: Schema.optional(InlineData),\n})\n\nconst Content = Schema.Struct({\n parts: Schema.optional(Schema.Array(Part)),\n})\n\nconst Candidate = Schema.Struct({\n content: Schema.optional(Content),\n})\n\nconst Wire = Schema.Struct({\n candidates: Schema.optional(Schema.Array(Candidate)),\n})\n\nconst decodeWire = Schema.decodeUnknownEffect(Wire)\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string =>\n cfg.baseUrl ?? \"https://generativelanguage.googleapis.com/v1beta\"\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"lyria\", raw: cause })\n\nconst httpStatusError: (status: number, body: string) => AiError.AiError = (status, body) =>\n Match.value(status).pipe(\n Match.when(\n 429,\n (): AiError.AiError => new AiError.RateLimited({ provider: \"lyria\", raw: body }),\n ),\n Match.whenOr(\n 408,\n 504,\n (): AiError.AiError => new AiError.Timeout({ provider: \"lyria\", raw: body }),\n ),\n Match.when(\n 401,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"lyria\", subtype: \"billing\", raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError => new AiError.Unavailable({ provider: \"lyria\", status: n, raw: body }),\n ),\n Match.orElse(\n (): AiError.AiError => new AiError.InvalidRequest({ provider: \"lyria\", raw: body }),\n ),\n )\n\nconst decodeBase64ToBytes = (b64: string): Effect.Effect<Uint8Array, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onSuccess: Effect.succeed,\n onFailure: (cause) =>\n Effect.fail(new AiError.InvalidRequest({ provider: \"lyria\", param: \"audio\", raw: cause })),\n })\n\n/**\n * Find the first inline-data audio part and the concatenated text parts\n * from a Wire response.\n */\nconst extractParts = (\n wire: typeof Wire.Type,\n): { readonly inline?: typeof InlineData.Type; readonly text?: string } => {\n const allParts = (wire.candidates ?? []).flatMap((c) => c.content?.parts ?? [])\n const inline = allParts.find((p) => p.inlineData !== undefined)?.inlineData\n const textParts = allParts.flatMap((p) => (p.text !== undefined ? [p.text] : []))\n const text = textParts.length === 0 ? undefined : textParts.join(\"\\n\")\n return {\n ...(inline !== undefined && { inline }),\n ...(text !== undefined && { text }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst generateImpl =\n (cfg: Config) =>\n (\n request: LyriaGenerateRequest,\n ): Effect.Effect<GenerateResult, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const requestedContainer = (request.outputFormat?.container ??\n \"mp3\") satisfies AudioFormat[\"container\"]\n const requestedMime = yield* containerToMimeType(requestedContainer)\n // lyria-3-clip-preview has no `responseFormat` wire field and always\n // emits mp3, so there is no format to send and no 400 to surface. Rather\n // than keep a per-model table that rejects `wav`, report the format the\n // model actually produces (mp3) so the result is never mislabeled.\n // lyria-3-pro honours the requested container.\n const mimeType = isClipModel(request.model) ? (\"audio/mp3\" as const) : requestedMime\n yield* warnDroppedHints(request)\n const body = isClipModel(request.model)\n ? { contents: [{ parts: [{ text: request.prompt }] }] }\n : {\n contents: [{ parts: [{ text: request.prompt }] }],\n generationConfig: {\n responseModalities: [\"AUDIO\", \"TEXT\"],\n responseFormat: { audio: { mimeType } },\n },\n }\n const httpRequest = HttpClientRequest.post(\n `${baseUrl(cfg)}/models/${request.model}:generateContent`,\n ).pipe(\n HttpClientRequest.setHeader(\"x-goog-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\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* decodeWire(json).pipe(\n Effect.mapError(\n (cause): AiError.AiError =>\n new AiError.GenerationFailed({ provider: \"lyria\", raw: cause }),\n ),\n )\n const { inline, text } = extractParts(wire)\n if (inline === undefined) {\n return yield* new AiError.GenerationFailed({\n provider: \"lyria\",\n raw: {\n message: \"Lyria response had no inlineData audio part.\",\n hint: \"Likely a prompt-filter rejection. Lyria filters references to real artists / copyrighted song lyrics. Rephrase with descriptive style language instead of artist names.\",\n textParts: text,\n response: json,\n },\n })\n }\n const bytes = yield* decodeBase64ToBytes(inline.data)\n const result: MusicResult = {\n audio: { format: realizedFormat(mimeType), bytes },\n provider: \"lyria\",\n ...(text !== undefined && { lyrics: text }),\n watermark: \"synthid\",\n }\n return singleVariant(result)\n })\n\nconst streamGenerationImpl =\n (cfg: Config) =>\n (\n request: LyriaGenerateRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.map(generateImpl(cfg)(request), (result) =>\n Stream.succeed<AudioChunk>({ bytes: result.primary.audio.bytes }),\n ),\n )\n\n/**\n * Lyria 3 sync has no bidirectional session at the wire level, that's\n * Lyria RealTime (separate Layer). The provider Layer here also does\n * NOT register `MusicInteractiveSession`, so callers using\n * `MusicGenerator.streamGenerationFrom` against only this Layer get a\n * compile-time error before this runtime fallback ever fires.\n */\nconst streamGenerationFromUnsupported = <E, R>(\n _input: Stream.Stream<MusicSessionInput, E, R>,\n _request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<MusicStreamEvent, AiError.AiError | E, R> => {\n const fail: Stream.Stream<MusicStreamEvent, AiError.AiError | E, R> = Stream.fail(\n new AiError.Unsupported({\n provider: \"lyria\",\n capability: \"streamGenerationFrom\",\n reason:\n \"Lyria 3 sync does not support bidirectional sessions. Use the Lyria RealTime Layer (WebSocket BidiGenerateMusic) for interactive mid-session prompts.\",\n }),\n )\n return fail\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<LyriaGeneratorService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n generate: (request) =>\n generateImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamGeneration: (request) =>\n streamGenerationImpl(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamGenerationFrom: streamGenerationFromUnsupported,\n }))\n\n/**\n * Layer that registers both the provider-specific `LyriaGenerator` tag\n * and the generic `MusicGenerator` tag.\n *\n * Does NOT register the `MusicInteractiveSession` capability marker,\n * Lyria 3 sync has no bidirectional session at the wire level. Code\n * calling `MusicGenerator.streamGenerationFrom` will fail to typecheck\n * against this Layer alone, which is the intended UX.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<LyriaGenerator | MusicGenerator, never, HttpClient.HttpClient> =>\n Layer.merge(\n Layer.effect(LyriaGenerator, make(cfg)),\n Layer.effect(\n MusicGenerator,\n Effect.map(make(cfg), (s): MusicGeneratorService => ({\n generate: (req: CommonGenerateMusicRequest) =>\n s.generate({ ...req, model: req.model as LyriaModel }),\n streamGeneration: (req: CommonStreamGenerateMusicRequest) =>\n s.streamGeneration({ ...req, model: req.model as LyriaModel }),\n streamGenerationFrom: s.streamGenerationFrom,\n })),\n ),\n )\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,9 +1,150 @@
1
- import { i as it, n as globalExpect, r as describe, t as import_dist } from "./dist-CWoAuwS9.mjs";
1
+ import { t as __commonJSMin } from "./rolldown-runtime-EAsCxrDo.mjs";
2
+ import { n as describe, r as it, t as globalExpect } from "./test.DNmyFkvJ-N3QqMRwZ.mjs";
2
3
  import { LyriaGenerator, containerToMimeType, layer, realizedFormat } from "./LyriaGenerator.mjs";
3
4
  import { Duration, Effect, Layer, Redacted, Stream } from "effect";
4
5
  import { FetchHttpClient } from "effect/unstable/http";
5
6
  import { promptsInput } from "@effect-uai/core/Music";
6
7
  import * as MusicGenerator$1 from "@effect-uai/core/MusicGenerator";
8
+ //#region ../../../node_modules/.pnpm/vite@7.3.2_@types+node@26.2.0_jiti@2.6.1_lightningcss@1.32.0_tsx@4.23.12_yaml@2.9.0/node_modules/vite/dist/node/module-runner.js
9
+ let SOURCEMAPPING_URL = "sourceMa";
10
+ SOURCEMAPPING_URL += "ppingURL";
11
+ typeof process < "u" && process.platform;
12
+ (async function() {}).constructor;
13
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
14
+ var intToChar = /* @__PURE__ */ new Uint8Array(64);
15
+ var charToInt = /* @__PURE__ */ new Uint8Array(128);
16
+ for (let i = 0; i < chars.length; i++) {
17
+ let c = chars.charCodeAt(i);
18
+ intToChar[i] = c, charToInt[c] = i;
19
+ }
20
+ `${SOURCEMAPPING_URL}`;
21
+ Error.prepareStackTrace;
22
+ const customizationHookNamespace = "vite-module-runner:import-meta-resolve/v1/";
23
+ `${JSON.stringify(customizationHookNamespace)}${JSON.stringify(customizationHookNamespace)}`;
24
+ new Proxy({}, { get(_, p) {
25
+ throw Error(`[module runner] Dynamic access of "import.meta.env" is not supported. Please, use "import.meta.env.${String(p)}" instead.`);
26
+ } });
27
+ //#endregion
28
+ //#region ../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js
29
+ var require_branding = /* @__PURE__ */ __commonJSMin(((exports) => {
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ }));
32
+ //#endregion
33
+ //#region ../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js
34
+ var require_messages = /* @__PURE__ */ __commonJSMin(((exports) => {
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ }));
37
+ //#endregion
38
+ //#region ../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js
39
+ var require_overloads = /* @__PURE__ */ __commonJSMin(((exports) => {
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ }));
42
+ //#endregion
43
+ //#region ../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js
44
+ var require_utils = /* @__PURE__ */ __commonJSMin(((exports) => {
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ }));
47
+ //#endregion
48
+ //#region ../../../node_modules/.pnpm/vitest@4.1.11_@types+node@26.2.0_vite@7.3.2_@types+node@26.2.0_jiti@2.6.1_lightningcss@1.32.0_tsx@4.23.12_yaml@2.9.0_/node_modules/vitest/dist/index.js
49
+ var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
50
+ var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
51
+ if (k2 === void 0) k2 = k;
52
+ var desc = Object.getOwnPropertyDescriptor(m, k);
53
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = {
54
+ enumerable: true,
55
+ get: function() {
56
+ return m[k];
57
+ }
58
+ };
59
+ Object.defineProperty(o, k2, desc);
60
+ }) : (function(o, m, k, k2) {
61
+ if (k2 === void 0) k2 = k;
62
+ o[k2] = m[k];
63
+ }));
64
+ var __exportStar = exports && exports.__exportStar || function(m, exports$1) {
65
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
66
+ };
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.expectTypeOf = void 0;
69
+ __exportStar(require_branding(), exports);
70
+ __exportStar(require_messages(), exports);
71
+ __exportStar(require_overloads(), exports);
72
+ __exportStar(require_utils(), exports);
73
+ const fn = () => true;
74
+ /**
75
+ * Similar to Jest's `expect`, but with type-awareness.
76
+ * Gives you access to a number of type-matchers that let you make assertions about the
77
+ * form of a reference or generic type parameter.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { foo, bar } from '../foo'
82
+ * import { expectTypeOf } from 'expect-type'
83
+ *
84
+ * test('foo types', () => {
85
+ * // make sure `foo` has type { a: number }
86
+ * expectTypeOf(foo).toMatchTypeOf({ a: 1 })
87
+ * expectTypeOf(foo).toHaveProperty('a').toBeNumber()
88
+ *
89
+ * // make sure `bar` is a function taking a string:
90
+ * expectTypeOf(bar).parameter(0).toBeString()
91
+ * expectTypeOf(bar).returns.not.toBeAny()
92
+ * })
93
+ * ```
94
+ *
95
+ * @description
96
+ * See the [full docs](https://npmjs.com/package/expect-type#documentation) for lots more examples.
97
+ */
98
+ const expectTypeOf = (_actual) => {
99
+ const nonFunctionProperties = [
100
+ "parameters",
101
+ "returns",
102
+ "resolves",
103
+ "not",
104
+ "items",
105
+ "constructorParameters",
106
+ "thisParameter",
107
+ "instance",
108
+ "guards",
109
+ "asserts",
110
+ "branded"
111
+ ];
112
+ const obj = {
113
+ toBeAny: fn,
114
+ toBeUnknown: fn,
115
+ toBeNever: fn,
116
+ toBeFunction: fn,
117
+ toBeObject: fn,
118
+ toBeArray: fn,
119
+ toBeString: fn,
120
+ toBeNumber: fn,
121
+ toBeBoolean: fn,
122
+ toBeVoid: fn,
123
+ toBeSymbol: fn,
124
+ toBeNull: fn,
125
+ toBeUndefined: fn,
126
+ toBeNullable: fn,
127
+ toBeBigInt: fn,
128
+ toMatchTypeOf: fn,
129
+ toEqualTypeOf: fn,
130
+ toBeConstructibleWith: fn,
131
+ toMatchObjectType: fn,
132
+ toExtend: fn,
133
+ map: exports.expectTypeOf,
134
+ toBeCallableWith: exports.expectTypeOf,
135
+ extract: exports.expectTypeOf,
136
+ exclude: exports.expectTypeOf,
137
+ pick: exports.expectTypeOf,
138
+ omit: exports.expectTypeOf,
139
+ toHaveProperty: exports.expectTypeOf,
140
+ parameter: exports.expectTypeOf
141
+ };
142
+ nonFunctionProperties.forEach((prop) => Object.defineProperty(obj, prop, { get: () => (0, exports.expectTypeOf)({}) }));
143
+ return obj;
144
+ };
145
+ exports.expectTypeOf = expectTypeOf;
146
+ })))();
147
+ //#endregion
7
148
  //#region src/LyriaGenerator.test.ts
8
149
  const cfg = { apiKey: Redacted.make("test-key") };
9
150
  const live = Layer.provide(layer(cfg), FetchHttpClient.layer);
@@ -57,19 +198,22 @@ describe("LyriaGenerator Layer (compile-time)", () => {
57
198
  model: "lyria-3-clip-preview",
58
199
  prompt: ""
59
200
  }));
60
- (0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(live))).toEqualTypeOf();
201
+ const provided = Stream.runDrain(events).pipe(Effect.provide(live));
202
+ (0, import_dist.expectTypeOf)(provided).toEqualTypeOf();
61
203
  });
62
204
  it("sync `generate` returns GenerateResult and requires no marker", () => {
63
- (0, import_dist.expectTypeOf)(MusicGenerator$1.generate({
205
+ const gen = MusicGenerator$1.generate({
64
206
  model: "lyria-3-clip-preview",
65
207
  prompt: "ambient"
66
- }).pipe(Effect.provide(live))).toEqualTypeOf();
208
+ }).pipe(Effect.provide(live));
209
+ (0, import_dist.expectTypeOf)(gen).toEqualTypeOf();
67
210
  });
68
211
  it("chunked `streamGeneration` requires no marker", () => {
69
- globalExpect(Stream.runCollect(MusicGenerator$1.streamGeneration({
212
+ const _stream = Stream.runCollect(MusicGenerator$1.streamGeneration({
70
213
  model: "lyria-3-clip-preview",
71
214
  prompt: "ambient"
72
- })).pipe(Effect.provide(live))).toBeDefined();
215
+ })).pipe(Effect.provide(live));
216
+ globalExpect(_stream).toBeDefined();
73
217
  });
74
218
  it("Lyria-typed request accepts no `instrumental` field", () => {
75
219
  globalExpect({
@@ -79,11 +223,12 @@ describe("LyriaGenerator Layer (compile-time)", () => {
79
223
  }).toBeDefined();
80
224
  });
81
225
  it("Common request supports `duration: Duration.Duration`", () => {
82
- globalExpect({
226
+ const _req = {
83
227
  model: "lyria-3-clip-preview",
84
228
  prompt: "ambient",
85
229
  duration: Duration.seconds(30)
86
- }).toBeDefined();
230
+ };
231
+ globalExpect(_req).toBeDefined();
87
232
  });
88
233
  });
89
234
  //#endregion