@effect-uai/core 0.10.0 → 0.11.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 (134) hide show
  1. package/dist/{Image-BZmKfIdq.d.mts → Image-C4vuBmu0.d.mts} +11 -11
  2. package/dist/{Image-BZmKfIdq.d.mts.map → Image-C4vuBmu0.d.mts.map} +1 -1
  3. package/dist/{Items-BnIduEV2.d.mts → Items-BWNu6VGp.d.mts} +116 -90
  4. package/dist/Items-BWNu6VGp.d.mts.map +1 -0
  5. package/dist/{Tool-2lC_XMth.d.mts → Tool-CXFHCQrT.d.mts} +42 -9
  6. package/dist/Tool-CXFHCQrT.d.mts.map +1 -0
  7. package/dist/{ToolEvent-cWZ2BnbS.d.mts → ToolEvent-nVijCrvm.d.mts} +2 -2
  8. package/dist/{ToolEvent-cWZ2BnbS.d.mts.map → ToolEvent-nVijCrvm.d.mts.map} +1 -1
  9. package/dist/{ToolResult-CL4hK_9z.d.mts → ToolResult-CQqKkcDa.d.mts} +2 -2
  10. package/dist/{ToolResult-CL4hK_9z.d.mts.map → ToolResult-CQqKkcDa.d.mts.map} +1 -1
  11. package/dist/{Toolkit-Dr9l_hNy.d.mts → Toolkit-BYPwwu-j.d.mts} +6 -6
  12. package/dist/Toolkit-BYPwwu-j.d.mts.map +1 -0
  13. package/dist/browser/Browser.mjs +4 -4
  14. package/dist/browser/Browser.mjs.map +1 -1
  15. package/dist/browser/BrowserTool.d.mts +34 -33
  16. package/dist/browser/BrowserTool.d.mts.map +1 -1
  17. package/dist/{dist-DWgRbDN3.mjs → dist-C0m0QnUP.mjs} +71 -30
  18. package/dist/dist-C0m0QnUP.mjs.map +1 -0
  19. package/dist/{dist-B04dlFHg.mjs → dist-CcRV3-A5.mjs} +8 -8
  20. package/dist/{dist-B04dlFHg.mjs.map → dist-CcRV3-A5.mjs.map} +1 -1
  21. package/dist/domain/Citation.d.mts +60 -0
  22. package/dist/domain/Citation.d.mts.map +1 -0
  23. package/dist/domain/Citation.mjs +7 -0
  24. package/dist/domain/Citation.mjs.map +1 -0
  25. package/dist/domain/Image.d.mts +1 -1
  26. package/dist/domain/Items.d.mts +1 -1
  27. package/dist/domain/Items.mjs +13 -3
  28. package/dist/domain/Items.mjs.map +1 -1
  29. package/dist/domain/Research.d.mts +37 -0
  30. package/dist/domain/Research.d.mts.map +1 -0
  31. package/dist/domain/Research.mjs +7 -0
  32. package/dist/domain/Research.mjs.map +1 -0
  33. package/dist/domain/Turn.d.mts +91 -9
  34. package/dist/domain/Turn.d.mts.map +1 -1
  35. package/dist/domain/Turn.mjs +8 -1
  36. package/dist/domain/Turn.mjs.map +1 -1
  37. package/dist/domain/Turn.test.mjs +1 -1
  38. package/dist/embedding-model/Embedding.d.mts +1 -1
  39. package/dist/embedding-model/EmbeddingModel.mjs +2 -2
  40. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
  41. package/dist/embedding-model/EmbeddingModel.test.mjs +1 -1
  42. package/dist/index.d.mts +12 -8
  43. package/dist/index.mjs +5 -1
  44. package/dist/job/Job.d.mts +80 -0
  45. package/dist/job/Job.d.mts.map +1 -0
  46. package/dist/job/Job.mjs +49 -0
  47. package/dist/job/Job.mjs.map +1 -0
  48. package/dist/language-model/LanguageModel.d.mts +2 -2
  49. package/dist/language-model/LanguageModel.mjs +2 -2
  50. package/dist/language-model/LanguageModel.mjs.map +1 -1
  51. package/dist/language-model/LanguageModel.test.mjs +1 -1
  52. package/dist/loop/Loop.test.mjs +1 -1
  53. package/dist/music-generator/MusicGenerator.mjs +4 -4
  54. package/dist/music-generator/MusicGenerator.mjs.map +1 -1
  55. package/dist/music-generator/MusicGenerator.test.mjs +1 -1
  56. package/dist/observability/Metrics.d.mts +1 -1
  57. package/dist/observability/Metrics.test.mjs +2 -2
  58. package/dist/observability/Telemetry.test.mjs +2 -2
  59. package/dist/research/DeepResearch.d.mts +71 -0
  60. package/dist/research/DeepResearch.d.mts.map +1 -0
  61. package/dist/research/DeepResearch.mjs +61 -0
  62. package/dist/research/DeepResearch.mjs.map +1 -0
  63. package/dist/retry/Retry.test.mjs +1 -1
  64. package/dist/sandbox/Sandbox.mjs +18 -18
  65. package/dist/sandbox/Sandbox.mjs.map +1 -1
  66. package/dist/speech-synthesizer/SpeechSynthesizer.mjs +8 -8
  67. package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
  68. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +1 -1
  69. package/dist/streaming/JSONL.d.mts +3 -1
  70. package/dist/streaming/JSONL.d.mts.map +1 -1
  71. package/dist/streaming/JSONL.mjs +5 -11
  72. package/dist/streaming/JSONL.mjs.map +1 -1
  73. package/dist/streaming/JSONL.test.mjs +10 -1
  74. package/dist/streaming/JSONL.test.mjs.map +1 -1
  75. package/dist/streaming/SSE.d.mts +5 -3
  76. package/dist/streaming/SSE.d.mts.map +1 -1
  77. package/dist/streaming/SSE.mjs +28 -38
  78. package/dist/streaming/SSE.mjs.map +1 -1
  79. package/dist/streaming/SSE.test.mjs +54 -4
  80. package/dist/streaming/SSE.test.mjs.map +1 -1
  81. package/dist/structured-format/StructuredFormat.test.mjs +1 -1
  82. package/dist/testing/MockProvider.d.mts +1 -1
  83. package/dist/tool/Approval.d.mts +3 -3
  84. package/dist/tool/Approval.test.mjs +1 -1
  85. package/dist/tool/HistoryCheck.d.mts +2 -2
  86. package/dist/tool/Tool.d.mts +2 -2
  87. package/dist/tool/Tool.mjs +27 -7
  88. package/dist/tool/Tool.mjs.map +1 -1
  89. package/dist/tool/Tool.test.mjs +127 -3
  90. package/dist/tool/Tool.test.mjs.map +1 -1
  91. package/dist/tool/ToolEvent.d.mts +1 -1
  92. package/dist/tool/ToolResult.d.mts +1 -1
  93. package/dist/tool/Toolkit.d.mts +1 -1
  94. package/dist/tool/Toolkit.mjs.map +1 -1
  95. package/dist/tool/Toolkit.test.mjs +46 -2
  96. package/dist/tool/Toolkit.test.mjs.map +1 -1
  97. package/dist/transcriber/Transcriber.mjs +3 -3
  98. package/dist/transcriber/Transcriber.mjs.map +1 -1
  99. package/dist/transcriber/Transcriber.test.mjs +1 -1
  100. package/dist/web-read/WebRead.mjs +1 -1
  101. package/dist/web-read/WebRead.mjs.map +1 -1
  102. package/dist/web-read/WebReadTool.d.mts +1 -1
  103. package/dist/web-search/WebSearch.mjs +1 -1
  104. package/dist/web-search/WebSearch.mjs.map +1 -1
  105. package/dist/web-search/WebSearchTool.d.mts +1 -1
  106. package/package.json +19 -3
  107. package/src/browser/Browser.ts +4 -4
  108. package/src/domain/Citation.ts +64 -0
  109. package/src/domain/Items.ts +14 -4
  110. package/src/domain/Research.ts +42 -0
  111. package/src/domain/Turn.ts +26 -0
  112. package/src/embedding-model/EmbeddingModel.ts +2 -2
  113. package/src/index.ts +4 -0
  114. package/src/job/Job.ts +125 -0
  115. package/src/language-model/LanguageModel.ts +2 -2
  116. package/src/music-generator/MusicGenerator.ts +4 -4
  117. package/src/research/DeepResearch.ts +137 -0
  118. package/src/sandbox/Sandbox.ts +18 -18
  119. package/src/speech-synthesizer/SpeechSynthesizer.ts +8 -8
  120. package/src/streaming/JSONL.test.ts +19 -0
  121. package/src/streaming/JSONL.ts +6 -38
  122. package/src/streaming/SSE.test.ts +86 -3
  123. package/src/streaming/SSE.ts +36 -80
  124. package/src/tool/Tool.test.ts +151 -1
  125. package/src/tool/Tool.ts +120 -22
  126. package/src/tool/Toolkit.test.ts +60 -0
  127. package/src/tool/Toolkit.ts +2 -2
  128. package/src/transcriber/Transcriber.ts +3 -3
  129. package/src/web-read/WebRead.ts +1 -1
  130. package/src/web-search/WebSearch.ts +1 -1
  131. package/dist/Items-BnIduEV2.d.mts.map +0 -1
  132. package/dist/Tool-2lC_XMth.d.mts.map +0 -1
  133. package/dist/Toolkit-Dr9l_hNy.d.mts.map +0 -1
  134. package/dist/dist-DWgRbDN3.mjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,23 +1,27 @@
1
1
  import { t as BrowserError_d_exports } from "./browser/BrowserError.mjs";
2
2
  import { t as Browser_d_exports } from "./browser/Browser.mjs";
3
3
  import { a as Media_d_exports } from "./Media-D_CpcM1Z.mjs";
4
- import { o as Image_d_exports } from "./Image-BZmKfIdq.mjs";
5
- import { u as Items_d_exports } from "./Items-BnIduEV2.mjs";
6
- import { _ as Tool_d_exports } from "./Tool-2lC_XMth.mjs";
4
+ import { o as Image_d_exports } from "./Image-C4vuBmu0.mjs";
5
+ import { u as Items_d_exports } from "./Items-BWNu6VGp.mjs";
6
+ import { _ as Tool_d_exports } from "./Tool-CXFHCQrT.mjs";
7
7
  import { t as BrowserTool_d_exports } from "./browser/BrowserTool.mjs";
8
8
  import { t as Capabilities_d_exports } from "./capabilities/Capabilities.mjs";
9
9
  import { n as AiError_d_exports } from "./AiError-Bf1pq4BB.mjs";
10
10
  import { o as Audio_d_exports } from "./Audio-CR7CSljJ.mjs";
11
+ import { t as Citation_d_exports } from "./domain/Citation.mjs";
11
12
  import { t as Music_d_exports } from "./domain/Music.mjs";
12
- import { t as Transcript_d_exports } from "./domain/Transcript.mjs";
13
+ import { t as Job_d_exports } from "./job/Job.mjs";
13
14
  import { a as StructuredFormat_d_exports } from "./StructuredFormat-DHaUJBdd.mjs";
14
15
  import { t as Turn_d_exports } from "./domain/Turn.mjs";
16
+ import { t as Research_d_exports } from "./domain/Research.mjs";
17
+ import { t as Transcript_d_exports } from "./domain/Transcript.mjs";
15
18
  import { t as Embedding_d_exports } from "./embedding-model/Embedding.mjs";
16
19
  import { t as EmbeddingModel_d_exports } from "./embedding-model/EmbeddingModel.mjs";
20
+ import { t as DeepResearch_d_exports } from "./research/DeepResearch.mjs";
17
21
  import { t as Loop_d_exports } from "./Loop-D8nWpowc.mjs";
18
- import { n as ToolResult_d_exports } from "./ToolResult-CL4hK_9z.mjs";
19
- import { n as ToolEvent_d_exports } from "./ToolEvent-cWZ2BnbS.mjs";
20
- import { l as Toolkit_d_exports } from "./Toolkit-Dr9l_hNy.mjs";
22
+ import { n as ToolResult_d_exports } from "./ToolResult-CQqKkcDa.mjs";
23
+ import { n as ToolEvent_d_exports } from "./ToolEvent-nVijCrvm.mjs";
24
+ import { l as Toolkit_d_exports } from "./Toolkit-BYPwwu-j.mjs";
21
25
  import { t as LanguageModel_d_exports } from "./language-model/LanguageModel.mjs";
22
26
  import { t as MusicGenerator_d_exports } from "./music-generator/MusicGenerator.mjs";
23
27
  import { t as Memory_d_exports } from "./sandbox/Memory.mjs";
@@ -40,4 +44,4 @@ import { t as Lines_d_exports } from "./streaming/Lines.mjs";
40
44
  import { t as SSE_d_exports } from "./streaming/SSE.mjs";
41
45
  import { t as Metrics_d_exports } from "./observability/Metrics.mjs";
42
46
  import { t as Telemetry_d_exports } from "./observability/Telemetry.mjs";
43
- export { AiError_d_exports as AiError, Approval_d_exports as Approval, Audio_d_exports as Audio, Browser_d_exports as Browser, BrowserError_d_exports as BrowserError, BrowserTool_d_exports as BrowserTool, Capabilities_d_exports as Capabilities, Embedding_d_exports as Embedding, EmbeddingModel_d_exports as EmbeddingModel, HistoryCheck_d_exports as HistoryCheck, Image_d_exports as Image, Items_d_exports as Items, JSONL_d_exports as JSONL, LanguageModel_d_exports as LanguageModel, Lines_d_exports as Lines, Loop_d_exports as Loop, Media_d_exports as Media, Memory_d_exports as Memory, Metrics_d_exports as Metrics, Music_d_exports as Music, MusicGenerator_d_exports as MusicGenerator, Retry_d_exports as Retry, SSE_d_exports as SSE, Sandbox_d_exports as Sandbox, SandboxError_d_exports as SandboxError, SandboxImage_d_exports as SandboxImage, SandboxNetwork_d_exports as SandboxNetwork, SpeechSynthesizer_d_exports as SpeechSynthesizer, StructuredFormat_d_exports as StructuredFormat, Telemetry_d_exports as Telemetry, Tool_d_exports as Tool, ToolEvent_d_exports as ToolEvent, ToolResult_d_exports as ToolResult, Toolkit_d_exports as Toolkit, Transcriber_d_exports as Transcriber, Transcript_d_exports as Transcript, Turn_d_exports as Turn, Vector_d_exports as Vector, WebRead_d_exports as WebRead, WebReadTool_d_exports as WebReadTool, WebSearch_d_exports as WebSearch, WebSearchTool_d_exports as WebSearchTool };
47
+ export { AiError_d_exports as AiError, Approval_d_exports as Approval, Audio_d_exports as Audio, Browser_d_exports as Browser, BrowserError_d_exports as BrowserError, BrowserTool_d_exports as BrowserTool, Capabilities_d_exports as Capabilities, Citation_d_exports as Citation, DeepResearch_d_exports as DeepResearch, Embedding_d_exports as Embedding, EmbeddingModel_d_exports as EmbeddingModel, HistoryCheck_d_exports as HistoryCheck, Image_d_exports as Image, Items_d_exports as Items, JSONL_d_exports as JSONL, Job_d_exports as Job, LanguageModel_d_exports as LanguageModel, Lines_d_exports as Lines, Loop_d_exports as Loop, Media_d_exports as Media, Memory_d_exports as Memory, Metrics_d_exports as Metrics, Music_d_exports as Music, MusicGenerator_d_exports as MusicGenerator, Research_d_exports as Research, Retry_d_exports as Retry, SSE_d_exports as SSE, Sandbox_d_exports as Sandbox, SandboxError_d_exports as SandboxError, SandboxImage_d_exports as SandboxImage, SandboxNetwork_d_exports as SandboxNetwork, SpeechSynthesizer_d_exports as SpeechSynthesizer, StructuredFormat_d_exports as StructuredFormat, Telemetry_d_exports as Telemetry, Tool_d_exports as Tool, ToolEvent_d_exports as ToolEvent, ToolResult_d_exports as ToolResult, Toolkit_d_exports as Toolkit, Transcriber_d_exports as Transcriber, Transcript_d_exports as Transcript, Turn_d_exports as Turn, Vector_d_exports as Vector, WebRead_d_exports as WebRead, WebReadTool_d_exports as WebReadTool, WebSearch_d_exports as WebSearch, WebSearchTool_d_exports as WebSearchTool };
package/dist/index.mjs CHANGED
@@ -13,8 +13,12 @@ import { t as ToolEvent_exports } from "./tool/ToolEvent.mjs";
13
13
  import { t as Toolkit_exports } from "./tool/Toolkit.mjs";
14
14
  import { t as BrowserTool_exports } from "./browser/BrowserTool.mjs";
15
15
  import { t as Capabilities_exports } from "./capabilities/Capabilities.mjs";
16
+ import { t as Citation_exports } from "./domain/Citation.mjs";
17
+ import { t as Job_exports } from "./job/Job.mjs";
18
+ import { t as DeepResearch_exports } from "./research/DeepResearch.mjs";
16
19
  import { t as Media_exports } from "./domain/Media.mjs";
17
20
  import { t as Music_exports } from "./domain/Music.mjs";
21
+ import { t as Research_exports } from "./domain/Research.mjs";
18
22
  import { t as Transcript_exports } from "./domain/Transcript.mjs";
19
23
  import { t as Embedding_exports } from "./embedding-model/Embedding.mjs";
20
24
  import { t as EmbeddingModel_exports } from "./embedding-model/EmbeddingModel.mjs";
@@ -40,4 +44,4 @@ import { t as Lines_exports } from "./streaming/Lines.mjs";
40
44
  import { t as SSE_exports } from "./streaming/SSE.mjs";
41
45
  import { t as Metrics_exports } from "./observability/Metrics.mjs";
42
46
  import { t as Telemetry_exports } from "./observability/Telemetry.mjs";
43
- export { AiError_exports as AiError, Approval_exports as Approval, Audio_exports as Audio, Browser_exports as Browser, BrowserError_exports as BrowserError, BrowserTool_exports as BrowserTool, Capabilities_exports as Capabilities, Embedding_exports as Embedding, EmbeddingModel_exports as EmbeddingModel, HistoryCheck_exports as HistoryCheck, Image_exports as Image, Items_exports as Items, JSONL_exports as JSONL, LanguageModel_exports as LanguageModel, Lines_exports as Lines, Loop_exports as Loop, Media_exports as Media, Memory_exports as Memory, Metrics_exports as Metrics, Music_exports as Music, MusicGenerator_exports as MusicGenerator, Retry_exports as Retry, SSE_exports as SSE, Sandbox_exports as Sandbox, SandboxError_exports as SandboxError, SandboxImage_exports as SandboxImage, SandboxNetwork_exports as SandboxNetwork, SpeechSynthesizer_exports as SpeechSynthesizer, StructuredFormat_exports as StructuredFormat, Telemetry_exports as Telemetry, Tool_exports as Tool, ToolEvent_exports as ToolEvent, ToolResult_exports as ToolResult, Toolkit_exports as Toolkit, Transcriber_exports as Transcriber, Transcript_exports as Transcript, Turn_exports as Turn, Vector_exports as Vector, WebRead_exports as WebRead, WebReadTool_exports as WebReadTool, WebSearch_exports as WebSearch, WebSearchTool_exports as WebSearchTool };
47
+ export { AiError_exports as AiError, Approval_exports as Approval, Audio_exports as Audio, Browser_exports as Browser, BrowserError_exports as BrowserError, BrowserTool_exports as BrowserTool, Capabilities_exports as Capabilities, Citation_exports as Citation, DeepResearch_exports as DeepResearch, Embedding_exports as Embedding, EmbeddingModel_exports as EmbeddingModel, HistoryCheck_exports as HistoryCheck, Image_exports as Image, Items_exports as Items, JSONL_exports as JSONL, Job_exports as Job, LanguageModel_exports as LanguageModel, Lines_exports as Lines, Loop_exports as Loop, Media_exports as Media, Memory_exports as Memory, Metrics_exports as Metrics, Music_exports as Music, MusicGenerator_exports as MusicGenerator, Research_exports as Research, Retry_exports as Retry, SSE_exports as SSE, Sandbox_exports as Sandbox, SandboxError_exports as SandboxError, SandboxImage_exports as SandboxImage, SandboxNetwork_exports as SandboxNetwork, SpeechSynthesizer_exports as SpeechSynthesizer, StructuredFormat_exports as StructuredFormat, Telemetry_exports as Telemetry, Tool_exports as Tool, ToolEvent_exports as ToolEvent, ToolResult_exports as ToolResult, Toolkit_exports as Toolkit, Transcriber_exports as Transcriber, Transcript_exports as Transcript, Turn_exports as Turn, Vector_exports as Vector, WebRead_exports as WebRead, WebReadTool_exports as WebReadTool, WebSearch_exports as WebSearch, WebSearchTool_exports as WebSearchTool };
@@ -0,0 +1,80 @@
1
+ import { t as AiError } from "../AiError-Bf1pq4BB.mjs";
2
+ import { Duration, Effect } from "effect";
3
+
4
+ //#region src/job/Job.d.ts
5
+ declare namespace Job_d_exports {
6
+ export { JobConfig, JobOps, JobRef, JobState, collect, isSettled, jobRef, run };
7
+ }
8
+ /**
9
+ * Normalized state of a provider-hosted background job. Implementors map their
10
+ * provider's status vocabulary onto these four states. The terminal states
11
+ * carry their payload: `Succeeded` the collected result, `Failed` an optional
12
+ * reason and the raw provider error. This mirrors the wire, where a single
13
+ * poll response returns both status and (when done) the result.
14
+ */
15
+ type JobState<A> = {
16
+ readonly _tag: "Pending";
17
+ } | {
18
+ readonly _tag: "Running";
19
+ } | {
20
+ readonly _tag: "Succeeded";
21
+ readonly result: A;
22
+ } | {
23
+ readonly _tag: "Failed";
24
+ readonly reason?: string;
25
+ readonly raw?: unknown;
26
+ };
27
+ /** The two states that end a poll loop. */
28
+ declare const isSettled: <A>(state: JobState<A>) => state is Extract<JobState<A>, {
29
+ _tag: "Succeeded" | "Failed";
30
+ }>;
31
+ declare const ResultType: unique symbol;
32
+ /**
33
+ * Opaque, provider-tagged handle to a running background job. Jobs can run for
34
+ * minutes and outlive a single process, so the handle is detachable: submit
35
+ * now, collect later. `provider` tags who owns `id`.
36
+ *
37
+ * Parameterized by the result type `A` it collects to, so a ref cannot be
38
+ * crossed between capabilities (a `JobRef<Turn>` is not a `JobRef<VideoResult>`).
39
+ * The brand is phantom: the runtime value is just `{ _tag, provider, id }`, so
40
+ * a ref stays plain serializable data for persistence across restarts.
41
+ */
42
+ type JobRef<A = unknown> = {
43
+ readonly _tag: "JobRef";
44
+ readonly provider: string;
45
+ readonly id: string;
46
+ readonly [ResultType]?: A;
47
+ };
48
+ declare const jobRef: <A = unknown>(provider: string, id: string) => JobRef<A>;
49
+ /**
50
+ * The three wire operations a poll-based async job supplies. `run` / `collect`
51
+ * drive the submit -> poll -> settle cadence around them, so an implementor
52
+ * states only its provider calls, not the loop. `poll` returns the current
53
+ * `JobState`, carrying the result in `Succeeded`. Generic over the collected
54
+ * `Result`.
55
+ */
56
+ type JobOps<Result> = {
57
+ readonly submit: Effect.Effect<JobRef<Result>, AiError>;
58
+ readonly poll: (ref: JobRef<Result>) => Effect.Effect<JobState<Result>, AiError>;
59
+ readonly cancel: (ref: JobRef<Result>) => Effect.Effect<void, AiError>;
60
+ };
61
+ type JobConfig = {
62
+ /** Cadence between status fetches. Default 10 seconds, jittered. */readonly pollInterval?: Duration.Input; /** Overall wait cap; exceeding it fails `Timeout`. Default 45 minutes. */
63
+ readonly timeout?: Duration.Input;
64
+ };
65
+ /**
66
+ * Poll `poll(ref)` on a jittered fixed cadence until the job settles, then
67
+ * return the result or fail `GenerationFailed`. Bounded by `timeout`, which
68
+ * fails `Timeout` if the job never settles. Takes the poll op alone (not the
69
+ * full `JobOps`) so a detached `collect(ref)` needs no submit-bound request.
70
+ */
71
+ declare const collect: <Result>(poll: (ref: JobRef<Result>) => Effect.Effect<JobState<Result>, AiError>, ref: JobRef<Result>, config?: JobConfig) => Effect.Effect<Result, AiError>;
72
+ /**
73
+ * Submit, then poll to completion. Best-effort cancels the server job if the
74
+ * caller interrupts (where the provider supports it). The one-call path; use
75
+ * `ops.submit` + `collect` directly for the detached case.
76
+ */
77
+ declare const run: <Result>(ops: JobOps<Result>, config?: JobConfig) => Effect.Effect<Result, AiError>;
78
+ //#endregion
79
+ export { JobConfig, JobOps, JobRef, JobState, collect, isSettled, jobRef, run, Job_d_exports as t };
80
+ //# sourceMappingURL=Job.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Job.d.mts","names":[],"sources":["../../src/job/Job.ts"],"mappings":";;;;;;;;;;;;;;KAcY,QAAA;EAAA,SACG,IAAA;AAAA;EAAA,SACA,IAAA;AAAA;EAAA,SACA,IAAA;EAAA,SAA4B,MAAA,EAAQ,CAAA;AAAA;EAAA,SACpC,IAAA;EAAA,SAAyB,MAAA;EAAA,SAA0B,GAAA;AAAA;;cAGrD,SAAA,MACX,KAAA,EAAO,QAAA,CAAS,CAAA,MACf,KAAA,IAAS,OAAA,CAAQ,QAAA,CAAS,CAAA;EAAM,IAAA;AAAA;AAAA,cAGrB,UAAA;;;;;AALd;;;;;;KAiBY,MAAA;EAAA,SACD,IAAA;EAAA,SACA,QAAA;EAAA,SACA,EAAA;EAAA,UACC,UAAA,IAAc,CAAA;AAAA;AAAA,cAGb,MAAA,gBAAuB,QAAA,UAAkB,EAAA,aAAa,MAAA,CAAO,CAAA;;;;;;;;KAiB9D,MAAA;EAAA,SACD,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,OAAA;EAAA,SACtC,IAAA,GAAO,GAAA,EAAK,MAAA,CAAO,MAAA,MAAY,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,MAAA,GAAS,OAAA;EAAA,SAC/D,MAAA,GAAS,GAAA,EAAK,MAAA,CAAO,MAAA,MAAY,MAAA,CAAO,MAAA,OAAa,OAAA;AAAA;AAAA,KAGpD,SAAA;EA9BA,6EAgCD,YAAA,GAAe,QAAA,CAAS,KAAA,EAhCjB;EAAA,SAkCP,OAAA,GAAU,QAAA,CAAS,KAAA;AAAA;;;;;;;cAYjB,OAAA,WACX,IAAA,GAAO,GAAA,EAAK,MAAA,CAAO,MAAA,MAAY,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,MAAA,GAAS,OAAA,GAC/D,GAAA,EAAK,MAAA,CAAO,MAAA,GACZ,MAAA,GAAS,SAAA,KACR,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,OAAA;AA3CzB;;;;;AAAA,cAuEa,GAAA,WACX,GAAA,EAAK,MAAA,CAAO,MAAA,GACZ,MAAA,GAAS,SAAA,KACR,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,OAAA"}
@@ -0,0 +1,49 @@
1
+ import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
2
+ import { GenerationFailed, Timeout } from "../domain/AiError.mjs";
3
+ import { Effect, Schedule } from "effect";
4
+ //#region src/job/Job.ts
5
+ var Job_exports = /* @__PURE__ */ __exportAll({
6
+ collect: () => collect,
7
+ isSettled: () => isSettled,
8
+ jobRef: () => jobRef,
9
+ run: () => run
10
+ });
11
+ /** The two states that end a poll loop. */
12
+ const isSettled = (state) => state._tag === "Succeeded" || state._tag === "Failed";
13
+ const jobRef = (provider, id) => ({
14
+ _tag: "JobRef",
15
+ provider,
16
+ id
17
+ });
18
+ const DEFAULT_POLL_INTERVAL = "10 seconds";
19
+ const DEFAULT_TIMEOUT = "45 minutes";
20
+ /**
21
+ * Poll `poll(ref)` on a jittered fixed cadence until the job settles, then
22
+ * return the result or fail `GenerationFailed`. Bounded by `timeout`, which
23
+ * fails `Timeout` if the job never settles. Takes the poll op alone (not the
24
+ * full `JobOps`) so a detached `collect(ref)` needs no submit-bound request.
25
+ */
26
+ const collect = (poll, ref, config) => poll(ref).pipe(Effect.repeat({
27
+ schedule: Schedule.jittered(Schedule.spaced(config?.pollInterval ?? DEFAULT_POLL_INTERVAL)),
28
+ until: isSettled
29
+ }), Effect.flatMap((state) => state._tag === "Succeeded" ? Effect.succeed(state.result) : Effect.fail(new GenerationFailed({
30
+ provider: ref.provider,
31
+ message: state._tag === "Failed" ? state.reason ?? "job failed" : "job failed",
32
+ raw: state._tag === "Failed" ? state.raw ?? ref : ref
33
+ }))), Effect.timeoutOrElse({
34
+ duration: config?.timeout ?? DEFAULT_TIMEOUT,
35
+ orElse: () => Effect.fail(new Timeout({
36
+ provider: ref.provider,
37
+ raw: ref
38
+ }))
39
+ }));
40
+ /**
41
+ * Submit, then poll to completion. Best-effort cancels the server job if the
42
+ * caller interrupts (where the provider supports it). The one-call path; use
43
+ * `ops.submit` + `collect` directly for the detached case.
44
+ */
45
+ const run = (ops, config) => ops.submit.pipe(Effect.flatMap((ref) => collect(ops.poll, ref, config).pipe(Effect.onInterrupt(() => Effect.ignore(ops.cancel(ref))))));
46
+ //#endregion
47
+ export { collect, isSettled, jobRef, run, Job_exports as t };
48
+
49
+ //# sourceMappingURL=Job.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Job.mjs","names":["AiError.GenerationFailed","AiError.Timeout"],"sources":["../../src/job/Job.ts"],"sourcesContent":["import { Duration, Effect, Schedule } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\n\n// ---------------------------------------------------------------------------\n// Status + handle\n// ---------------------------------------------------------------------------\n\n/**\n * Normalized state of a provider-hosted background job. Implementors map their\n * provider's status vocabulary onto these four states. The terminal states\n * carry their payload: `Succeeded` the collected result, `Failed` an optional\n * reason and the raw provider error. This mirrors the wire, where a single\n * poll response returns both status and (when done) the result.\n */\nexport type JobState<A> =\n | { readonly _tag: \"Pending\" }\n | { readonly _tag: \"Running\" }\n | { readonly _tag: \"Succeeded\"; readonly result: A }\n | { readonly _tag: \"Failed\"; readonly reason?: string; readonly raw?: unknown }\n\n/** The two states that end a poll loop. */\nexport const isSettled = <A>(\n state: JobState<A>,\n): state is Extract<JobState<A>, { _tag: \"Succeeded\" | \"Failed\" }> =>\n state._tag === \"Succeeded\" || state._tag === \"Failed\"\n\ndeclare const ResultType: unique symbol\n\n/**\n * Opaque, provider-tagged handle to a running background job. Jobs can run for\n * minutes and outlive a single process, so the handle is detachable: submit\n * now, collect later. `provider` tags who owns `id`.\n *\n * Parameterized by the result type `A` it collects to, so a ref cannot be\n * crossed between capabilities (a `JobRef<Turn>` is not a `JobRef<VideoResult>`).\n * The brand is phantom: the runtime value is just `{ _tag, provider, id }`, so\n * a ref stays plain serializable data for persistence across restarts.\n */\nexport type JobRef<A = unknown> = {\n readonly _tag: \"JobRef\"\n readonly provider: string\n readonly id: string\n readonly [ResultType]?: A\n}\n\nexport const jobRef = <A = unknown>(provider: string, id: string): JobRef<A> => ({\n _tag: \"JobRef\",\n provider,\n id,\n})\n\n// ---------------------------------------------------------------------------\n// Poll driver\n// ---------------------------------------------------------------------------\n\n/**\n * The three wire operations a poll-based async job supplies. `run` / `collect`\n * drive the submit -> poll -> settle cadence around them, so an implementor\n * states only its provider calls, not the loop. `poll` returns the current\n * `JobState`, carrying the result in `Succeeded`. Generic over the collected\n * `Result`.\n */\nexport type JobOps<Result> = {\n readonly submit: Effect.Effect<JobRef<Result>, AiError.AiError>\n readonly poll: (ref: JobRef<Result>) => Effect.Effect<JobState<Result>, AiError.AiError>\n readonly cancel: (ref: JobRef<Result>) => Effect.Effect<void, AiError.AiError>\n}\n\nexport type JobConfig = {\n /** Cadence between status fetches. Default 10 seconds, jittered. */\n readonly pollInterval?: Duration.Input\n /** Overall wait cap; exceeding it fails `Timeout`. Default 45 minutes. */\n readonly timeout?: Duration.Input\n}\n\nconst DEFAULT_POLL_INTERVAL: Duration.Input = \"10 seconds\"\nconst DEFAULT_TIMEOUT: Duration.Input = \"45 minutes\"\n\n/**\n * Poll `poll(ref)` on a jittered fixed cadence until the job settles, then\n * return the result or fail `GenerationFailed`. Bounded by `timeout`, which\n * fails `Timeout` if the job never settles. Takes the poll op alone (not the\n * full `JobOps`) so a detached `collect(ref)` needs no submit-bound request.\n */\nexport const collect = <Result>(\n poll: (ref: JobRef<Result>) => Effect.Effect<JobState<Result>, AiError.AiError>,\n ref: JobRef<Result>,\n config?: JobConfig,\n): Effect.Effect<Result, AiError.AiError> =>\n poll(ref).pipe(\n Effect.repeat({\n schedule: Schedule.jittered(Schedule.spaced(config?.pollInterval ?? DEFAULT_POLL_INTERVAL)),\n until: isSettled,\n }),\n Effect.flatMap((state) =>\n state._tag === \"Succeeded\"\n ? Effect.succeed(state.result)\n : Effect.fail(\n new AiError.GenerationFailed({\n provider: ref.provider,\n message: state._tag === \"Failed\" ? (state.reason ?? \"job failed\") : \"job failed\",\n raw: state._tag === \"Failed\" ? (state.raw ?? ref) : ref,\n }),\n ),\n ),\n Effect.timeoutOrElse({\n duration: config?.timeout ?? DEFAULT_TIMEOUT,\n orElse: () => Effect.fail(new AiError.Timeout({ provider: ref.provider, raw: ref })),\n }),\n )\n\n/**\n * Submit, then poll to completion. Best-effort cancels the server job if the\n * caller interrupts (where the provider supports it). The one-call path; use\n * `ops.submit` + `collect` directly for the detached case.\n */\nexport const run = <Result>(\n ops: JobOps<Result>,\n config?: JobConfig,\n): Effect.Effect<Result, AiError.AiError> =>\n ops.submit.pipe(\n Effect.flatMap((ref) =>\n collect(ops.poll, ref, config).pipe(Effect.onInterrupt(() => Effect.ignore(ops.cancel(ref)))),\n ),\n )\n"],"mappings":";;;;;;;;;;;AAqBA,MAAa,aACX,UAEA,MAAM,SAAS,eAAe,MAAM,SAAS;AAqB/C,MAAa,UAAuB,UAAkB,QAA2B;CAC/E,MAAM;CACN;CACA;CACD;AA0BD,MAAM,wBAAwC;AAC9C,MAAM,kBAAkC;;;;;;;AAQxC,MAAa,WACX,MACA,KACA,WAEA,KAAK,IAAI,CAAC,KACR,OAAO,OAAO;CACZ,UAAU,SAAS,SAAS,SAAS,OAAO,QAAQ,gBAAgB,sBAAsB,CAAC;CAC3F,OAAO;CACR,CAAC,EACF,OAAO,SAAS,UACd,MAAM,SAAS,cACX,OAAO,QAAQ,MAAM,OAAO,GAC5B,OAAO,KACL,IAAIA,iBAAyB;CAC3B,UAAU,IAAI;CACd,SAAS,MAAM,SAAS,WAAY,MAAM,UAAU,eAAgB;CACpE,KAAK,MAAM,SAAS,WAAY,MAAM,OAAO,MAAO;CACrD,CAAC,CACH,CACN,EACD,OAAO,cAAc;CACnB,UAAU,QAAQ,WAAW;CAC7B,cAAc,OAAO,KAAK,IAAIC,QAAgB;EAAE,UAAU,IAAI;EAAU,KAAK;EAAK,CAAC,CAAC;CACrF,CAAC,CACH;;;;;;AAOH,MAAa,OACX,KACA,WAEA,IAAI,OAAO,KACT,OAAO,SAAS,QACd,QAAQ,IAAI,MAAM,KAAK,OAAO,CAAC,KAAK,OAAO,kBAAkB,OAAO,OAAO,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAC9F,CACF"}
@@ -1,8 +1,8 @@
1
- import { o as HistoryItem } from "../Items-BnIduEV2.mjs";
1
+ import { o as HistoryItem } from "../Items-BWNu6VGp.mjs";
2
2
  import { t as AiError } from "../AiError-Bf1pq4BB.mjs";
3
3
  import { i as StructuredFormat } from "../StructuredFormat-DHaUJBdd.mjs";
4
4
  import { Turn, TurnEvent } from "../domain/Turn.mjs";
5
- import { o as Toolkit } from "../Toolkit-Dr9l_hNy.mjs";
5
+ import { o as Toolkit } from "../Toolkit-BYPwwu-j.mjs";
6
6
  import { Context, Effect, Stream } from "effect";
7
7
 
8
8
  //#region src/language-model/LanguageModel.d.ts
@@ -13,14 +13,14 @@ var LanguageModel = class extends Context.Service()("@betalyra/effect-uai/Langua
13
13
  /**
14
14
  * Stream the deltas of a single turn.
15
15
  */
16
- const streamTurn = (request) => Stream.unwrap(Effect.map(LanguageModel.asEffect(), (m) => m.streamTurn(request)));
16
+ const streamTurn = (request) => Stream.unwrap(Effect.map(LanguageModel, (m) => m.streamTurn(request)));
17
17
  /**
18
18
  * Drain a single turn and return the assembled `Turn`. Delegates to the
19
19
  * service's `turn` method — providers with a native complete endpoint
20
20
  * can override; the rest get the default streamTurn-drain via
21
21
  * {@link turnFromStream}.
22
22
  */
23
- const turn = (request) => Effect.flatMap(LanguageModel.asEffect(), (m) => m.turn(request));
23
+ const turn = (request) => Effect.flatMap(LanguageModel, (m) => m.turn(request));
24
24
  /**
25
25
  * Build a `turn` implementation from a `streamTurn` implementation.
26
26
  * Providers without a native non-streaming endpoint use this to
@@ -1 +1 @@
1
- {"version":3,"file":"LanguageModel.mjs","names":["Arr","AiError.IncompleteTurn"],"sources":["../../src/language-model/LanguageModel.ts"],"sourcesContent":["import { Array as Arr, Context, Effect, Option, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { HistoryItem } from \"../domain/Items.js\"\nimport type * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport type { Toolkit } from \"../tool/Toolkit.js\"\nimport { isTurnComplete, type Turn, type TurnEvent } from \"../domain/Turn.js\"\n\n/**\n * Cross-provider request shape. Every call carries its own `history` and\n * `model` - models are not bound at layer construction. Anything specific\n * to a single provider (reasoning effort, prompt caching, store flags,\n * ...) lives in that provider's own request interface, which extends this.\n */\nexport type CommonRequest = {\n readonly history: ReadonlyArray<HistoryItem>\n /**\n * Model identifier. Each provider narrows this to its typed literal union,\n * so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n /**\n * The model-visible tools for this request, as a `Toolkit`. The provider\n * renders it to wire descriptors (`Tool.descriptorsOf`); omit it for a turn\n * with no tools.\n */\n readonly tools?: Toolkit\n readonly toolChoice?:\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n readonly temperature?: number\n readonly topP?: number\n readonly maxOutputTokens?: number\n /**\n * Schema-bound JSON output. The provider constrains the wire to match the\n * schema; pair with `Turn.decodeStructured` for runtime validation. Supported\n * across all current providers (OpenAI Responses json_schema, Anthropic\n * `output_config`, Gemini `responseJsonSchema`).\n */\n readonly structured?: StructuredFormat.StructuredFormat<unknown>\n}\n\nexport type LanguageModelService = {\n readonly streamTurn: (request: CommonRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn` from the\n * terminal `TurnComplete` event. Fails with `IncompleteTurn` if the\n * stream ends without one.\n *\n * Most providers derive this from `streamTurn` via\n * {@link turnFromStream}; providers with a native non-streaming\n * endpoint may override with a cheaper direct call.\n */\n readonly turn: (request: CommonRequest) => Effect.Effect<Turn, AiError.AiError>\n}\n\nexport class LanguageModel extends Context.Service<LanguageModel, LanguageModelService>()(\n \"@betalyra/effect-uai/LanguageModel\",\n) {}\n\n/**\n * Stream the deltas of a single turn.\n */\nexport const streamTurn = (\n request: CommonRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, LanguageModel> =>\n Stream.unwrap(Effect.map(LanguageModel.asEffect(), (m) => m.streamTurn(request)))\n\n/**\n * Drain a single turn and return the assembled `Turn`. Delegates to the\n * service's `turn` method — providers with a native complete endpoint\n * can override; the rest get the default streamTurn-drain via\n * {@link turnFromStream}.\n */\nexport const turn = (request: CommonRequest): Effect.Effect<Turn, AiError.AiError, LanguageModel> =>\n Effect.flatMap(LanguageModel.asEffect(), (m) => m.turn(request))\n\n/**\n * Build a `turn` implementation from a `streamTurn` implementation.\n * Providers without a native non-streaming endpoint use this to\n * populate the service's `turn` field. Generic over the request type so\n * provider-typed services (with their narrowed request) can reuse it.\n */\nexport const turnFromStream =\n <Req>(streamTurn: (request: Req) => Stream.Stream<TurnEvent, AiError.AiError>) =>\n (request: Req): Effect.Effect<Turn, AiError.AiError> =>\n streamTurn(request).pipe(\n Stream.runCollect,\n Effect.flatMap((events) =>\n Arr.findLast(events, isTurnComplete).pipe(\n Option.match({\n onNone: () => Effect.fail(new AiError.IncompleteTurn({})),\n onSome: (e) => Effect.succeed(e.turn),\n }),\n ),\n ),\n )\n"],"mappings":";;;;;;;;;;;AAyDA,IAAa,gBAAb,cAAmC,QAAQ,SAA8C,CACvF,qCACD,CAAC;;;;AAKF,MAAa,cACX,YAEA,OAAO,OAAO,OAAO,IAAI,cAAc,UAAU,GAAG,MAAM,EAAE,WAAW,QAAQ,CAAC,CAAC;;;;;;;AAQnF,MAAa,QAAQ,YACnB,OAAO,QAAQ,cAAc,UAAU,GAAG,MAAM,EAAE,KAAK,QAAQ,CAAC;;;;;;;AAQlE,MAAa,kBACL,gBACL,YACC,WAAW,QAAQ,CAAC,KAClB,OAAO,YACP,OAAO,SAAS,WACdA,MAAI,SAAS,QAAQ,eAAe,CAAC,KACnC,OAAO,MAAM;CACX,cAAc,OAAO,KAAK,IAAIC,eAAuB,EAAE,CAAC,CAAC;CACzD,SAAS,MAAM,OAAO,QAAQ,EAAE,KAAK;CACtC,CAAC,CACH,CACF,CACF"}
1
+ {"version":3,"file":"LanguageModel.mjs","names":["Arr","AiError.IncompleteTurn"],"sources":["../../src/language-model/LanguageModel.ts"],"sourcesContent":["import { Array as Arr, Context, Effect, Option, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { HistoryItem } from \"../domain/Items.js\"\nimport type * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport type { Toolkit } from \"../tool/Toolkit.js\"\nimport { isTurnComplete, type Turn, type TurnEvent } from \"../domain/Turn.js\"\n\n/**\n * Cross-provider request shape. Every call carries its own `history` and\n * `model` - models are not bound at layer construction. Anything specific\n * to a single provider (reasoning effort, prompt caching, store flags,\n * ...) lives in that provider's own request interface, which extends this.\n */\nexport type CommonRequest = {\n readonly history: ReadonlyArray<HistoryItem>\n /**\n * Model identifier. Each provider narrows this to its typed literal union,\n * so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n /**\n * The model-visible tools for this request, as a `Toolkit`. The provider\n * renders it to wire descriptors (`Tool.descriptorsOf`); omit it for a turn\n * with no tools.\n */\n readonly tools?: Toolkit\n readonly toolChoice?:\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n readonly temperature?: number\n readonly topP?: number\n readonly maxOutputTokens?: number\n /**\n * Schema-bound JSON output. The provider constrains the wire to match the\n * schema; pair with `Turn.decodeStructured` for runtime validation. Supported\n * across all current providers (OpenAI Responses json_schema, Anthropic\n * `output_config`, Gemini `responseJsonSchema`).\n */\n readonly structured?: StructuredFormat.StructuredFormat<unknown>\n}\n\nexport type LanguageModelService = {\n readonly streamTurn: (request: CommonRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n /**\n * Drain a single turn and return the assembled `Turn` from the\n * terminal `TurnComplete` event. Fails with `IncompleteTurn` if the\n * stream ends without one.\n *\n * Most providers derive this from `streamTurn` via\n * {@link turnFromStream}; providers with a native non-streaming\n * endpoint may override with a cheaper direct call.\n */\n readonly turn: (request: CommonRequest) => Effect.Effect<Turn, AiError.AiError>\n}\n\nexport class LanguageModel extends Context.Service<LanguageModel, LanguageModelService>()(\n \"@betalyra/effect-uai/LanguageModel\",\n) {}\n\n/**\n * Stream the deltas of a single turn.\n */\nexport const streamTurn = (\n request: CommonRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, LanguageModel> =>\n Stream.unwrap(Effect.map(LanguageModel, (m) => m.streamTurn(request)))\n\n/**\n * Drain a single turn and return the assembled `Turn`. Delegates to the\n * service's `turn` method — providers with a native complete endpoint\n * can override; the rest get the default streamTurn-drain via\n * {@link turnFromStream}.\n */\nexport const turn = (request: CommonRequest): Effect.Effect<Turn, AiError.AiError, LanguageModel> =>\n Effect.flatMap(LanguageModel, (m) => m.turn(request))\n\n/**\n * Build a `turn` implementation from a `streamTurn` implementation.\n * Providers without a native non-streaming endpoint use this to\n * populate the service's `turn` field. Generic over the request type so\n * provider-typed services (with their narrowed request) can reuse it.\n */\nexport const turnFromStream =\n <Req>(streamTurn: (request: Req) => Stream.Stream<TurnEvent, AiError.AiError>) =>\n (request: Req): Effect.Effect<Turn, AiError.AiError> =>\n streamTurn(request).pipe(\n Stream.runCollect,\n Effect.flatMap((events) =>\n Arr.findLast(events, isTurnComplete).pipe(\n Option.match({\n onNone: () => Effect.fail(new AiError.IncompleteTurn({})),\n onSome: (e) => Effect.succeed(e.turn),\n }),\n ),\n ),\n )\n"],"mappings":";;;;;;;;;;;AAyDA,IAAa,gBAAb,cAAmC,QAAQ,SAA8C,CACvF,qCACD,CAAC;;;;AAKF,MAAa,cACX,YAEA,OAAO,OAAO,OAAO,IAAI,gBAAgB,MAAM,EAAE,WAAW,QAAQ,CAAC,CAAC;;;;;;;AAQxE,MAAa,QAAQ,YACnB,OAAO,QAAQ,gBAAgB,MAAM,EAAE,KAAK,QAAQ,CAAC;;;;;;;AAQvD,MAAa,kBACL,gBACL,YACC,WAAW,QAAQ,CAAC,KAClB,OAAO,YACP,OAAO,SAAS,WACdA,MAAI,SAAS,QAAQ,eAAe,CAAC,KACnC,OAAO,MAAM;CACX,cAAc,OAAO,KAAK,IAAIC,eAAuB,EAAE,CAAC,CAAC;CACzD,SAAS,MAAM,OAAO,QAAQ,EAAE,KAAK;CACtC,CAAC,CACH,CACF,CACF"}
@@ -2,7 +2,7 @@ import { IncompleteTurn, RateLimited } from "../domain/AiError.mjs";
2
2
  import { assistantText, userText } from "../domain/Items.mjs";
3
3
  import { TurnEvent } from "../domain/Turn.mjs";
4
4
  import { LanguageModel, turn, turnFromStream } from "./LanguageModel.mjs";
5
- import { a as describe, n as globalExpect, o as it } from "../dist-B04dlFHg.mjs";
5
+ import { c as it, n as globalExpect, o as describe } from "../dist-CcRV3-A5.mjs";
6
6
  import { layer } from "../testing/MockProvider.mjs";
7
7
  import { Cause, Effect, Exit, Layer, Option, Stream } from "effect";
8
8
  //#region src/language-model/LanguageModel.test.ts
@@ -1,7 +1,7 @@
1
1
  import { RateLimited } from "../domain/AiError.mjs";
2
2
  import { TurnEvent } from "../domain/Turn.mjs";
3
3
  import { loop, loopOver, loopWithState, next, onTurnComplete, stop, value } from "./Loop.mjs";
4
- import { a as describe, n as globalExpect, o as it, t as import_dist } from "../dist-B04dlFHg.mjs";
4
+ import { c as it, n as globalExpect, o as describe, t as import_dist } from "../dist-CcRV3-A5.mjs";
5
5
  import { Deferred, Effect, Fiber, Latch, Ref, Stream, SubscriptionRef, pipe } from "effect";
6
6
  //#region src/loop/Loop.test.ts
7
7
  describe("Loop.loop", () => {
@@ -21,9 +21,9 @@ var MusicGenerator = class extends Context.Service()("@betalyra/effect-uai/Music
21
21
  */
22
22
  var MusicInteractiveSession = class extends Context.Service()("@betalyra/effect-uai/capability/MusicInteractiveSession") {};
23
23
  /** One-shot generation. Returns the full `GenerateResult`. */
24
- const generate = (request) => Effect.flatMap(MusicGenerator.asEffect(), (s) => s.generate(request));
24
+ const generate = (request) => Effect.flatMap(MusicGenerator, (s) => s.generate(request));
25
25
  /** Prompt in, audio chunks out. */
26
- const streamGeneration = (request) => Stream.unwrap(Effect.map(MusicGenerator.asEffect(), (s) => s.streamGeneration(request)));
26
+ const streamGeneration = (request) => Stream.unwrap(Effect.map(MusicGenerator, (s) => s.streamGeneration(request)));
27
27
  /**
28
28
  * Bidirectional generation. Dual-arity: pipeable (data-last) and
29
29
  * direct (data-first). Requires `MusicInteractiveSession` in R,
@@ -49,8 +49,8 @@ const streamGeneration = (request) => Stream.unwrap(Effect.map(MusicGenerator.as
49
49
  * ```
50
50
  */
51
51
  const streamGenerationFrom = Function.dual(2, (input, request) => Stream.unwrap(Effect.gen(function* () {
52
- const s = yield* MusicGenerator.asEffect();
53
- yield* MusicInteractiveSession.asEffect();
52
+ const s = yield* MusicGenerator;
53
+ yield* MusicInteractiveSession;
54
54
  return s.streamGenerationFrom(input, request);
55
55
  })));
56
56
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"MusicGenerator.mjs","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"../domain/Music.js\"\nimport type { AudioChunk } from \"../domain/Audio.js\"\n\nexport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSection,\n MusicSessionControl,\n MusicSessionInput,\n MusicStreamEvent,\n Watermark,\n WeightedPrompt,\n} from \"../domain/Music.js\"\n\nexport type MusicGeneratorService = {\n /**\n * One-shot. Prompt in, full audio bytes out. Universally supported.\n * Async/poll-based providers (Suno, Mureka) hide their poll loop\n * inside the adapter, caller still sees a single `Effect`.\n *\n * Returns `GenerateResult` with `primary` plus a `variants` array.\n * Suno and Mureka always return 2 tracks; every other provider\n * returns 1 (and `primary === variants[0]`).\n */\n readonly generate: (\n request: CommonGenerateMusicRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n /**\n * Prompt in, audio chunks streamed out. Providers without a native\n * chunked-output endpoint (Lyria 3 sync, Mureka, MiniMax, Stable\n * Audio) emulate this by calling `generate` and emitting a single\n * `AudioChunk`, first-class, no `Unsupported`.\n */\n readonly streamGeneration: (\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n /**\n * Bidirectional session: a `Stream` of `MusicSessionInput` (prompts\n * + playback control) flows in, a `Stream` of `MusicStreamEvent`\n * flows out. The session WS / RPC is acquired on first pull and\n * released when the output stream is finalized via `Stream.scoped`.\n *\n * Cross-provider callers send `prompts` (steer the active\n * generation) and `control` (play / pause / stop / reset) — the two\n * actions that converge across interactive-media protocols.\n * Provider-specific model knobs (Lyria RealTime's density /\n * brightness / mute-stems / BPM / scale, etc.) live on the\n * provider-typed service (`LyriaRealtimeGenerator.streamGenerationFrom`),\n * which extends this union with its own `config` variant.\n *\n * Output is `MusicStreamEvent`: `audio` chunks alongside in-band\n * `warning` and `filteredPrompt` events the model emits server-side.\n *\n * Gated by the `MusicInteractiveSession` capability marker on the\n * top-level helper. Providers without bidirectional support don't\n * ship the marker, so calls fail at `Effect.provide` with a type\n * error.\n */\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 MusicGenerator extends Context.Service<MusicGenerator, MusicGeneratorService>()(\n \"@betalyra/effect-uai/MusicGenerator\",\n) {}\n\n/**\n * Capability marker, provided by provider layers whose\n * `streamGenerationFrom` is wired up at the wire level. Currently only\n * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.\n * Calling `streamGenerationFrom` while only a non-interactive Layer is\n * in scope fails at `Effect.provide` with a type error.\n *\n * Phantom: the value is `void`; providers register with\n * `Layer.succeed(MusicInteractiveSession, undefined)`.\n */\nexport class MusicInteractiveSession extends Context.Service<MusicInteractiveSession, void>()(\n \"@betalyra/effect-uai/capability/MusicInteractiveSession\",\n) {}\n\n/** One-shot generation. Returns the full `GenerateResult`. */\nexport const generate = (\n request: CommonGenerateMusicRequest,\n): Effect.Effect<GenerateResult, AiError.AiError, MusicGenerator> =>\n Effect.flatMap(MusicGenerator.asEffect(), (s) => s.generate(request))\n\n/** Prompt in, audio chunks out. */\nexport const streamGeneration = (\n request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator> =>\n Stream.unwrap(Effect.map(MusicGenerator.asEffect(), (s) => s.streamGeneration(request)))\n\n/**\n * Bidirectional generation. Dual-arity: pipeable (data-last) and\n * direct (data-first). Requires `MusicInteractiveSession` in R,\n * providers without bidirectional support are a type error at provide\n * time.\n *\n * On the cross-provider surface, `input` is `Stream<unknown>` and\n * output is `Stream<MusicStreamEvent>`. Use the provider-typed\n * service (`LyriaRealtimeGenerator`) when you want to construct\n * typed session messages.\n *\n * @example\n * ```ts\n * // Generic surface (opaque input):\n * const audio = inputs.pipe(\n * MusicGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n *\n * // Lyria-typed surface (full config knobs):\n * const audio = inputs.pipe(\n * LyriaRealtimeGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n * ```\n */\nexport const streamGenerationFrom: {\n (\n request: CommonStreamGenerateMusicRequest,\n ): <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n ) => Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n} = Function.dual(\n 2,\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n > =>\n Stream.unwrap(\n Effect.gen(function* () {\n const s = yield* MusicGenerator.asEffect()\n yield* MusicInteractiveSession.asEffect()\n return s.streamGenerationFrom(input, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;;AA0EA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;;;;;;;;;;AAYF,IAAa,0BAAb,cAA6C,QAAQ,SAAwC,CAC3F,0DACD,CAAC;;AAGF,MAAa,YACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,SAAS,QAAQ,CAAC;;AAGvE,MAAa,oBACX,YAEA,OAAO,OAAO,OAAO,IAAI,eAAe,UAAU,GAAG,MAAM,EAAE,iBAAiB,QAAQ,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0B1F,MAAa,uBAkBT,SAAS,KACX,IAEE,OACA,YAMA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO,eAAe,UAAU;AAC1C,QAAO,wBAAwB,UAAU;AACzC,QAAO,EAAE,qBAAqB,OAAO,QAAQ;EAC7C,CACH,CACJ"}
1
+ {"version":3,"file":"MusicGenerator.mjs","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"../domain/Music.js\"\nimport type { AudioChunk } from \"../domain/Audio.js\"\n\nexport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSection,\n MusicSessionControl,\n MusicSessionInput,\n MusicStreamEvent,\n Watermark,\n WeightedPrompt,\n} from \"../domain/Music.js\"\n\nexport type MusicGeneratorService = {\n /**\n * One-shot. Prompt in, full audio bytes out. Universally supported.\n * Async/poll-based providers (Suno, Mureka) hide their poll loop\n * inside the adapter, caller still sees a single `Effect`.\n *\n * Returns `GenerateResult` with `primary` plus a `variants` array.\n * Suno and Mureka always return 2 tracks; every other provider\n * returns 1 (and `primary === variants[0]`).\n */\n readonly generate: (\n request: CommonGenerateMusicRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n /**\n * Prompt in, audio chunks streamed out. Providers without a native\n * chunked-output endpoint (Lyria 3 sync, Mureka, MiniMax, Stable\n * Audio) emulate this by calling `generate` and emitting a single\n * `AudioChunk`, first-class, no `Unsupported`.\n */\n readonly streamGeneration: (\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n /**\n * Bidirectional session: a `Stream` of `MusicSessionInput` (prompts\n * + playback control) flows in, a `Stream` of `MusicStreamEvent`\n * flows out. The session WS / RPC is acquired on first pull and\n * released when the output stream is finalized via `Stream.scoped`.\n *\n * Cross-provider callers send `prompts` (steer the active\n * generation) and `control` (play / pause / stop / reset) — the two\n * actions that converge across interactive-media protocols.\n * Provider-specific model knobs (Lyria RealTime's density /\n * brightness / mute-stems / BPM / scale, etc.) live on the\n * provider-typed service (`LyriaRealtimeGenerator.streamGenerationFrom`),\n * which extends this union with its own `config` variant.\n *\n * Output is `MusicStreamEvent`: `audio` chunks alongside in-band\n * `warning` and `filteredPrompt` events the model emits server-side.\n *\n * Gated by the `MusicInteractiveSession` capability marker on the\n * top-level helper. Providers without bidirectional support don't\n * ship the marker, so calls fail at `Effect.provide` with a type\n * error.\n */\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 MusicGenerator extends Context.Service<MusicGenerator, MusicGeneratorService>()(\n \"@betalyra/effect-uai/MusicGenerator\",\n) {}\n\n/**\n * Capability marker, provided by provider layers whose\n * `streamGenerationFrom` is wired up at the wire level. Currently only\n * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.\n * Calling `streamGenerationFrom` while only a non-interactive Layer is\n * in scope fails at `Effect.provide` with a type error.\n *\n * Phantom: the value is `void`; providers register with\n * `Layer.succeed(MusicInteractiveSession, undefined)`.\n */\nexport class MusicInteractiveSession extends Context.Service<MusicInteractiveSession, void>()(\n \"@betalyra/effect-uai/capability/MusicInteractiveSession\",\n) {}\n\n/** One-shot generation. Returns the full `GenerateResult`. */\nexport const generate = (\n request: CommonGenerateMusicRequest,\n): Effect.Effect<GenerateResult, AiError.AiError, MusicGenerator> =>\n Effect.flatMap(MusicGenerator, (s) => s.generate(request))\n\n/** Prompt in, audio chunks out. */\nexport const streamGeneration = (\n request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator> =>\n Stream.unwrap(Effect.map(MusicGenerator, (s) => s.streamGeneration(request)))\n\n/**\n * Bidirectional generation. Dual-arity: pipeable (data-last) and\n * direct (data-first). Requires `MusicInteractiveSession` in R,\n * providers without bidirectional support are a type error at provide\n * time.\n *\n * On the cross-provider surface, `input` is `Stream<unknown>` and\n * output is `Stream<MusicStreamEvent>`. Use the provider-typed\n * service (`LyriaRealtimeGenerator`) when you want to construct\n * typed session messages.\n *\n * @example\n * ```ts\n * // Generic surface (opaque input):\n * const audio = inputs.pipe(\n * MusicGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n *\n * // Lyria-typed surface (full config knobs):\n * const audio = inputs.pipe(\n * LyriaRealtimeGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n * ```\n */\nexport const streamGenerationFrom: {\n (\n request: CommonStreamGenerateMusicRequest,\n ): <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n ) => Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n} = Function.dual(\n 2,\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n > =>\n Stream.unwrap(\n Effect.gen(function* () {\n const s = yield* MusicGenerator\n yield* MusicInteractiveSession\n return s.streamGenerationFrom(input, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;;AA0EA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;;;;;;;;;;AAYF,IAAa,0BAAb,cAA6C,QAAQ,SAAwC,CAC3F,0DACD,CAAC;;AAGF,MAAa,YACX,YAEA,OAAO,QAAQ,iBAAiB,MAAM,EAAE,SAAS,QAAQ,CAAC;;AAG5D,MAAa,oBACX,YAEA,OAAO,OAAO,OAAO,IAAI,iBAAiB,MAAM,EAAE,iBAAiB,QAAQ,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0B/E,MAAa,uBAkBT,SAAS,KACX,IAEE,OACA,YAMA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO;AACjB,QAAO;AACP,QAAO,EAAE,qBAAqB,OAAO,QAAQ;EAC7C,CACH,CACJ"}
@@ -1,6 +1,6 @@
1
1
  import { audioEvent, controlInput, promptsInput, singleVariant } from "../domain/Music.mjs";
2
2
  import { generate, streamGeneration, streamGenerationFrom } from "./MusicGenerator.mjs";
3
- import { a as describe, n as globalExpect, o as it, t as import_dist } from "../dist-B04dlFHg.mjs";
3
+ import { c as it, n as globalExpect, o as describe, t as import_dist } from "../dist-CcRV3-A5.mjs";
4
4
  import { layer, layerWithoutInteractive } from "../testing/MockMusicGenerator.mjs";
5
5
  import { Duration, Effect, Stream } from "effect";
6
6
  //#region src/music-generator/MusicGenerator.test.ts
@@ -1,4 +1,4 @@
1
- import { x as Usage } from "../Items-BnIduEV2.mjs";
1
+ import { x as Usage } from "../Items-BWNu6VGp.mjs";
2
2
  import { TurnEvent } from "../domain/Turn.mjs";
3
3
  import { Duration, Effect, Option, Stream } from "effect";
4
4
 
@@ -1,7 +1,7 @@
1
1
  import { TurnEvent } from "../domain/Turn.mjs";
2
2
  import { computeThroughputTick, isMetricEvent, makeEvent, metricEvents, throughput, timeToCompletion, timeToFirstToken, tokenTotals } from "./Metrics.mjs";
3
- import { a as describe, n as globalExpect } from "../dist-B04dlFHg.mjs";
4
- import { t as it } from "../dist-DWgRbDN3.mjs";
3
+ import { n as globalExpect, o as describe } from "../dist-CcRV3-A5.mjs";
4
+ import { t as it } from "../dist-C0m0QnUP.mjs";
5
5
  import { Array, Duration, Effect, Fiber, Option, Stream } from "effect";
6
6
  import * as TestClock from "effect/testing/TestClock";
7
7
  //#region src/observability/Metrics.test.ts
@@ -1,7 +1,7 @@
1
1
  import { makeEvent } from "./Metrics.mjs";
2
2
  import { layerOtlp, record } from "./Telemetry.mjs";
3
- import { a as describe, n as globalExpect } from "../dist-B04dlFHg.mjs";
4
- import { t as it } from "../dist-DWgRbDN3.mjs";
3
+ import { n as globalExpect, o as describe } from "../dist-CcRV3-A5.mjs";
4
+ import { t as it } from "../dist-C0m0QnUP.mjs";
5
5
  import { Array, Duration, Effect, Layer, Metric, Option, Ref, Stream } from "effect";
6
6
  import { HttpClient, HttpClientResponse } from "effect/unstable/http";
7
7
  //#region src/observability/Telemetry.test.ts
@@ -0,0 +1,71 @@
1
+ import { t as AiError } from "../AiError-Bf1pq4BB.mjs";
2
+ import { JobConfig } from "../job/Job.mjs";
3
+ import { Turn, TurnEvent } from "../domain/Turn.mjs";
4
+ import { ResearchJobRef, ResearchRequest, ResearchState } from "../domain/Research.mjs";
5
+ import { Context, Effect, Stream } from "effect";
6
+
7
+ //#region src/research/DeepResearch.d.ts
8
+ declare namespace DeepResearch_d_exports {
9
+ export { DeepResearch, DeepResearchService, DeepResearchServiceShape, ResearchJobOps, ResearchJobRef, ResearchRequest, ResearchState, cancel, collect, fromJob, research, researchStream, status, streamFrom, submit };
10
+ }
11
+ /**
12
+ * Deep research: submit a question, watch a long-running background job, collect
13
+ * one cited report. Every provider is modeled as a job (`submit` / `poll` /
14
+ * `cancel`), so the surface is uniform. The convenience derivations
15
+ * (`research` / `researchStream` / `collect` / `status`) are the same for every
16
+ * provider and are built once by {@link fromJob}, so an implementor states only
17
+ * its wire calls, never the poll loop.
18
+ *
19
+ * `research` is the blocking one-liner (submit then poll to the terminal `Turn`);
20
+ * `researchStream` forwards live progress as `TurnEvent`s, terminating in
21
+ * `TurnComplete` whose `turn` is the value `research` / `collect` return. The job
22
+ * ref is plain serializable data, so it can be persisted and collected from a
23
+ * later process where the provider's job is server-backed.
24
+ */
25
+ type DeepResearchServiceShape<Req> = {
26
+ readonly submit: (request: Req) => Effect.Effect<ResearchJobRef, AiError>;
27
+ readonly status: (ref: ResearchJobRef) => Effect.Effect<ResearchState, AiError>;
28
+ readonly collect: (ref: ResearchJobRef) => Effect.Effect<Turn, AiError>;
29
+ readonly streamFrom: (ref: ResearchJobRef) => Stream.Stream<TurnEvent, AiError>;
30
+ readonly cancel: (ref: ResearchJobRef) => Effect.Effect<void, AiError>;
31
+ readonly research: (request: Req) => Effect.Effect<Turn, AiError>;
32
+ readonly researchStream: (request: Req) => Stream.Stream<TurnEvent, AiError>;
33
+ };
34
+ type DeepResearchService = DeepResearchServiceShape<ResearchRequest>;
35
+ declare const DeepResearch_base: Context.ServiceClass<DeepResearch, "@betalyra/effect-uai/DeepResearch", DeepResearchService>;
36
+ declare class DeepResearch extends DeepResearch_base {}
37
+ /** The wire ops a provider supplies. `streamFrom` is optional: poll-only jobs
38
+ * get a synthesized stream ({@link synthesizedStream}) in its place. */
39
+ type ResearchJobOps<Req> = {
40
+ readonly submit: (request: Req) => Effect.Effect<ResearchJobRef, AiError>;
41
+ readonly poll: (ref: ResearchJobRef) => Effect.Effect<ResearchState, AiError>;
42
+ readonly cancel: (ref: ResearchJobRef) => Effect.Effect<void, AiError>;
43
+ readonly streamFrom?: (ref: ResearchJobRef) => Stream.Stream<TurnEvent, AiError>;
44
+ };
45
+ /**
46
+ * Assemble a {@link DeepResearchServiceShape} from the job ops. `research` is
47
+ * `submit` then poll-to-settle (best-effort cancel on interrupt, via
48
+ * {@link Job.run}); `collect` / `status` are the detached equivalents;
49
+ * `researchStream` submits then attaches. Generic over the request type so a
50
+ * provider-typed tag keeps its narrowed `submit`. `config` tunes the poll
51
+ * cadence and overall timeout of every derived poll loop (see
52
+ * {@link Job.JobConfig} for the defaults).
53
+ */
54
+ declare const fromJob: <Req extends ResearchRequest>(ops: ResearchJobOps<Req>, config?: JobConfig) => DeepResearchServiceShape<Req>;
55
+ /** Submit and poll to the terminal `Turn`. Cancels the job on interrupt. */
56
+ declare const research: (request: ResearchRequest) => Effect.Effect<Turn, AiError, DeepResearch>;
57
+ /** Submit and forward live progress, terminating in `TurnComplete`. */
58
+ declare const researchStream: (request: ResearchRequest) => Stream.Stream<TurnEvent, AiError, DeepResearch>;
59
+ /** Submit a detached job and return its ref. */
60
+ declare const submit: (request: ResearchRequest) => Effect.Effect<ResearchJobRef, AiError, DeepResearch>;
61
+ /** Current state of a job. */
62
+ declare const status: (ref: ResearchJobRef) => Effect.Effect<ResearchState, AiError, DeepResearch>;
63
+ /** Poll a detached job to completion. */
64
+ declare const collect: (ref: ResearchJobRef) => Effect.Effect<Turn, AiError, DeepResearch>;
65
+ /** Attach a live progress stream to a job (call again to re-attach). */
66
+ declare const streamFrom: (ref: ResearchJobRef) => Stream.Stream<TurnEvent, AiError, DeepResearch>;
67
+ /** Cancel a running job. */
68
+ declare const cancel: (ref: ResearchJobRef) => Effect.Effect<void, AiError, DeepResearch>;
69
+ //#endregion
70
+ export { DeepResearch, DeepResearchService, DeepResearchServiceShape, ResearchJobOps, type ResearchJobRef, type ResearchRequest, type ResearchState, cancel, collect, fromJob, research, researchStream, status, streamFrom, submit, DeepResearch_d_exports as t };
71
+ //# sourceMappingURL=DeepResearch.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeepResearch.d.mts","names":[],"sources":["../../src/research/DeepResearch.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;KAuBY,wBAAA;EAAA,SACD,MAAA,GAAS,OAAA,EAAS,GAAA,KAAQ,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA;EAAA,SACxD,MAAA,GAAS,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA;EAAA,SAC9D,OAAA,GAAU,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA;EAAA,SACtD,UAAA,GAAa,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;EAAA,SAC9D,MAAA,GAAS,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,OAAa,OAAA;EAAA,SACrD,QAAA,GAAW,OAAA,EAAS,GAAA,KAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA;EAAA,SAChD,cAAA,GAAiB,OAAA,EAAS,GAAA,KAAQ,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;AAAA;AAAA,KAG1D,mBAAA,GAAsB,wBAAA,CAAyB,eAAA;AAAA,cAAgB,iBAAA;cAE9D,YAAA,SAAqB,iBAAA;;;KAWtB,cAAA;EAAA,SACD,MAAA,GAAS,OAAA,EAAS,GAAA,KAAQ,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA;EAAA,SACxD,IAAA,GAAO,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA;EAAA,SAC5D,MAAA,GAAS,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,OAAa,OAAA;EAAA,SACrD,UAAA,IAAc,GAAA,EAAK,cAAA,KAAmB,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;AAAA;;;;;;;;;;cA2B7D,OAAA,eAAuB,eAAA,EAClC,GAAA,EAAK,cAAA,CAAe,GAAA,GACpB,MAAA,GAAS,SAAA,KACR,wBAAA,CAAyB,GAAA;;cAqBf,QAAA,GACX,OAAA,EAAS,eAAA,KACR,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA,EAAiB,YAAA;;cAI3B,cAAA,GACX,OAAA,EAAS,eAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,YAAA;;cAIhC,MAAA,GACX,OAAA,EAAS,eAAA,KACR,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA,EAAiB,YAAA;;cAIrC,MAAA,GACX,GAAA,EAAK,cAAA,KACJ,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA,EAAiB,YAAA;;cAIpC,OAAA,GAAW,GAAA,EAAK,cAAA,KAAiB,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA,EAAiB,YAAA;;cAItE,UAAA,GACX,GAAA,EAAK,cAAA,KACJ,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,YAAA;;cAIhC,MAAA,GAAU,GAAA,EAAK,cAAA,KAAiB,MAAA,CAAO,MAAA,OAAa,OAAA,EAAiB,YAAA"}
@@ -0,0 +1,61 @@
1
+ import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
2
+ import { TurnEvent } from "../domain/Turn.mjs";
3
+ import { collect as collect$1, run } from "../job/Job.mjs";
4
+ import { Context, Effect, Stream } from "effect";
5
+ //#region src/research/DeepResearch.ts
6
+ var DeepResearch_exports = /* @__PURE__ */ __exportAll({
7
+ DeepResearch: () => DeepResearch,
8
+ cancel: () => cancel,
9
+ collect: () => collect,
10
+ fromJob: () => fromJob,
11
+ research: () => research,
12
+ researchStream: () => researchStream,
13
+ status: () => status,
14
+ streamFrom: () => streamFrom,
15
+ submit: () => submit
16
+ });
17
+ var DeepResearch = class extends Context.Service()("@betalyra/effect-uai/DeepResearch") {};
18
+ const synthesizedStream = (ops, ref, config) => Stream.concat(Stream.make(TurnEvent.WebSearchCall({ status: "searching" })), Stream.map(Stream.fromEffect(collect$1(ops.poll, ref, config)), (turn) => TurnEvent.TurnComplete({ turn })));
19
+ /**
20
+ * Assemble a {@link DeepResearchServiceShape} from the job ops. `research` is
21
+ * `submit` then poll-to-settle (best-effort cancel on interrupt, via
22
+ * {@link Job.run}); `collect` / `status` are the detached equivalents;
23
+ * `researchStream` submits then attaches. Generic over the request type so a
24
+ * provider-typed tag keeps its narrowed `submit`. `config` tunes the poll
25
+ * cadence and overall timeout of every derived poll loop (see
26
+ * {@link Job.JobConfig} for the defaults).
27
+ */
28
+ const fromJob = (ops, config) => {
29
+ const streamFrom = ops.streamFrom ?? ((ref) => synthesizedStream(ops, ref, config));
30
+ return {
31
+ submit: ops.submit,
32
+ status: ops.poll,
33
+ collect: (ref) => collect$1(ops.poll, ref, config),
34
+ cancel: ops.cancel,
35
+ streamFrom,
36
+ research: (request) => run({
37
+ submit: ops.submit(request),
38
+ poll: ops.poll,
39
+ cancel: ops.cancel
40
+ }, config),
41
+ researchStream: (request) => Stream.unwrap(Effect.map(ops.submit(request), streamFrom))
42
+ };
43
+ };
44
+ /** Submit and poll to the terminal `Turn`. Cancels the job on interrupt. */
45
+ const research = (request) => Effect.flatMap(DeepResearch, (s) => s.research(request));
46
+ /** Submit and forward live progress, terminating in `TurnComplete`. */
47
+ const researchStream = (request) => Stream.unwrap(Effect.map(DeepResearch, (s) => s.researchStream(request)));
48
+ /** Submit a detached job and return its ref. */
49
+ const submit = (request) => Effect.flatMap(DeepResearch, (s) => s.submit(request));
50
+ /** Current state of a job. */
51
+ const status = (ref) => Effect.flatMap(DeepResearch, (s) => s.status(ref));
52
+ /** Poll a detached job to completion. */
53
+ const collect = (ref) => Effect.flatMap(DeepResearch, (s) => s.collect(ref));
54
+ /** Attach a live progress stream to a job (call again to re-attach). */
55
+ const streamFrom = (ref) => Stream.unwrap(Effect.map(DeepResearch, (s) => s.streamFrom(ref)));
56
+ /** Cancel a running job. */
57
+ const cancel = (ref) => Effect.flatMap(DeepResearch, (s) => s.cancel(ref));
58
+ //#endregion
59
+ export { DeepResearch, cancel, collect, fromJob, research, researchStream, status, streamFrom, submit, DeepResearch_exports as t };
60
+
61
+ //# sourceMappingURL=DeepResearch.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeepResearch.mjs","names":["Job.collect","Job.run"],"sources":["../../src/research/DeepResearch.ts"],"sourcesContent":["import { Context, Effect, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport * as Job from \"../job/Job.js\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"../domain/Research.js\"\nimport type { Turn } from \"../domain/Turn.js\"\nimport { TurnEvent } from \"../domain/Turn.js\"\n\nexport type { ResearchJobRef, ResearchRequest, ResearchState } from \"../domain/Research.js\"\n\n/**\n * Deep research: submit a question, watch a long-running background job, collect\n * one cited report. Every provider is modeled as a job (`submit` / `poll` /\n * `cancel`), so the surface is uniform. The convenience derivations\n * (`research` / `researchStream` / `collect` / `status`) are the same for every\n * provider and are built once by {@link fromJob}, so an implementor states only\n * its wire calls, never the poll loop.\n *\n * `research` is the blocking one-liner (submit then poll to the terminal `Turn`);\n * `researchStream` forwards live progress as `TurnEvent`s, terminating in\n * `TurnComplete` whose `turn` is the value `research` / `collect` return. The job\n * ref is plain serializable data, so it can be persisted and collected from a\n * later process where the provider's job is server-backed.\n */\nexport type DeepResearchServiceShape<Req> = {\n readonly submit: (request: Req) => Effect.Effect<ResearchJobRef, AiError.AiError>\n readonly status: (ref: ResearchJobRef) => Effect.Effect<ResearchState, AiError.AiError>\n readonly collect: (ref: ResearchJobRef) => Effect.Effect<Turn, AiError.AiError>\n readonly streamFrom: (ref: ResearchJobRef) => Stream.Stream<TurnEvent, AiError.AiError>\n readonly cancel: (ref: ResearchJobRef) => Effect.Effect<void, AiError.AiError>\n readonly research: (request: Req) => Effect.Effect<Turn, AiError.AiError>\n readonly researchStream: (request: Req) => Stream.Stream<TurnEvent, AiError.AiError>\n}\n\nexport type DeepResearchService = DeepResearchServiceShape<ResearchRequest>\n\nexport class DeepResearch extends Context.Service<DeepResearch, DeepResearchService>()(\n \"@betalyra/effect-uai/DeepResearch\",\n) {}\n\n// ---------------------------------------------------------------------------\n// fromJob: build the whole service from the three job ops (+ an optional real\n// live stream). The derivations are identical for every provider.\n// ---------------------------------------------------------------------------\n\n/** The wire ops a provider supplies. `streamFrom` is optional: poll-only jobs\n * get a synthesized stream ({@link synthesizedStream}) in its place. */\nexport type ResearchJobOps<Req> = {\n readonly submit: (request: Req) => Effect.Effect<ResearchJobRef, AiError.AiError>\n readonly poll: (ref: ResearchJobRef) => Effect.Effect<ResearchState, AiError.AiError>\n readonly cancel: (ref: ResearchJobRef) => Effect.Effect<void, AiError.AiError>\n readonly streamFrom?: (ref: ResearchJobRef) => Stream.Stream<TurnEvent, AiError.AiError>\n}\n\n// A poll-only job has no event stream, so synthesize one: a leading \"searching\"\n// progress event, then the terminal `TurnComplete` off the poll loop. Honest\n// low-fidelity progress, not a fabricated per-search feed.\nconst synthesizedStream = <Req>(\n ops: ResearchJobOps<Req>,\n ref: ResearchJobRef,\n config?: Job.JobConfig,\n): Stream.Stream<TurnEvent, AiError.AiError> =>\n Stream.concat(\n Stream.make(TurnEvent.WebSearchCall({ status: \"searching\" })),\n Stream.map(Stream.fromEffect(Job.collect(ops.poll, ref, config)), (turn) =>\n TurnEvent.TurnComplete({ turn }),\n ),\n )\n\n/**\n * Assemble a {@link DeepResearchServiceShape} from the job ops. `research` is\n * `submit` then poll-to-settle (best-effort cancel on interrupt, via\n * {@link Job.run}); `collect` / `status` are the detached equivalents;\n * `researchStream` submits then attaches. Generic over the request type so a\n * provider-typed tag keeps its narrowed `submit`. `config` tunes the poll\n * cadence and overall timeout of every derived poll loop (see\n * {@link Job.JobConfig} for the defaults).\n */\nexport const fromJob = <Req extends ResearchRequest>(\n ops: ResearchJobOps<Req>,\n config?: Job.JobConfig,\n): DeepResearchServiceShape<Req> => {\n const streamFrom =\n ops.streamFrom ?? ((ref: ResearchJobRef) => synthesizedStream(ops, ref, config))\n return {\n submit: ops.submit,\n status: ops.poll,\n collect: (ref) => Job.collect(ops.poll, ref, config),\n cancel: ops.cancel,\n streamFrom,\n research: (request) =>\n Job.run({ submit: ops.submit(request), poll: ops.poll, cancel: ops.cancel }, config),\n researchStream: (request) => Stream.unwrap(Effect.map(ops.submit(request), streamFrom)),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Top-level accessors over the generic tag. Portable code uses these; they\n// require only `DeepResearch` in `R`.\n// ---------------------------------------------------------------------------\n\n/** Submit and poll to the terminal `Turn`. Cancels the job on interrupt. */\nexport const research = (\n request: ResearchRequest,\n): Effect.Effect<Turn, AiError.AiError, DeepResearch> =>\n Effect.flatMap(DeepResearch, (s) => s.research(request))\n\n/** Submit and forward live progress, terminating in `TurnComplete`. */\nexport const researchStream = (\n request: ResearchRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, DeepResearch> =>\n Stream.unwrap(Effect.map(DeepResearch, (s) => s.researchStream(request)))\n\n/** Submit a detached job and return its ref. */\nexport const submit = (\n request: ResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, DeepResearch> =>\n Effect.flatMap(DeepResearch, (s) => s.submit(request))\n\n/** Current state of a job. */\nexport const status = (\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, DeepResearch> =>\n Effect.flatMap(DeepResearch, (s) => s.status(ref))\n\n/** Poll a detached job to completion. */\nexport const collect = (ref: ResearchJobRef): Effect.Effect<Turn, AiError.AiError, DeepResearch> =>\n Effect.flatMap(DeepResearch, (s) => s.collect(ref))\n\n/** Attach a live progress stream to a job (call again to re-attach). */\nexport const streamFrom = (\n ref: ResearchJobRef,\n): Stream.Stream<TurnEvent, AiError.AiError, DeepResearch> =>\n Stream.unwrap(Effect.map(DeepResearch, (s) => s.streamFrom(ref)))\n\n/** Cancel a running job. */\nexport const cancel = (ref: ResearchJobRef): Effect.Effect<void, AiError.AiError, DeepResearch> =>\n Effect.flatMap(DeepResearch, (s) => s.cancel(ref))\n"],"mappings":";;;;;;;;;;;;;;;;AAmCA,IAAa,eAAb,cAAkC,QAAQ,SAA4C,CACpF,oCACD,CAAC;AAmBF,MAAM,qBACJ,KACA,KACA,WAEA,OAAO,OACL,OAAO,KAAK,UAAU,cAAc,EAAE,QAAQ,aAAa,CAAC,CAAC,EAC7D,OAAO,IAAI,OAAO,WAAWA,UAAY,IAAI,MAAM,KAAK,OAAO,CAAC,GAAG,SACjE,UAAU,aAAa,EAAE,MAAM,CAAC,CACjC,CACF;;;;;;;;;;AAWH,MAAa,WACX,KACA,WACkC;CAClC,MAAM,aACJ,IAAI,gBAAgB,QAAwB,kBAAkB,KAAK,KAAK,OAAO;AACjF,QAAO;EACL,QAAQ,IAAI;EACZ,QAAQ,IAAI;EACZ,UAAU,QAAQA,UAAY,IAAI,MAAM,KAAK,OAAO;EACpD,QAAQ,IAAI;EACZ;EACA,WAAW,YACTC,IAAQ;GAAE,QAAQ,IAAI,OAAO,QAAQ;GAAE,MAAM,IAAI;GAAM,QAAQ,IAAI;GAAQ,EAAE,OAAO;EACtF,iBAAiB,YAAY,OAAO,OAAO,OAAO,IAAI,IAAI,OAAO,QAAQ,EAAE,WAAW,CAAC;EACxF;;;AASH,MAAa,YACX,YAEA,OAAO,QAAQ,eAAe,MAAM,EAAE,SAAS,QAAQ,CAAC;;AAG1D,MAAa,kBACX,YAEA,OAAO,OAAO,OAAO,IAAI,eAAe,MAAM,EAAE,eAAe,QAAQ,CAAC,CAAC;;AAG3E,MAAa,UACX,YAEA,OAAO,QAAQ,eAAe,MAAM,EAAE,OAAO,QAAQ,CAAC;;AAGxD,MAAa,UACX,QAEA,OAAO,QAAQ,eAAe,MAAM,EAAE,OAAO,IAAI,CAAC;;AAGpD,MAAa,WAAW,QACtB,OAAO,QAAQ,eAAe,MAAM,EAAE,QAAQ,IAAI,CAAC;;AAGrD,MAAa,cACX,QAEA,OAAO,OAAO,OAAO,IAAI,eAAe,MAAM,EAAE,WAAW,IAAI,CAAC,CAAC;;AAGnE,MAAa,UAAU,QACrB,OAAO,QAAQ,eAAe,MAAM,EAAE,OAAO,IAAI,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { ContentFiltered, RateLimited, Timeout, Unavailable } from "../domain/AiError.mjs";
2
2
  import { effect, stream } from "./Retry.mjs";
3
- import { a as describe, n as globalExpect, o as it } from "../dist-B04dlFHg.mjs";
3
+ import { c as it, n as globalExpect, o as describe } from "../dist-CcRV3-A5.mjs";
4
4
  import { Cause, Effect, Exit, Option, Ref, Schedule, Stream } from "effect";
5
5
  //#region src/retry/Retry.test.ts
6
6
  const planned = (attempts, plan) => Stream.unwrap(Ref.getAndUpdate(attempts, (n) => n + 1).pipe(Effect.map((n) => plan[Math.min(n, plan.length - 1)])));