@effect-uai/core 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/{AiError-csR8Bhxx.d.mts → AiError-CAX_48RU.d.mts} +2 -2
  2. package/dist/{AiError-csR8Bhxx.d.mts.map → AiError-CAX_48RU.d.mts.map} +1 -1
  3. package/dist/{Image-DxyXqzAM.d.mts → Image-HNmMpMTh.d.mts} +4 -4
  4. package/dist/{Image-DxyXqzAM.d.mts.map → Image-HNmMpMTh.d.mts.map} +1 -1
  5. package/dist/{Items-Hg5AsYxl.d.mts → Items-BH8xUkoR.d.mts} +3 -3
  6. package/dist/{Items-Hg5AsYxl.d.mts.map → Items-BH8xUkoR.d.mts.map} +1 -1
  7. package/dist/{StructuredFormat-Cl41C56K.d.mts → StructuredFormat-BbN4dosH.d.mts} +11 -4
  8. package/dist/StructuredFormat-BbN4dosH.d.mts.map +1 -0
  9. package/dist/{Tool-B8B5qVEy.d.mts → Tool-87ViKCCO.d.mts} +20 -4
  10. package/dist/Tool-87ViKCCO.d.mts.map +1 -0
  11. package/dist/Turn-0CwCAyVe.d.mts +388 -0
  12. package/dist/Turn-0CwCAyVe.d.mts.map +1 -0
  13. package/dist/domain/AiError.d.mts +1 -1
  14. package/dist/domain/AiError.mjs +1 -1
  15. package/dist/domain/AiError.mjs.map +1 -1
  16. package/dist/domain/Image.d.mts +1 -1
  17. package/dist/domain/Items.d.mts +1 -1
  18. package/dist/domain/Items.mjs +1 -1
  19. package/dist/domain/Items.mjs.map +1 -1
  20. package/dist/domain/Turn.d.mts +2 -2
  21. package/dist/domain/Turn.mjs +22 -4
  22. package/dist/domain/Turn.mjs.map +1 -1
  23. package/dist/domain/Turn.test.d.mts +1 -0
  24. package/dist/domain/Turn.test.mjs +136 -0
  25. package/dist/domain/Turn.test.mjs.map +1 -0
  26. package/dist/embedding-model/Embedding.d.mts +15 -3
  27. package/dist/embedding-model/Embedding.d.mts.map +1 -1
  28. package/dist/embedding-model/Embedding.mjs.map +1 -1
  29. package/dist/embedding-model/EmbeddingModel.d.mts +33 -17
  30. package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
  31. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
  32. package/dist/embedding-model/EmbeddingModel.test.d.mts +1 -0
  33. package/dist/embedding-model/EmbeddingModel.test.mjs +59 -0
  34. package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -0
  35. package/dist/index.d.mts +6 -6
  36. package/dist/language-model/LanguageModel.d.mts +30 -8
  37. package/dist/language-model/LanguageModel.d.mts.map +1 -1
  38. package/dist/language-model/LanguageModel.mjs +33 -3
  39. package/dist/language-model/LanguageModel.mjs.map +1 -1
  40. package/dist/language-model/LanguageModel.test.d.mts +1 -0
  41. package/dist/language-model/LanguageModel.test.mjs +143 -0
  42. package/dist/language-model/LanguageModel.test.mjs.map +1 -0
  43. package/dist/loop/Loop.d.mts +94 -11
  44. package/dist/loop/Loop.d.mts.map +1 -1
  45. package/dist/loop/Loop.mjs +92 -26
  46. package/dist/loop/Loop.mjs.map +1 -1
  47. package/dist/loop/Loop.test.mjs +171 -3
  48. package/dist/loop/Loop.test.mjs.map +1 -1
  49. package/dist/music-generator/MusicGenerator.d.mts +1 -1
  50. package/dist/observability/Metrics.d.mts +1 -1
  51. package/dist/observability/Metrics.mjs +1 -1
  52. package/dist/observability/Metrics.mjs.map +1 -1
  53. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +1 -1
  54. package/dist/streaming/JSONL.d.mts +1 -1
  55. package/dist/streaming/JSONL.d.mts.map +1 -1
  56. package/dist/streaming/JSONL.mjs +7 -12
  57. package/dist/streaming/JSONL.mjs.map +1 -1
  58. package/dist/structured-format/StructuredFormat.d.mts +2 -2
  59. package/dist/structured-format/StructuredFormat.mjs +9 -1
  60. package/dist/structured-format/StructuredFormat.mjs.map +1 -1
  61. package/dist/structured-format/StructuredFormat.test.d.mts +1 -0
  62. package/dist/structured-format/StructuredFormat.test.mjs +70 -0
  63. package/dist/structured-format/StructuredFormat.test.mjs.map +1 -0
  64. package/dist/testing/MockMusicGenerator.d.mts.map +1 -1
  65. package/dist/testing/MockMusicGenerator.mjs +2 -2
  66. package/dist/testing/MockMusicGenerator.mjs.map +1 -1
  67. package/dist/testing/MockProvider.d.mts +23 -18
  68. package/dist/testing/MockProvider.d.mts.map +1 -1
  69. package/dist/testing/MockProvider.mjs +56 -72
  70. package/dist/testing/MockProvider.mjs.map +1 -1
  71. package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -1
  72. package/dist/testing/MockSpeechSynthesizer.mjs +2 -2
  73. package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -1
  74. package/dist/testing/MockTranscriber.d.mts.map +1 -1
  75. package/dist/testing/MockTranscriber.mjs +2 -2
  76. package/dist/testing/MockTranscriber.mjs.map +1 -1
  77. package/dist/tool/HistoryCheck.d.mts +1 -1
  78. package/dist/tool/Outcome.d.mts +1 -1
  79. package/dist/tool/Resolvers.d.mts +65 -8
  80. package/dist/tool/Resolvers.d.mts.map +1 -1
  81. package/dist/tool/Resolvers.mjs +8 -12
  82. package/dist/tool/Resolvers.mjs.map +1 -1
  83. package/dist/tool/Resolvers.test.mjs +6 -5
  84. package/dist/tool/Resolvers.test.mjs.map +1 -1
  85. package/dist/tool/Tool.d.mts +2 -2
  86. package/dist/tool/Tool.mjs +18 -1
  87. package/dist/tool/Tool.mjs.map +1 -1
  88. package/dist/tool/Tool.test.d.mts +1 -0
  89. package/dist/tool/Tool.test.mjs +66 -0
  90. package/dist/tool/Tool.test.mjs.map +1 -0
  91. package/dist/tool/Toolkit.d.mts +4 -6
  92. package/dist/tool/Toolkit.d.mts.map +1 -1
  93. package/dist/tool/Toolkit.mjs +14 -43
  94. package/dist/tool/Toolkit.mjs.map +1 -1
  95. package/dist/transcriber/Transcriber.d.mts +1 -1
  96. package/package.json +1 -1
  97. package/src/domain/AiError.ts +1 -1
  98. package/src/domain/Items.ts +1 -1
  99. package/src/domain/Turn.test.ts +141 -0
  100. package/src/domain/Turn.ts +50 -43
  101. package/src/embedding-model/Embedding.ts +23 -0
  102. package/src/embedding-model/EmbeddingModel.test.ts +92 -0
  103. package/src/embedding-model/EmbeddingModel.ts +30 -20
  104. package/src/language-model/LanguageModel.test.ts +170 -0
  105. package/src/language-model/LanguageModel.ts +64 -1
  106. package/src/loop/Loop.test.ts +256 -3
  107. package/src/loop/Loop.ts +225 -49
  108. package/src/observability/Metrics.ts +1 -1
  109. package/src/streaming/JSONL.ts +9 -18
  110. package/src/structured-format/StructuredFormat.test.ts +105 -0
  111. package/src/structured-format/StructuredFormat.ts +14 -1
  112. package/src/testing/MockMusicGenerator.ts +4 -6
  113. package/src/testing/MockProvider.ts +126 -105
  114. package/src/testing/MockSpeechSynthesizer.ts +4 -6
  115. package/src/testing/MockTranscriber.ts +4 -6
  116. package/src/tool/Resolvers.test.ts +8 -5
  117. package/src/tool/Resolvers.ts +17 -19
  118. package/src/tool/Tool.test.ts +105 -0
  119. package/src/tool/Tool.ts +20 -0
  120. package/src/tool/Toolkit.ts +49 -50
  121. package/dist/StructuredFormat-Cl41C56K.d.mts.map +0 -1
  122. package/dist/Tool-B8B5qVEy.d.mts.map +0 -1
  123. package/dist/Turn-7geUcKsf.d.mts +0 -194
  124. package/dist/Turn-7geUcKsf.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Items.mjs","names":[],"sources":["../../src/domain/Items.ts"],"sourcesContent":["import { Schema } from \"effect\"\nimport { ImageSource } from \"./Image.js\"\n\n// ---------------------------------------------------------------------------\n// Content blocks (inside Message.content)\n// ---------------------------------------------------------------------------\n\nexport const InputText = Schema.Struct({\n type: Schema.Literal(\"input_text\"),\n text: Schema.String,\n})\nexport type InputText = typeof InputText.Type\n\n/**\n * User-provided image content block. Pair with `InputText` inside a\n * `Message.content` array to ask \"what's in this image?\" style questions.\n *\n * `source` is the cross-modality `ImageSource` from `domain/Image.ts` -\n * url, base64, or raw bytes. Provider codecs encode bytes to whatever\n * wire format the provider wants.\n */\nexport const InputImage = Schema.Struct({\n type: Schema.Literal(\"input_image\"),\n source: ImageSource,\n})\nexport type InputImage = typeof InputImage.Type\n\n// ---------------------------------------------------------------------------\n// Annotations - source / citation pointers attached to `output_text` blocks.\n// Mirrors OpenAI Responses API; other providers can omit or map onto these\n// shapes.\n// ---------------------------------------------------------------------------\n\nexport const UrlCitation = Schema.Struct({\n type: Schema.Literal(\"url_citation\"),\n url: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n title: Schema.String,\n})\nexport type UrlCitation = typeof UrlCitation.Type\n\nexport const FileCitation = Schema.Struct({\n type: Schema.Literal(\"file_citation\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\nexport type FileCitation = typeof FileCitation.Type\n\nexport const ContainerFileCitation = Schema.Struct({\n type: Schema.Literal(\"container_file_citation\"),\n container_id: Schema.String,\n file_id: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n})\nexport type ContainerFileCitation = typeof ContainerFileCitation.Type\n\nexport const FilePath = Schema.Struct({\n type: Schema.Literal(\"file_path\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\nexport type FilePath = typeof FilePath.Type\n\nexport const Annotation = Schema.Union([UrlCitation, FileCitation, ContainerFileCitation, FilePath])\nexport type Annotation = typeof Annotation.Type\n\nexport const isUrlCitation = Schema.is(UrlCitation)\nexport const isFileCitation = Schema.is(FileCitation)\nexport const isContainerFileCitation = Schema.is(ContainerFileCitation)\nexport const isFilePath = Schema.is(FilePath)\n\nexport const OutputText = Schema.Struct({\n type: Schema.Literal(\"output_text\"),\n text: Schema.String,\n annotations: Schema.optional(Schema.Array(Annotation)),\n})\nexport type OutputText = typeof OutputText.Type\n\n/**\n * Model-emitted refusal. Distinct from `output_text`: the model declined\n * to answer rather than producing normal output. Pair with\n * `stop_reason: \"refusal\"` on the surrounding `Turn`. Streamed via the\n * `refusal_delta` `TurnEvent`.\n */\nexport const Refusal = Schema.Struct({\n type: Schema.Literal(\"refusal\"),\n text: Schema.String,\n})\nexport type Refusal = typeof Refusal.Type\n\nexport const ContentBlock = Schema.Union([InputText, InputImage, OutputText, Refusal])\nexport type ContentBlock = typeof ContentBlock.Type\n\nexport const Role = Schema.Literals([\"user\", \"assistant\", \"system\"])\nexport type Role = typeof Role.Type\n\n// ---------------------------------------------------------------------------\n// Provider passthrough - every Item type carries this opaque slot.\n// The framework never reads or interprets it; provider modules decode\n// their own data via their own typed readers (see e.g.\n// the `@effect-uai/responses` package).\n// ---------------------------------------------------------------------------\n\nconst ProviderData = Schema.optional(Schema.Unknown)\n\n// ---------------------------------------------------------------------------\n// Items\n// ---------------------------------------------------------------------------\n\nexport const Message = Schema.Struct({\n type: Schema.Literal(\"message\"),\n role: Role,\n content: Schema.Array(ContentBlock),\n providerData: ProviderData,\n})\nexport type Message = typeof Message.Type\n\nexport const FunctionCall = Schema.Struct({\n type: Schema.Literal(\"function_call\"),\n call_id: Schema.String,\n name: Schema.String,\n // JSON-encoded arguments string, mirroring OpenAI Responses API\n arguments: Schema.String,\n providerData: ProviderData,\n})\nexport type FunctionCall = typeof FunctionCall.Type\n\nexport const FunctionCallOutput = Schema.Struct({\n type: Schema.Literal(\"function_call_output\"),\n call_id: Schema.String,\n output: Schema.String,\n providerData: ProviderData,\n})\nexport type FunctionCallOutput = typeof FunctionCallOutput.Type\n\n/**\n * Reasoning item - top-level, mirrors OpenAI Responses API. Common shape\n * across providers covers `summary` (human-readable text) and `signature`\n * (opaque round-trip blob - Anthropic's signed thinking, OpenAI's\n * encrypted_content, etc.). Provider-specific fields go in `providerData`.\n */\nexport const Reasoning = Schema.Struct({\n type: Schema.Literal(\"reasoning\"),\n id: Schema.optional(Schema.String),\n summary: Schema.optional(Schema.String),\n signature: Schema.optional(Schema.String),\n providerData: ProviderData,\n})\nexport type Reasoning = typeof Reasoning.Type\n\nexport const Item = Schema.Union([Message, FunctionCall, FunctionCallOutput, Reasoning])\nexport type Item = typeof Item.Type\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\nexport const isInputText = Schema.is(InputText)\nexport const isInputImage = Schema.is(InputImage)\nexport const isOutputText = Schema.is(OutputText)\nexport const isRefusal = Schema.is(Refusal)\n\nexport const isMessage = Schema.is(Message)\nexport const isFunctionCall = Schema.is(FunctionCall)\nexport const isFunctionCallOutput = Schema.is(FunctionCallOutput)\nexport const isReasoning = Schema.is(Reasoning)\n\n// ---------------------------------------------------------------------------\n// Usage and stop reason\n// ---------------------------------------------------------------------------\n\nexport const InputTokensDetails = Schema.Struct({\n cached_tokens: Schema.optional(Schema.Number),\n})\nexport type InputTokensDetails = typeof InputTokensDetails.Type\n\nexport const OutputTokensDetails = Schema.Struct({\n reasoning_tokens: Schema.optional(Schema.Number),\n})\nexport type OutputTokensDetails = typeof OutputTokensDetails.Type\n\nexport const Usage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n input_tokens_details: Schema.optional(InputTokensDetails),\n output_tokens_details: Schema.optional(OutputTokensDetails),\n})\nexport type Usage = typeof Usage.Type\n\nexport const StopReason = Schema.Literals([\n \"stop\",\n \"tool_calls\",\n \"max_tokens\",\n \"refusal\",\n /** Provider-side safety classifier flagged the output. */\n \"content_filter\",\n /** Server-enforced cap on tool calls per turn was hit. */\n \"max_tool_calls\",\n])\nexport type StopReason = typeof StopReason.Type\n\n// ---------------------------------------------------------------------------\n// Helper constructors\n// ---------------------------------------------------------------------------\n\nexport const userText = (text: string): Message => ({\n type: \"message\",\n role: \"user\",\n content: [{ type: \"input_text\", text }],\n})\n\nexport const systemText = (text: string): Message => ({\n type: \"message\",\n role: \"system\",\n content: [{ type: \"input_text\", text }],\n})\n\nexport const assistantText = (text: string): Message => ({\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text }],\n})\n\nexport const functionCallOutput = (call_id: string, output: string): FunctionCallOutput => ({\n type: \"function_call_output\",\n call_id,\n output,\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"Items.mjs","names":[],"sources":["../../src/domain/Items.ts"],"sourcesContent":["import { Schema } from \"effect\"\nimport { ImageSource } from \"./Image.js\"\n\n// ---------------------------------------------------------------------------\n// Content blocks (inside Message.content)\n// ---------------------------------------------------------------------------\n\nexport const InputText = Schema.Struct({\n type: Schema.Literal(\"input_text\"),\n text: Schema.String,\n})\nexport type InputText = typeof InputText.Type\n\n/**\n * User-provided image content block. Pair with `InputText` inside a\n * `Message.content` array to ask \"what's in this image?\" style questions.\n *\n * `source` is the cross-modality `ImageSource` from `domain/Image.ts` -\n * url, base64, or raw bytes. Provider codecs encode bytes to whatever\n * wire format the provider wants.\n */\nexport const InputImage = Schema.Struct({\n type: Schema.Literal(\"input_image\"),\n source: ImageSource,\n})\nexport type InputImage = typeof InputImage.Type\n\n// ---------------------------------------------------------------------------\n// Annotations - source / citation pointers attached to `output_text` blocks.\n// Mirrors OpenAI Responses API; other providers can omit or map onto these\n// shapes.\n// ---------------------------------------------------------------------------\n\nexport const UrlCitation = Schema.Struct({\n type: Schema.Literal(\"url_citation\"),\n url: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n title: Schema.String,\n})\nexport type UrlCitation = typeof UrlCitation.Type\n\nexport const FileCitation = Schema.Struct({\n type: Schema.Literal(\"file_citation\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\nexport type FileCitation = typeof FileCitation.Type\n\nexport const ContainerFileCitation = Schema.Struct({\n type: Schema.Literal(\"container_file_citation\"),\n container_id: Schema.String,\n file_id: Schema.String,\n start_index: Schema.Number,\n end_index: Schema.Number,\n})\nexport type ContainerFileCitation = typeof ContainerFileCitation.Type\n\nexport const FilePath = Schema.Struct({\n type: Schema.Literal(\"file_path\"),\n file_id: Schema.String,\n index: Schema.Number,\n})\nexport type FilePath = typeof FilePath.Type\n\nexport const Annotation = Schema.Union([UrlCitation, FileCitation, ContainerFileCitation, FilePath])\nexport type Annotation = typeof Annotation.Type\n\nexport const isUrlCitation = Schema.is(UrlCitation)\nexport const isFileCitation = Schema.is(FileCitation)\nexport const isContainerFileCitation = Schema.is(ContainerFileCitation)\nexport const isFilePath = Schema.is(FilePath)\n\nexport const OutputText = Schema.Struct({\n type: Schema.Literal(\"output_text\"),\n text: Schema.String,\n annotations: Schema.optional(Schema.Array(Annotation)),\n})\nexport type OutputText = typeof OutputText.Type\n\n/**\n * Model-emitted refusal. Distinct from `output_text`: the model declined\n * to answer rather than producing normal output. Pair with\n * `stop_reason: \"refusal\"` on the surrounding `Turn`. Streamed via the\n * `RefusalDelta` `TurnEvent`.\n */\nexport const Refusal = Schema.Struct({\n type: Schema.Literal(\"refusal\"),\n text: Schema.String,\n})\nexport type Refusal = typeof Refusal.Type\n\nexport const ContentBlock = Schema.Union([InputText, InputImage, OutputText, Refusal])\nexport type ContentBlock = typeof ContentBlock.Type\n\nexport const Role = Schema.Literals([\"user\", \"assistant\", \"system\"])\nexport type Role = typeof Role.Type\n\n// ---------------------------------------------------------------------------\n// Provider passthrough - every Item type carries this opaque slot.\n// The framework never reads or interprets it; provider modules decode\n// their own data via their own typed readers (see e.g.\n// the `@effect-uai/responses` package).\n// ---------------------------------------------------------------------------\n\nconst ProviderData = Schema.optional(Schema.Unknown)\n\n// ---------------------------------------------------------------------------\n// Items\n// ---------------------------------------------------------------------------\n\nexport const Message = Schema.Struct({\n type: Schema.Literal(\"message\"),\n role: Role,\n content: Schema.Array(ContentBlock),\n providerData: ProviderData,\n})\nexport type Message = typeof Message.Type\n\nexport const FunctionCall = Schema.Struct({\n type: Schema.Literal(\"function_call\"),\n call_id: Schema.String,\n name: Schema.String,\n // JSON-encoded arguments string, mirroring OpenAI Responses API\n arguments: Schema.String,\n providerData: ProviderData,\n})\nexport type FunctionCall = typeof FunctionCall.Type\n\nexport const FunctionCallOutput = Schema.Struct({\n type: Schema.Literal(\"function_call_output\"),\n call_id: Schema.String,\n output: Schema.String,\n providerData: ProviderData,\n})\nexport type FunctionCallOutput = typeof FunctionCallOutput.Type\n\n/**\n * Reasoning item - top-level, mirrors OpenAI Responses API. Common shape\n * across providers covers `summary` (human-readable text) and `signature`\n * (opaque round-trip blob - Anthropic's signed thinking, OpenAI's\n * encrypted_content, etc.). Provider-specific fields go in `providerData`.\n */\nexport const Reasoning = Schema.Struct({\n type: Schema.Literal(\"reasoning\"),\n id: Schema.optional(Schema.String),\n summary: Schema.optional(Schema.String),\n signature: Schema.optional(Schema.String),\n providerData: ProviderData,\n})\nexport type Reasoning = typeof Reasoning.Type\n\nexport const Item = Schema.Union([Message, FunctionCall, FunctionCallOutput, Reasoning])\nexport type Item = typeof Item.Type\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\nexport const isInputText = Schema.is(InputText)\nexport const isInputImage = Schema.is(InputImage)\nexport const isOutputText = Schema.is(OutputText)\nexport const isRefusal = Schema.is(Refusal)\n\nexport const isMessage = Schema.is(Message)\nexport const isFunctionCall = Schema.is(FunctionCall)\nexport const isFunctionCallOutput = Schema.is(FunctionCallOutput)\nexport const isReasoning = Schema.is(Reasoning)\n\n// ---------------------------------------------------------------------------\n// Usage and stop reason\n// ---------------------------------------------------------------------------\n\nexport const InputTokensDetails = Schema.Struct({\n cached_tokens: Schema.optional(Schema.Number),\n})\nexport type InputTokensDetails = typeof InputTokensDetails.Type\n\nexport const OutputTokensDetails = Schema.Struct({\n reasoning_tokens: Schema.optional(Schema.Number),\n})\nexport type OutputTokensDetails = typeof OutputTokensDetails.Type\n\nexport const Usage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n input_tokens_details: Schema.optional(InputTokensDetails),\n output_tokens_details: Schema.optional(OutputTokensDetails),\n})\nexport type Usage = typeof Usage.Type\n\nexport const StopReason = Schema.Literals([\n \"stop\",\n \"tool_calls\",\n \"max_tokens\",\n \"refusal\",\n /** Provider-side safety classifier flagged the output. */\n \"content_filter\",\n /** Server-enforced cap on tool calls per turn was hit. */\n \"max_tool_calls\",\n])\nexport type StopReason = typeof StopReason.Type\n\n// ---------------------------------------------------------------------------\n// Helper constructors\n// ---------------------------------------------------------------------------\n\nexport const userText = (text: string): Message => ({\n type: \"message\",\n role: \"user\",\n content: [{ type: \"input_text\", text }],\n})\n\nexport const systemText = (text: string): Message => ({\n type: \"message\",\n role: \"system\",\n content: [{ type: \"input_text\", text }],\n})\n\nexport const assistantText = (text: string): Message => ({\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text }],\n})\n\nexport const functionCallOutput = (call_id: string, output: string): FunctionCallOutput => ({\n type: \"function_call_output\",\n call_id,\n output,\n})\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { c as functionCalls, d as textDeltas, f as toStructured, i as TurnEvent, l as isTurnComplete, n as RefusalRejected, o as appendTurn, r as Turn, s as assistantMessages, t as InteractionEvent, u as reasonings } from "../Turn-7geUcKsf.mjs";
2
- export { InteractionEvent, RefusalRejected, Turn, TurnEvent, appendTurn, assistantMessages, functionCalls, isTurnComplete, reasonings, textDeltas, toStructured };
1
+ import { c as assistantText, d as isTurnComplete, f as reasonings, i as TurnEvent, l as assistantTexts, m as toStructured, n as RefusalRejected, o as appendTurn, p as textDeltas, r as Turn, s as assistantMessages, t as InteractionEvent, u as functionCalls } from "../Turn-0CwCAyVe.mjs";
2
+ export { InteractionEvent, RefusalRejected, Turn, TurnEvent, appendTurn, assistantMessages, assistantText, assistantTexts, functionCalls, isTurnComplete, reasonings, textDeltas, toStructured };
@@ -6,8 +6,11 @@ import { Data, Effect, Result, Schema, Stream, pipe } from "effect";
6
6
  var Turn_exports = /* @__PURE__ */ __exportAll({
7
7
  RefusalRejected: () => RefusalRejected,
8
8
  Turn: () => Turn,
9
+ TurnEvent: () => TurnEvent,
9
10
  appendTurn: () => appendTurn,
10
11
  assistantMessages: () => assistantMessages,
12
+ assistantText: () => assistantText,
13
+ assistantTexts: () => assistantTexts,
11
14
  functionCalls: () => functionCalls,
12
15
  isTurnComplete: () => isTurnComplete,
13
16
  reasonings: () => reasonings,
@@ -24,11 +27,26 @@ const Turn = Schema.Struct({
24
27
  usage: Usage,
25
28
  stop_reason: StopReason
26
29
  });
27
- const isTurnComplete = (d) => d.type === "turn_complete";
30
+ const TurnEvent = Data.taggedEnum();
31
+ const isTurnComplete = TurnEvent.$is("TurnComplete");
28
32
  const functionCalls = (turn) => turn.items.filter((i) => i.type === "function_call");
29
33
  const reasonings = (turn) => turn.items.filter((i) => i.type === "reasoning");
30
34
  const assistantMessages = (turn) => turn.items.filter((i) => i.type === "message" && i.role === "assistant");
31
35
  /**
36
+ * Every `output_text` payload across every assistant message in the turn,
37
+ * preserving order. Refusals and other content blocks are dropped — use
38
+ * `assistantMessages` if you need to inspect them. The primitive for
39
+ * "give me the assistant's text"; callers decide how to combine
40
+ * (typically `.join("")` for prose or `.join(" ")` for log strings).
41
+ */
42
+ const assistantTexts = (turn) => assistantMessages(turn).flatMap((m) => m.content).filter(isOutputText).map((b) => b.text);
43
+ /**
44
+ * Sugar over `assistantTexts(turn).join("")` — the common case for
45
+ * summarizers, classifiers, judge calls, and structured-output backstops
46
+ * that want one concatenated string.
47
+ */
48
+ const assistantText = (turn) => assistantTexts(turn).join("");
49
+ /**
32
50
  * Append a completed turn and optional follow-up items to a state record's
33
51
  * history. Recipes use this at the point where structured tool results are
34
52
  * converted to model-facing `FunctionCallOutput`s.
@@ -42,11 +60,11 @@ const appendTurn = (state, turn, items = []) => ({
42
60
  ]
43
61
  });
44
62
  /**
45
- * Project a `TurnEvent` stream onto its `text_delta` payloads. Other
63
+ * Project a `TurnEvent` stream onto its `TextDelta` payloads. Other
46
64
  * variants are dropped. Composes with `Lines.lines` +
47
65
  * `decodeJsonLines` for prompted-JSONL streaming.
48
66
  */
49
- const textDeltas = (self) => self.pipe(Stream.filterMap((ev) => ev.type === "text_delta" ? Result.succeed(ev.text) : Result.failVoid));
67
+ const textDeltas = (self) => self.pipe(Stream.filterMap((ev) => ev._tag === "TextDelta" ? Result.succeed(ev.text) : Result.failVoid));
50
68
  /**
51
69
  * The assistant message on the just-completed turn was a refusal block,
52
70
  * not an `output_text` payload. Returned by `toStructured` to short-circuit
@@ -81,6 +99,6 @@ const lastAssistantContent = (turn) => {
81
99
  */
82
100
  const toStructured = (turn, format) => pipe(lastAssistantContent(turn), ({ text, refused }) => refused ? Effect.fail(new RefusalRejected({ turn })) : parseJson(format)(text));
83
101
  //#endregion
84
- export { RefusalRejected, Turn, appendTurn, assistantMessages, functionCalls, isTurnComplete, reasonings, Turn_exports as t, textDeltas, toStructured };
102
+ export { RefusalRejected, Turn, TurnEvent, appendTurn, assistantMessages, assistantText, assistantTexts, functionCalls, isTurnComplete, reasonings, Turn_exports as t, textDeltas, toStructured };
85
103
 
86
104
  //# sourceMappingURL=Turn.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Turn.mjs","names":["StructuredFormat.parseJson"],"sources":["../../src/domain/Turn.ts"],"sourcesContent":["import { Data, Effect, Result, Schema, Stream, pipe } from \"effect\"\nimport * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport {\n FunctionCall,\n FunctionCallOutput,\n Item,\n isOutputText,\n isRefusal,\n Message,\n Reasoning,\n StopReason,\n Usage,\n} from \"./Items.js\"\n\n/**\n * The result of a single LLM generation. A turn produces zero or more items\n * (typically one assistant message and zero or more function_call items)\n * and reports usage + a stop reason.\n */\nexport const Turn = Schema.Struct({\n items: Schema.Array(Item),\n usage: Usage,\n stop_reason: StopReason,\n})\nexport type Turn = typeof Turn.Type\n\n/**\n * Canonical events emitted while a single turn is being generated. Most\n * variants are streaming deltas (text, reasoning, tool-call args); the\n * terminal `turn_complete` carries the assembled `Turn`. Lifecycle members\n * aren't deltas, hence the union name.\n */\nexport type TurnEvent =\n | { readonly type: \"text_delta\"; readonly text: string }\n | {\n readonly type: \"reasoning_delta\"\n readonly text: string\n /**\n * `trace` is the model's raw chain-of-thought; `summary` is a\n * model-written summary intended for display. OpenAI Responses emits\n * both as separate wire events; Anthropic and Gemini only emit\n * `trace`. Consumers who just want any reasoning text match once;\n * those who want only summaries filter `kind === \"summary\"`.\n */\n readonly kind: \"trace\" | \"summary\"\n }\n /**\n * The model declined to answer. `text` is the (streamed) explanation.\n * Distinct from the failure channel: a refusal is normal model output and\n * the stream still completes with `turn_complete`. OpenAI Responses emits\n * this; Anthropic surfaces refusals via `stop_reason`, and Gemini collapses\n * them into `finishReason: SAFETY` - both go without a `refusal_delta`.\n */\n | { readonly type: \"refusal_delta\"; readonly text: string }\n | { readonly type: \"tool_call_start\"; readonly call_id: string; readonly name: string }\n | { readonly type: \"tool_call_args_delta\"; readonly call_id: string; readonly delta: string }\n /**\n * Mid-stream cumulative usage. Carries the full `Usage` (including cache\n * token fields when the provider surfaces them) so consumers can drive\n * live budget / cost tracking without waiting for `turn_complete`.\n * Anthropic emits this on `message_start` and `message_delta`; other\n * providers may not emit any `usage_update` and only deliver usage via\n * `turn_complete.turn.usage`.\n */\n | { readonly type: \"usage_update\"; readonly usage: Usage }\n | { readonly type: \"turn_complete\"; readonly turn: Turn }\n\n/**\n * What flows out of an agent loop body to its consumer per turn: every\n * `TurnEvent` the provider emits (including the terminal `turn_complete`\n * carrying the assembled `Turn`), plus the output of any tool the loop ran.\n * Both variants carry a `type` discriminator.\n */\nexport type InteractionEvent = TurnEvent | FunctionCallOutput\n\nexport const isTurnComplete = (d: TurnEvent): d is Extract<TurnEvent, { type: \"turn_complete\" }> =>\n d.type === \"turn_complete\"\n\nexport const functionCalls = (turn: Turn): ReadonlyArray<FunctionCall> =>\n turn.items.filter((i): i is FunctionCall => i.type === \"function_call\")\n\nexport const reasonings = (turn: Turn): ReadonlyArray<Reasoning> =>\n turn.items.filter((i): i is Reasoning => i.type === \"reasoning\")\n\nexport const assistantMessages = (turn: Turn): ReadonlyArray<Message> =>\n turn.items.filter((i): i is Message => i.type === \"message\" && i.role === \"assistant\")\n\n/**\n * Append a completed turn and optional follow-up items to a state record's\n * history. Recipes use this at the point where structured tool results are\n * converted to model-facing `FunctionCallOutput`s.\n */\nexport const appendTurn = <S extends { readonly history: ReadonlyArray<Item> }>(\n state: S,\n turn: Turn,\n items: ReadonlyArray<Item> = [],\n): S => ({\n ...state,\n history: [...state.history, ...turn.items, ...items],\n})\n\n// ---------------------------------------------------------------------------\n// Stream operators\n// ---------------------------------------------------------------------------\n\n/**\n * Project a `TurnEvent` stream onto its `text_delta` payloads. Other\n * variants are dropped. Composes with `Lines.lines` +\n * `decodeJsonLines` for prompted-JSONL streaming.\n */\nexport const textDeltas = <E, R>(\n self: Stream.Stream<TurnEvent, E, R>,\n): Stream.Stream<string, E, R> =>\n self.pipe(\n Stream.filterMap((ev) =>\n ev.type === \"text_delta\" ? Result.succeed(ev.text) : Result.failVoid,\n ),\n )\n\n// ---------------------------------------------------------------------------\n// Structured-output integration\n// ---------------------------------------------------------------------------\n\n/**\n * The assistant message on the just-completed turn was a refusal block,\n * not an `output_text` payload. Returned by `toStructured` to short-circuit\n * decoding before `JSON.parse` / schema validation runs.\n */\nexport class RefusalRejected extends Data.TaggedError(\"RefusalRejected\")<{\n readonly turn: Turn\n}> {}\n\nconst lastAssistantContent = (turn: Turn): { readonly text: string; readonly refused: boolean } => {\n const assistants = assistantMessages(turn)\n const last = assistants[assistants.length - 1]\n if (last === undefined) return { text: \"\", refused: false }\n if (last.content.some(isRefusal)) return { text: \"\", refused: true }\n const text = last.content\n .filter(isOutputText)\n .map((b) => b.text)\n .join(\"\")\n return { text, refused: false }\n}\n\n/**\n * Validate a completed `Turn` against a `StructuredFormat`. Concatenates\n * `output_text` blocks on the last assistant message, then runs\n * `JSON.parse` + the format's schema validation.\n *\n * Three failure modes:\n * - `RefusalRejected` — the assistant emitted a refusal block.\n * - `JsonParseError` — the assembled text wasn't valid JSON.\n * - `StructuredDecodeError` — the JSON didn't match the schema.\n */\nexport const toStructured = <A>(\n turn: Turn,\n format: StructuredFormat.StructuredFormat<A>,\n): Effect.Effect<\n A,\n RefusalRejected | StructuredFormat.JsonParseError | StructuredFormat.StructuredDecodeError\n> =>\n pipe(lastAssistantContent(turn), ({ text, refused }) =>\n refused ? Effect.fail(new RefusalRejected({ turn })) : StructuredFormat.parseJson(format)(text),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,OAAO,OAAO,OAAO;CAChC,OAAO,OAAO,MAAM,KAAK;CACzB,OAAO;CACP,aAAa;CACd,CAAC;AAoDF,MAAa,kBAAkB,MAC7B,EAAE,SAAS;AAEb,MAAa,iBAAiB,SAC5B,KAAK,MAAM,QAAQ,MAAyB,EAAE,SAAS,gBAAgB;AAEzE,MAAa,cAAc,SACzB,KAAK,MAAM,QAAQ,MAAsB,EAAE,SAAS,YAAY;AAElE,MAAa,qBAAqB,SAChC,KAAK,MAAM,QAAQ,MAAoB,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;;;;;;AAOxF,MAAa,cACX,OACA,MACA,QAA6B,EAAE,MACxB;CACP,GAAG;CACH,SAAS;EAAC,GAAG,MAAM;EAAS,GAAG,KAAK;EAAO,GAAG;EAAM;CACrD;;;;;;AAWD,MAAa,cACX,SAEA,KAAK,KACH,OAAO,WAAW,OAChB,GAAG,SAAS,eAAe,OAAO,QAAQ,GAAG,KAAK,GAAG,OAAO,SAC7D,CACF;;;;;;AAWH,IAAa,kBAAb,cAAqC,KAAK,YAAY,kBAAkB,CAErE;AAEH,MAAM,wBAAwB,SAAqE;CACjG,MAAM,aAAa,kBAAkB,KAAK;CAC1C,MAAM,OAAO,WAAW,WAAW,SAAS;AAC5C,KAAI,SAAS,KAAA,EAAW,QAAO;EAAE,MAAM;EAAI,SAAS;EAAO;AAC3D,KAAI,KAAK,QAAQ,KAAK,UAAU,CAAE,QAAO;EAAE,MAAM;EAAI,SAAS;EAAM;AAKpE,QAAO;EAAE,MAJI,KAAK,QACf,OAAO,aAAa,CACpB,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,GACK;EAAE,SAAS;EAAO;;;;;;;;;;;;AAajC,MAAa,gBACX,MACA,WAKA,KAAK,qBAAqB,KAAK,GAAG,EAAE,MAAM,cACxC,UAAU,OAAO,KAAK,IAAI,gBAAgB,EAAE,MAAM,CAAC,CAAC,GAAGA,UAA2B,OAAO,CAAC,KAAK,CAChG"}
1
+ {"version":3,"file":"Turn.mjs","names":["StructuredFormat.parseJson"],"sources":["../../src/domain/Turn.ts"],"sourcesContent":["import { Data, Effect, Result, Schema, Stream, pipe } from \"effect\"\nimport * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport {\n FunctionCall,\n FunctionCallOutput,\n Item,\n isOutputText,\n isRefusal,\n Message,\n Reasoning,\n StopReason,\n Usage,\n} from \"./Items.js\"\n\n/**\n * The result of a single LLM generation. A turn produces zero or more items\n * (typically one assistant message and zero or more function_call items)\n * and reports usage + a stop reason.\n */\nexport const Turn = Schema.Struct({\n items: Schema.Array(Item),\n usage: Usage,\n stop_reason: StopReason,\n})\nexport type Turn = typeof Turn.Type\n\n/**\n * Canonical events emitted while a single turn is being generated. Most\n * variants are streaming deltas (text, reasoning, tool-call args); the\n * terminal `TurnComplete` carries the assembled `Turn`. Lifecycle members\n * aren't deltas, hence the union name.\n *\n * `ReasoningDelta.kind`: `trace` is the model's raw chain-of-thought;\n * `summary` is a model-written summary intended for display. OpenAI\n * Responses emits both; Anthropic and Gemini only emit `trace`.\n *\n * `RefusalDelta`: the model declined to answer. OpenAI Responses emits\n * this as its own event; Anthropic surfaces refusals via `stop_reason`\n * and Gemini collapses them into `finishReason: SAFETY` both go\n * without a `RefusalDelta`.\n *\n * `UsageUpdate`: mid-stream cumulative usage. Anthropic emits this on\n * `message_start` and `message_delta`; other providers may only deliver\n * usage via `TurnComplete.turn.usage`.\n */\nexport type TurnEvent = Data.TaggedEnum<{\n TextDelta: { readonly text: string }\n ReasoningDelta: { readonly text: string; readonly kind: \"trace\" | \"summary\" }\n RefusalDelta: { readonly text: string }\n ToolCallStart: { readonly call_id: string; readonly name: string }\n ToolCallArgsDelta: { readonly call_id: string; readonly delta: string }\n UsageUpdate: { readonly usage: Usage }\n TurnComplete: { readonly turn: Turn }\n}>\n\nexport const TurnEvent = Data.taggedEnum<TurnEvent>()\n\n/**\n * What flows out of an agent loop body to its consumer per turn: every\n * `TurnEvent` the provider emits (including the terminal `TurnComplete`\n * carrying the assembled `Turn`), plus the output of any tool the loop ran.\n * Both variants carry a `_tag` discriminator.\n */\nexport type InteractionEvent = TurnEvent | FunctionCallOutput\n\nexport const isTurnComplete = TurnEvent.$is(\"TurnComplete\")\n\nexport const functionCalls = (turn: Turn): ReadonlyArray<FunctionCall> =>\n turn.items.filter((i): i is FunctionCall => i.type === \"function_call\")\n\nexport const reasonings = (turn: Turn): ReadonlyArray<Reasoning> =>\n turn.items.filter((i): i is Reasoning => i.type === \"reasoning\")\n\nexport const assistantMessages = (turn: Turn): ReadonlyArray<Message> =>\n turn.items.filter((i): i is Message => i.type === \"message\" && i.role === \"assistant\")\n\n/**\n * Every `output_text` payload across every assistant message in the turn,\n * preserving order. Refusals and other content blocks are dropped — use\n * `assistantMessages` if you need to inspect them. The primitive for\n * \"give me the assistant's text\"; callers decide how to combine\n * (typically `.join(\"\")` for prose or `.join(\" \")` for log strings).\n */\nexport const assistantTexts = (turn: Turn): ReadonlyArray<string> =>\n assistantMessages(turn)\n .flatMap((m) => m.content)\n .filter(isOutputText)\n .map((b) => b.text)\n\n/**\n * Sugar over `assistantTexts(turn).join(\"\")` — the common case for\n * summarizers, classifiers, judge calls, and structured-output backstops\n * that want one concatenated string.\n */\nexport const assistantText = (turn: Turn): string => assistantTexts(turn).join(\"\")\n\n/**\n * Append a completed turn and optional follow-up items to a state record's\n * history. Recipes use this at the point where structured tool results are\n * converted to model-facing `FunctionCallOutput`s.\n */\nexport const appendTurn = <S extends { readonly history: ReadonlyArray<Item> }>(\n state: S,\n turn: Turn,\n items: ReadonlyArray<Item> = [],\n): S => ({\n ...state,\n history: [...state.history, ...turn.items, ...items],\n})\n\n// ---------------------------------------------------------------------------\n// Stream operators\n// ---------------------------------------------------------------------------\n\n/**\n * Project a `TurnEvent` stream onto its `TextDelta` payloads. Other\n * variants are dropped. Composes with `Lines.lines` +\n * `decodeJsonLines` for prompted-JSONL streaming.\n */\nexport const textDeltas = <E, R>(\n self: Stream.Stream<TurnEvent, E, R>,\n): Stream.Stream<string, E, R> =>\n self.pipe(\n Stream.filterMap((ev) => (ev._tag === \"TextDelta\" ? Result.succeed(ev.text) : Result.failVoid)),\n )\n\n// ---------------------------------------------------------------------------\n// Structured-output integration\n// ---------------------------------------------------------------------------\n\n/**\n * The assistant message on the just-completed turn was a refusal block,\n * not an `output_text` payload. Returned by `toStructured` to short-circuit\n * decoding before `JSON.parse` / schema validation runs.\n */\nexport class RefusalRejected extends Data.TaggedError(\"RefusalRejected\")<{\n readonly turn: Turn\n}> {}\n\nconst lastAssistantContent = (turn: Turn): { readonly text: string; readonly refused: boolean } => {\n const assistants = assistantMessages(turn)\n const last = assistants[assistants.length - 1]\n if (last === undefined) return { text: \"\", refused: false }\n if (last.content.some(isRefusal)) return { text: \"\", refused: true }\n const text = last.content\n .filter(isOutputText)\n .map((b) => b.text)\n .join(\"\")\n return { text, refused: false }\n}\n\n/**\n * Validate a completed `Turn` against a `StructuredFormat`. Concatenates\n * `output_text` blocks on the last assistant message, then runs\n * `JSON.parse` + the format's schema validation.\n *\n * Three failure modes:\n * - `RefusalRejected` — the assistant emitted a refusal block.\n * - `JsonParseError` — the assembled text wasn't valid JSON.\n * - `StructuredDecodeError` — the JSON didn't match the schema.\n */\nexport const toStructured = <A>(\n turn: Turn,\n format: StructuredFormat.StructuredFormat<A>,\n): Effect.Effect<\n A,\n RefusalRejected | StructuredFormat.JsonParseError | StructuredFormat.StructuredDecodeError\n> =>\n pipe(lastAssistantContent(turn), ({ text, refused }) =>\n refused ? Effect.fail(new RefusalRejected({ turn })) : StructuredFormat.parseJson(format)(text),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAa,OAAO,OAAO,OAAO;CAChC,OAAO,OAAO,MAAM,KAAK;CACzB,OAAO;CACP,aAAa;CACd,CAAC;AAgCF,MAAa,YAAY,KAAK,YAAuB;AAUrD,MAAa,iBAAiB,UAAU,IAAI,eAAe;AAE3D,MAAa,iBAAiB,SAC5B,KAAK,MAAM,QAAQ,MAAyB,EAAE,SAAS,gBAAgB;AAEzE,MAAa,cAAc,SACzB,KAAK,MAAM,QAAQ,MAAsB,EAAE,SAAS,YAAY;AAElE,MAAa,qBAAqB,SAChC,KAAK,MAAM,QAAQ,MAAoB,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY;;;;;;;;AASxF,MAAa,kBAAkB,SAC7B,kBAAkB,KAAK,CACpB,SAAS,MAAM,EAAE,QAAQ,CACzB,OAAO,aAAa,CACpB,KAAK,MAAM,EAAE,KAAK;;;;;;AAOvB,MAAa,iBAAiB,SAAuB,eAAe,KAAK,CAAC,KAAK,GAAG;;;;;;AAOlF,MAAa,cACX,OACA,MACA,QAA6B,EAAE,MACxB;CACP,GAAG;CACH,SAAS;EAAC,GAAG,MAAM;EAAS,GAAG,KAAK;EAAO,GAAG;EAAM;CACrD;;;;;;AAWD,MAAa,cACX,SAEA,KAAK,KACH,OAAO,WAAW,OAAQ,GAAG,SAAS,cAAc,OAAO,QAAQ,GAAG,KAAK,GAAG,OAAO,SAAU,CAChG;;;;;;AAWH,IAAa,kBAAb,cAAqC,KAAK,YAAY,kBAAkB,CAErE;AAEH,MAAM,wBAAwB,SAAqE;CACjG,MAAM,aAAa,kBAAkB,KAAK;CAC1C,MAAM,OAAO,WAAW,WAAW,SAAS;AAC5C,KAAI,SAAS,KAAA,EAAW,QAAO;EAAE,MAAM;EAAI,SAAS;EAAO;AAC3D,KAAI,KAAK,QAAQ,KAAK,UAAU,CAAE,QAAO;EAAE,MAAM;EAAI,SAAS;EAAM;AAKpE,QAAO;EAAE,MAJI,KAAK,QACf,OAAO,aAAa,CACpB,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,GACK;EAAE,SAAS;EAAO;;;;;;;;;;;;AAajC,MAAa,gBACX,MACA,WAKA,KAAK,qBAAqB,KAAK,GAAG,EAAE,MAAM,cACxC,UAAU,OAAO,KAAK,IAAI,gBAAgB,EAAE,MAAM,CAAC,CAAC,GAAGA,UAA2B,OAAO,CAAC,KAAK,CAChG"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,136 @@
1
+ import { assistantText, systemText, userText } from "./Items.mjs";
2
+ import { assistantText as assistantText$1, assistantTexts } from "./Turn.mjs";
3
+ import { i as it, n as globalExpect, r as describe } from "../dist-DV5ISja1.mjs";
4
+ //#region src/domain/Turn.test.ts
5
+ const turnOf = (items) => ({
6
+ items,
7
+ usage: {
8
+ input_tokens: 0,
9
+ output_tokens: 0
10
+ },
11
+ stop_reason: "stop"
12
+ });
13
+ describe("Turn.assistantTexts", () => {
14
+ it("returns each output_text block in order across a single assistant message", () => {
15
+ globalExpect(assistantTexts(turnOf([{
16
+ type: "message",
17
+ role: "assistant",
18
+ content: [{
19
+ type: "output_text",
20
+ text: "first"
21
+ }, {
22
+ type: "output_text",
23
+ text: "second"
24
+ }]
25
+ }]))).toEqual(["first", "second"]);
26
+ });
27
+ it("preserves order across multiple assistant messages", () => {
28
+ globalExpect(assistantTexts(turnOf([
29
+ assistantText("alpha"),
30
+ assistantText("beta"),
31
+ assistantText("gamma")
32
+ ]))).toEqual([
33
+ "alpha",
34
+ "beta",
35
+ "gamma"
36
+ ]);
37
+ });
38
+ it("drops refusal blocks and non-assistant messages", () => {
39
+ globalExpect(assistantTexts(turnOf([userText("ignored"), {
40
+ type: "message",
41
+ role: "assistant",
42
+ content: [{
43
+ type: "output_text",
44
+ text: "kept"
45
+ }, {
46
+ type: "refusal",
47
+ text: "I can't help with that."
48
+ }]
49
+ }]))).toEqual(["kept"]);
50
+ });
51
+ it("returns an empty array when there's nothing to extract", () => {
52
+ globalExpect(assistantTexts(turnOf([]))).toEqual([]);
53
+ globalExpect(assistantTexts(turnOf([userText("only user")]))).toEqual([]);
54
+ });
55
+ it("composes with caller-chosen separators", () => {
56
+ const turn = turnOf([assistantText("hello"), assistantText("world")]);
57
+ globalExpect(assistantTexts(turn).join("")).toBe("helloworld");
58
+ globalExpect(assistantTexts(turn).join(" ")).toBe("hello world");
59
+ globalExpect(assistantTexts(turn).join("\n")).toBe("hello\nworld");
60
+ });
61
+ });
62
+ describe("Turn.assistantText", () => {
63
+ it("concatenates output_text across a single assistant message's content blocks", () => {
64
+ globalExpect(assistantText$1(turnOf([{
65
+ type: "message",
66
+ role: "assistant",
67
+ content: [{
68
+ type: "output_text",
69
+ text: "hello "
70
+ }, {
71
+ type: "output_text",
72
+ text: "world"
73
+ }]
74
+ }]))).toBe("hello world");
75
+ });
76
+ it("concatenates output_text across multiple assistant messages", () => {
77
+ globalExpect(assistantText$1(turnOf([assistantText("first "), assistantText("second")]))).toBe("first second");
78
+ });
79
+ it("ignores non-assistant messages", () => {
80
+ globalExpect(assistantText$1(turnOf([
81
+ userText("ignored"),
82
+ systemText("also ignored"),
83
+ assistantText("kept")
84
+ ]))).toBe("kept");
85
+ });
86
+ it("ignores non-output_text content blocks (refusals, etc.)", () => {
87
+ globalExpect(assistantText$1(turnOf([{
88
+ type: "message",
89
+ role: "assistant",
90
+ content: [
91
+ {
92
+ type: "output_text",
93
+ text: "before "
94
+ },
95
+ {
96
+ type: "refusal",
97
+ text: "I can't help with that."
98
+ },
99
+ {
100
+ type: "output_text",
101
+ text: "after"
102
+ }
103
+ ]
104
+ }]))).toBe("before after");
105
+ });
106
+ it("ignores function_call items even when interleaved with messages", () => {
107
+ globalExpect(assistantText$1(turnOf([
108
+ assistantText("text "),
109
+ {
110
+ type: "function_call",
111
+ call_id: "c1",
112
+ name: "search",
113
+ arguments: "{}"
114
+ },
115
+ assistantText("more text")
116
+ ]))).toBe("text more text");
117
+ });
118
+ it("returns the empty string when no assistant messages exist", () => {
119
+ globalExpect(assistantText$1(turnOf([]))).toBe("");
120
+ globalExpect(assistantText$1(turnOf([userText("only user")]))).toBe("");
121
+ });
122
+ it("returns the empty string when the assistant message has no output_text blocks", () => {
123
+ globalExpect(assistantText$1(turnOf([{
124
+ type: "message",
125
+ role: "assistant",
126
+ content: [{
127
+ type: "refusal",
128
+ text: "I can't help."
129
+ }]
130
+ }]))).toBe("");
131
+ });
132
+ });
133
+ //#endregion
134
+ export {};
135
+
136
+ //# sourceMappingURL=Turn.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Turn.test.mjs","names":["Turn.assistantTexts","Items.assistantText","Items.userText","Turn.assistantText","Items.systemText"],"sources":["../../src/domain/Turn.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\"\nimport * as Items from \"./Items.js\"\nimport * as Turn from \"./Turn.js\"\n\nconst turnOf = (items: Turn.Turn[\"items\"]): Turn.Turn => ({\n items,\n usage: { input_tokens: 0, output_tokens: 0 },\n stop_reason: \"stop\",\n})\n\ndescribe(\"Turn.assistantTexts\", () => {\n it(\"returns each output_text block in order across a single assistant message\", () => {\n const turn = turnOf([\n {\n type: \"message\",\n role: \"assistant\",\n content: [\n { type: \"output_text\", text: \"first\" },\n { type: \"output_text\", text: \"second\" },\n ],\n },\n ])\n\n expect(Turn.assistantTexts(turn)).toEqual([\"first\", \"second\"])\n })\n\n it(\"preserves order across multiple assistant messages\", () => {\n const turn = turnOf([\n Items.assistantText(\"alpha\"),\n Items.assistantText(\"beta\"),\n Items.assistantText(\"gamma\"),\n ])\n\n expect(Turn.assistantTexts(turn)).toEqual([\"alpha\", \"beta\", \"gamma\"])\n })\n\n it(\"drops refusal blocks and non-assistant messages\", () => {\n const turn = turnOf([\n Items.userText(\"ignored\"),\n {\n type: \"message\",\n role: \"assistant\",\n content: [\n { type: \"output_text\", text: \"kept\" },\n { type: \"refusal\", text: \"I can't help with that.\" },\n ],\n },\n ])\n\n expect(Turn.assistantTexts(turn)).toEqual([\"kept\"])\n })\n\n it(\"returns an empty array when there's nothing to extract\", () => {\n expect(Turn.assistantTexts(turnOf([]))).toEqual([])\n expect(Turn.assistantTexts(turnOf([Items.userText(\"only user\")]))).toEqual([])\n })\n\n it(\"composes with caller-chosen separators\", () => {\n const turn = turnOf([Items.assistantText(\"hello\"), Items.assistantText(\"world\")])\n\n expect(Turn.assistantTexts(turn).join(\"\")).toBe(\"helloworld\")\n expect(Turn.assistantTexts(turn).join(\" \")).toBe(\"hello world\")\n expect(Turn.assistantTexts(turn).join(\"\\n\")).toBe(\"hello\\nworld\")\n })\n})\n\ndescribe(\"Turn.assistantText\", () => {\n it(\"concatenates output_text across a single assistant message's content blocks\", () => {\n const turn = turnOf([\n {\n type: \"message\",\n role: \"assistant\",\n content: [\n { type: \"output_text\", text: \"hello \" },\n { type: \"output_text\", text: \"world\" },\n ],\n },\n ])\n\n expect(Turn.assistantText(turn)).toBe(\"hello world\")\n })\n\n it(\"concatenates output_text across multiple assistant messages\", () => {\n const turn = turnOf([Items.assistantText(\"first \"), Items.assistantText(\"second\")])\n\n expect(Turn.assistantText(turn)).toBe(\"first second\")\n })\n\n it(\"ignores non-assistant messages\", () => {\n const turn = turnOf([\n Items.userText(\"ignored\"),\n Items.systemText(\"also ignored\"),\n Items.assistantText(\"kept\"),\n ])\n\n expect(Turn.assistantText(turn)).toBe(\"kept\")\n })\n\n it(\"ignores non-output_text content blocks (refusals, etc.)\", () => {\n const turn = turnOf([\n {\n type: \"message\",\n role: \"assistant\",\n content: [\n { type: \"output_text\", text: \"before \" },\n { type: \"refusal\", text: \"I can't help with that.\" },\n { type: \"output_text\", text: \"after\" },\n ],\n },\n ])\n\n expect(Turn.assistantText(turn)).toBe(\"before after\")\n })\n\n it(\"ignores function_call items even when interleaved with messages\", () => {\n const turn = turnOf([\n Items.assistantText(\"text \"),\n { type: \"function_call\", call_id: \"c1\", name: \"search\", arguments: \"{}\" },\n Items.assistantText(\"more text\"),\n ])\n\n expect(Turn.assistantText(turn)).toBe(\"text more text\")\n })\n\n it(\"returns the empty string when no assistant messages exist\", () => {\n expect(Turn.assistantText(turnOf([]))).toBe(\"\")\n expect(Turn.assistantText(turnOf([Items.userText(\"only user\")]))).toBe(\"\")\n })\n\n it(\"returns the empty string when the assistant message has no output_text blocks\", () => {\n const turn = turnOf([\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"refusal\", text: \"I can't help.\" }],\n },\n ])\n\n expect(Turn.assistantText(turn)).toBe(\"\")\n })\n})\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { i as ImageSource } from "../Image-DxyXqzAM.mjs";
1
+ import { i as ImageSource } from "../Image-HNmMpMTh.mjs";
2
2
 
3
3
  //#region src/embedding-model/Embedding.d.ts
4
4
  declare namespace Embedding_d_exports {
5
- export { BinaryEmbedding, EmbedContentPart, EmbedInput, Embedding, Float32Embedding, Int8Embedding, MultivectorEmbedding, SparseEmbedding, Usage, isBinary, isFloat32, isInt8, isMultivector, isSparse };
5
+ export { BinaryEmbedding, EmbedContentPart, EmbedInput, Embedding, EmbeddingFor, Float32Embedding, Int8Embedding, MultivectorEmbedding, SparseEmbedding, Usage, isBinary, isFloat32, isInt8, isMultivector, isSparse };
6
6
  }
7
7
  /**
8
8
  * One part of a mixed text+image input. Used inside `EmbedInput.content[]`
@@ -94,6 +94,18 @@ declare const isInt8: (e: Embedding) => e is Int8Embedding;
94
94
  declare const isBinary: (e: Embedding) => e is BinaryEmbedding;
95
95
  declare const isSparse: (e: Embedding) => e is SparseEmbedding;
96
96
  declare const isMultivector: (e: Embedding) => e is MultivectorEmbedding;
97
+ /**
98
+ * Maps an `encoding` request field to the corresponding response embedding
99
+ * variant. `undefined` (no encoding requested) defaults to `Float32Embedding`,
100
+ * which is what every provider returns when the caller doesn't ask for
101
+ * anything else. Widened `E` falls back to the full `Embedding` union — the
102
+ * caller has to narrow at use site, which honestly reflects what they know
103
+ * at compile time.
104
+ *
105
+ * Used by `EmbedResponse<E>` / `EmbedManyResponse<E>` to give callers a
106
+ * precise embedding type without a runtime narrowing helper.
107
+ */
108
+ type EmbeddingFor<E> = [E] extends [undefined | "float32"] ? Float32Embedding : [E] extends ["int8"] ? Int8Embedding : [E] extends ["binary"] ? BinaryEmbedding : [E] extends ["sparse"] ? SparseEmbedding : [E] extends ["multivector"] ? MultivectorEmbedding : Embedding;
97
109
  /**
98
110
  * Token usage for one embed / embedMany call. One value per HTTP request,
99
111
  * not per input vector. Most providers populate `inputTokens`; the field
@@ -103,5 +115,5 @@ type Usage = {
103
115
  readonly inputTokens?: number;
104
116
  };
105
117
  //#endregion
106
- export { BinaryEmbedding, EmbedContentPart, EmbedInput, Embedding, Float32Embedding, Int8Embedding, MultivectorEmbedding, SparseEmbedding, Usage, isBinary, isFloat32, isInt8, isMultivector, isSparse, Embedding_d_exports as t };
118
+ export { BinaryEmbedding, EmbedContentPart, EmbedInput, Embedding, EmbeddingFor, Float32Embedding, Int8Embedding, MultivectorEmbedding, SparseEmbedding, Usage, isBinary, isFloat32, isInt8, isMultivector, isSparse, Embedding_d_exports as t };
107
119
  //# sourceMappingURL=Embedding.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Embedding.d.mts","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"mappings":";;;;;;;;;;;KAOY,gBAAA;EAAA,SAA8B,IAAA;AAAA;EAAA,SAA4B,KAAA,EAAO,WAAA;AAAA;;;;;;;;;;KAWjE,UAAA;EAAA,SAEG,IAAA;AAAA;EAAA,SACA,KAAA,EAAO,WAAA;AAAA;EAAA,SACP,OAAA,EAAS,aAAA,CAAc,gBAAA;AAAA;;KAY1B,gBAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,YAAA;AAAA;;;;;KAOP,aAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,SAAA;AAAA;;;;;;KAQP,eAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,UAAA;AAAA;;;;;;AAZnB;;;;;;;;;AAUA;;;;KAuBY,eAAA;EAAA,SACD,IAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA;AAAA;;AAF7B;;;;;;;;;KAeY,oBAAA;EAAA,SACD,IAAA;EAAA,SACA,OAAA,EAAS,aAAA,CAAc,YAAA;AAAA;AAAA,KAGtB,SAAA,GACR,gBAAA,GACA,aAAA,GACA,eAAA,GACA,eAAA,GACA,oBAAA;AAAA,cAES,SAAA,GAAa,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,gBAAA;AAAA,cACjC,MAAA,GAAU,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,aAAA;AAAA,cAC9B,QAAA,GAAY,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,eAAA;AAAA,cAChC,QAAA,GAAY,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,eAAA;AAAA,cAChC,aAAA,GAAiB,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,oBAAA;;AAXlD;;;;KAkBY,KAAA;EAAA,SACD,WAAA;AAAA"}
1
+ {"version":3,"file":"Embedding.d.mts","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"mappings":";;;;;;;;;;;KAOY,gBAAA;EAAA,SAA8B,IAAA;AAAA;EAAA,SAA4B,KAAA,EAAO,WAAA;AAAA;;;;;;;;;;KAWjE,UAAA;EAAA,SAEG,IAAA;AAAA;EAAA,SACA,KAAA,EAAO,WAAA;AAAA;EAAA,SACP,OAAA,EAAS,aAAA,CAAc,gBAAA;AAAA;;KAY1B,gBAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,YAAA;AAAA;;;;;KAOP,aAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,SAAA;AAAA;;;;;;KAQP,eAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,UAAA;AAAA;;;;;;;AAZnB;;;;;;;;;AAUA;;;KAuBY,eAAA;EAAA,SACD,IAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA;AAAA;;;AAF7B;;;;;;;;KAeY,oBAAA;EAAA,SACD,IAAA;EAAA,SACA,OAAA,EAAS,aAAA,CAAc,YAAA;AAAA;AAAA,KAGtB,SAAA,GACR,gBAAA,GACA,aAAA,GACA,eAAA,GACA,eAAA,GACA,oBAAA;AAAA,cAES,SAAA,GAAa,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,gBAAA;AAAA,cACjC,MAAA,GAAU,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,aAAA;AAAA,cAC9B,QAAA,GAAY,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,eAAA;AAAA,cAChC,QAAA,GAAY,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,eAAA;AAAA,cAChC,aAAA,GAAiB,CAAA,EAAG,SAAA,KAAY,CAAA,IAAK,oBAAA;;;AAXlD;;;;;;;;;KAwBY,YAAA,OAAmB,CAAA,oCAC3B,gBAAA,IACC,CAAA,qBACC,aAAA,IACC,CAAA,uBACC,eAAA,IACC,CAAA,uBACC,eAAA,IACC,CAAA,4BACC,oBAAA,GACA,SAAA;;;;;;KAOA,KAAA;EAAA,SACD,WAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Embedding.mjs","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"sourcesContent":["import type { ImageSource } from \"../domain/Image.js\"\n\n/**\n * One part of a mixed text+image input. Used inside `EmbedInput.content[]`\n * for providers that accept interleaved modalities in a single embed call\n * (Cohere v4, Voyage multimodal, Jina v4, Google `gemini-embedding-2`).\n */\nexport type EmbedContentPart = { readonly text: string } | { readonly image: ImageSource }\n\n/**\n * What you embed. The `string` shorthand covers the common text-only case;\n * structured variants exist for image-only and mixed-modality inputs.\n *\n * Not every provider accepts every variant: text-only providers (OpenAI,\n * Mixedbread today) handle `string` and `{ text }`; multimodal providers\n * (Google, Jina v4, Voyage multimodal, Cohere v4) handle all four. A\n * provider layer rejects shapes it can't encode as `AiError.InvalidRequest`.\n */\nexport type EmbedInput =\n | string\n | { readonly text: string }\n | { readonly image: ImageSource }\n | { readonly content: ReadonlyArray<EmbedContentPart> }\n\n// ---------------------------------------------------------------------------\n// Embedding representations\n//\n// The `_tag` reflects the wire form the provider returned, *not* what the\n// consumer asked for - request `encoding: \"int8\"` and you get back an\n// `Int8Embedding`. Math primitives are typed against the named interfaces\n// (see `Vector.ts`) so e.g. `sparseCosine` only accepts `SparseEmbedding`.\n// ---------------------------------------------------------------------------\n\n/** Dense float32 vector. The default representation across all providers. */\nexport type Float32Embedding = {\n readonly _tag: \"float32\"\n readonly vector: Float32Array\n}\n\n/**\n * Dense int8-quantized vector. ~4x smaller than float32 with minimal\n * recall loss on most benchmarks.\n */\nexport type Int8Embedding = {\n readonly _tag: \"int8\"\n readonly vector: Int8Array\n}\n\n/**\n * Dense binary-quantized vector. One bit per dimension, packed into bytes.\n * ~32x smaller than float32; meaningful recall loss but useful for hot\n * indexes paired with a float32 reranker pass.\n */\nexport type BinaryEmbedding = {\n readonly _tag: \"binary\"\n readonly vector: Uint8Array\n}\n\n/**\n * Sparse vector. Token-keyed weights for hybrid search (dense + lexical-\n * style sparse). The single hosted producer today is Jina's `elser-v2`\n * model, which returns subword tokens (e.g. `\"bread\"`, `\"##ing\"`) with\n * their relevance weights.\n *\n * The shape is `Record<string, number>` rather than `(indices, values)`\n * because real hosted learned-sparse encoders (ELSER, SPLADE) emit token\n * strings with no shared vocabulary index. Converting to integer indices\n * would either need a vocabulary table the model doesn't expose, or\n * lose the cross-vector matching semantics. If a provider ever exposes\n * index-valued sparse vectors (Pinecone-style, where you bring your own\n * vocab), add an `IndexSparseEmbedding` sibling arm with `_tag:\n * \"sparse-indexed\"`.\n *\n * Score with `Vector.sparseCosine` — dot product over the intersection\n * of keys, normalized by the L2 norms of both maps.\n */\nexport type SparseEmbedding = {\n readonly _tag: \"sparse\"\n readonly weights: Readonly<Record<string, number>>\n}\n\n/**\n * Multivector / late-interaction output: one float32 vector per token.\n * Score documents with `Vector.maxSim` (ColBERT-style: per query vector,\n * max dot product across doc vectors, summed). Typically ~50-500 vectors\n * per document, each shorter than a single-vector embedding (~128 dim\n * vs ~1024).\n *\n * Quantized multivector forms aren't modeled for the same reason as\n * sparse - nothing on hosted APIs ships them yet.\n */\nexport type MultivectorEmbedding = {\n readonly _tag: \"multivector\"\n readonly vectors: ReadonlyArray<Float32Array>\n}\n\nexport type Embedding =\n | Float32Embedding\n | Int8Embedding\n | BinaryEmbedding\n | SparseEmbedding\n | MultivectorEmbedding\n\nexport const isFloat32 = (e: Embedding): e is Float32Embedding => e._tag === \"float32\"\nexport const isInt8 = (e: Embedding): e is Int8Embedding => e._tag === \"int8\"\nexport const isBinary = (e: Embedding): e is BinaryEmbedding => e._tag === \"binary\"\nexport const isSparse = (e: Embedding): e is SparseEmbedding => e._tag === \"sparse\"\nexport const isMultivector = (e: Embedding): e is MultivectorEmbedding => e._tag === \"multivector\"\n\n/**\n * Token usage for one embed / embedMany call. One value per HTTP request,\n * not per input vector. Most providers populate `inputTokens`; the field\n * is optional for those that don't (or for mock layers in tests).\n */\nexport type Usage = {\n readonly inputTokens?: number\n}\n"],"mappings":";;;;;;;;;AAuGA,MAAa,aAAa,MAAwC,EAAE,SAAS;AAC7E,MAAa,UAAU,MAAqC,EAAE,SAAS;AACvE,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,iBAAiB,MAA4C,EAAE,SAAS"}
1
+ {"version":3,"file":"Embedding.mjs","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"sourcesContent":["import type { ImageSource } from \"../domain/Image.js\"\n\n/**\n * One part of a mixed text+image input. Used inside `EmbedInput.content[]`\n * for providers that accept interleaved modalities in a single embed call\n * (Cohere v4, Voyage multimodal, Jina v4, Google `gemini-embedding-2`).\n */\nexport type EmbedContentPart = { readonly text: string } | { readonly image: ImageSource }\n\n/**\n * What you embed. The `string` shorthand covers the common text-only case;\n * structured variants exist for image-only and mixed-modality inputs.\n *\n * Not every provider accepts every variant: text-only providers (OpenAI,\n * Mixedbread today) handle `string` and `{ text }`; multimodal providers\n * (Google, Jina v4, Voyage multimodal, Cohere v4) handle all four. A\n * provider layer rejects shapes it can't encode as `AiError.InvalidRequest`.\n */\nexport type EmbedInput =\n | string\n | { readonly text: string }\n | { readonly image: ImageSource }\n | { readonly content: ReadonlyArray<EmbedContentPart> }\n\n// ---------------------------------------------------------------------------\n// Embedding representations\n//\n// The `_tag` reflects the wire form the provider returned, *not* what the\n// consumer asked for - request `encoding: \"int8\"` and you get back an\n// `Int8Embedding`. Math primitives are typed against the named interfaces\n// (see `Vector.ts`) so e.g. `sparseCosine` only accepts `SparseEmbedding`.\n// ---------------------------------------------------------------------------\n\n/** Dense float32 vector. The default representation across all providers. */\nexport type Float32Embedding = {\n readonly _tag: \"float32\"\n readonly vector: Float32Array\n}\n\n/**\n * Dense int8-quantized vector. ~4x smaller than float32 with minimal\n * recall loss on most benchmarks.\n */\nexport type Int8Embedding = {\n readonly _tag: \"int8\"\n readonly vector: Int8Array\n}\n\n/**\n * Dense binary-quantized vector. One bit per dimension, packed into bytes.\n * ~32x smaller than float32; meaningful recall loss but useful for hot\n * indexes paired with a float32 reranker pass.\n */\nexport type BinaryEmbedding = {\n readonly _tag: \"binary\"\n readonly vector: Uint8Array\n}\n\n/**\n * Sparse vector. Token-keyed weights for hybrid search (dense + lexical-\n * style sparse). The single hosted producer today is Jina's `elser-v2`\n * model, which returns subword tokens (e.g. `\"bread\"`, `\"##ing\"`) with\n * their relevance weights.\n *\n * The shape is `Record<string, number>` rather than `(indices, values)`\n * because real hosted learned-sparse encoders (ELSER, SPLADE) emit token\n * strings with no shared vocabulary index. Converting to integer indices\n * would either need a vocabulary table the model doesn't expose, or\n * lose the cross-vector matching semantics. If a provider ever exposes\n * index-valued sparse vectors (Pinecone-style, where you bring your own\n * vocab), add an `IndexSparseEmbedding` sibling arm with `_tag:\n * \"sparse-indexed\"`.\n *\n * Score with `Vector.sparseCosine` — dot product over the intersection\n * of keys, normalized by the L2 norms of both maps.\n */\nexport type SparseEmbedding = {\n readonly _tag: \"sparse\"\n readonly weights: Readonly<Record<string, number>>\n}\n\n/**\n * Multivector / late-interaction output: one float32 vector per token.\n * Score documents with `Vector.maxSim` (ColBERT-style: per query vector,\n * max dot product across doc vectors, summed). Typically ~50-500 vectors\n * per document, each shorter than a single-vector embedding (~128 dim\n * vs ~1024).\n *\n * Quantized multivector forms aren't modeled for the same reason as\n * sparse - nothing on hosted APIs ships them yet.\n */\nexport type MultivectorEmbedding = {\n readonly _tag: \"multivector\"\n readonly vectors: ReadonlyArray<Float32Array>\n}\n\nexport type Embedding =\n | Float32Embedding\n | Int8Embedding\n | BinaryEmbedding\n | SparseEmbedding\n | MultivectorEmbedding\n\nexport const isFloat32 = (e: Embedding): e is Float32Embedding => e._tag === \"float32\"\nexport const isInt8 = (e: Embedding): e is Int8Embedding => e._tag === \"int8\"\nexport const isBinary = (e: Embedding): e is BinaryEmbedding => e._tag === \"binary\"\nexport const isSparse = (e: Embedding): e is SparseEmbedding => e._tag === \"sparse\"\nexport const isMultivector = (e: Embedding): e is MultivectorEmbedding => e._tag === \"multivector\"\n\n/**\n * Maps an `encoding` request field to the corresponding response embedding\n * variant. `undefined` (no encoding requested) defaults to `Float32Embedding`,\n * which is what every provider returns when the caller doesn't ask for\n * anything else. Widened `E` falls back to the full `Embedding` union — the\n * caller has to narrow at use site, which honestly reflects what they know\n * at compile time.\n *\n * Used by `EmbedResponse<E>` / `EmbedManyResponse<E>` to give callers a\n * precise embedding type without a runtime narrowing helper.\n */\nexport type EmbeddingFor<E> = [E] extends [undefined | \"float32\"]\n ? Float32Embedding\n : [E] extends [\"int8\"]\n ? Int8Embedding\n : [E] extends [\"binary\"]\n ? BinaryEmbedding\n : [E] extends [\"sparse\"]\n ? SparseEmbedding\n : [E] extends [\"multivector\"]\n ? MultivectorEmbedding\n : Embedding\n\n/**\n * Token usage for one embed / embedMany call. One value per HTTP request,\n * not per input vector. Most providers populate `inputTokens`; the field\n * is optional for those that don't (or for mock layers in tests).\n */\nexport type Usage = {\n readonly inputTokens?: number\n}\n"],"mappings":";;;;;;;;;AAuGA,MAAa,aAAa,MAAwC,EAAE,SAAS;AAC7E,MAAa,UAAU,MAAqC,EAAE,SAAS;AACvE,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,iBAAiB,MAA4C,EAAE,SAAS"}
@@ -1,10 +1,10 @@
1
- import { t as AiError } from "../AiError-csR8Bhxx.mjs";
2
- import { EmbedInput, Embedding, Usage } from "./Embedding.mjs";
1
+ import { t as AiError } from "../AiError-CAX_48RU.mjs";
2
+ import { EmbedInput, EmbeddingFor, Usage } from "./Embedding.mjs";
3
3
  import { Context, Effect } from "effect";
4
4
 
5
5
  //#region src/embedding-model/EmbeddingModel.d.ts
6
6
  declare namespace EmbeddingModel_d_exports {
7
- export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, Encoding, embed, embedMany };
7
+ export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedEncoding, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, embed, embedMany };
8
8
  }
9
9
  /**
10
10
  * Output representation requested from the provider.
@@ -22,12 +22,12 @@ declare namespace EmbeddingModel_d_exports {
22
22
  * style) scoring via `Vector.maxSim`. Currently Jina v4 only.
23
23
  *
24
24
  * Each provider's typed request narrows this to its supported set at
25
- * compile time (e.g. `JinaEncoding = "float32" | "binary" | "sparse" |
25
+ * compile time (e.g. `JinaEmbedEncoding = "float32" | "binary" | "sparse" |
26
26
  * "multivector"`). On the generic `EmbeddingModel` path, callers can
27
- * pass any `Encoding` and the provider's API will reject mismatches at
27
+ * pass any `EmbedEncoding` and the provider's API will reject mismatches at
28
28
  * runtime.
29
29
  */
30
- type Encoding = "float32" | "int8" | "binary" | "sparse" | "multivector";
30
+ type EmbedEncoding = "float32" | "int8" | "binary" | "sparse" | "multivector";
31
31
  /**
32
32
  * Cross-provider single-embed request. Mirrors the shape of
33
33
  * `LanguageModel.CommonRequest`: cross-cutting fields here, vendor
@@ -60,11 +60,11 @@ type CommonEmbedRequest = {
60
60
  */
61
61
  readonly dimensions?: number;
62
62
  /**
63
- * Output representation - see {@link Encoding}. Dense float32 is the
63
+ * Output representation - see {@link EmbedEncoding}. Dense float32 is the
64
64
  * default; provider layers reject unsupported values up front with
65
65
  * `InvalidRequest`.
66
66
  */
67
- readonly encoding?: Encoding;
67
+ readonly encoding?: EmbedEncoding;
68
68
  };
69
69
  /**
70
70
  * Cross-provider batch-embed request. One `task` for the whole batch -
@@ -74,24 +74,40 @@ type CommonEmbedRequest = {
74
74
  type CommonEmbedManyRequest = Omit<CommonEmbedRequest, "input"> & {
75
75
  readonly inputs: ReadonlyArray<EmbedInput>;
76
76
  };
77
- type EmbedResponse = {
78
- readonly embedding: Embedding;
77
+ /**
78
+ * Single-embed response. The `embedding` type is determined by the
79
+ * request's `encoding` field via `EmbeddingFor<E>` — callers that don't
80
+ * specify an encoding get a `Float32Embedding` directly with no runtime
81
+ * narrowing. Defaults to `undefined` for back-compat with consumers that
82
+ * use the bare `EmbedResponse` name.
83
+ */
84
+ type EmbedResponse<E extends EmbedEncoding | undefined = undefined> = {
85
+ readonly embedding: EmbeddingFor<E>;
79
86
  readonly usage: Usage;
80
87
  };
81
- type EmbedManyResponse = {
82
- readonly embeddings: ReadonlyArray<Embedding>;
88
+ /** Batch-embed response. Same encoding rule as {@link EmbedResponse}. */
89
+ type EmbedManyResponse<E extends EmbedEncoding | undefined = undefined> = {
90
+ readonly embeddings: ReadonlyArray<EmbeddingFor<E>>;
83
91
  readonly usage: Usage;
84
92
  };
85
93
  type EmbeddingModelService = {
86
- readonly embed: (request: CommonEmbedRequest) => Effect.Effect<EmbedResponse, AiError>;
87
- readonly embedMany: (request: CommonEmbedManyRequest) => Effect.Effect<EmbedManyResponse, AiError>;
94
+ readonly embed: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedRequest, "encoding"> & {
95
+ readonly encoding?: E;
96
+ }) => Effect.Effect<EmbedResponse<E>, AiError>;
97
+ readonly embedMany: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedManyRequest, "encoding"> & {
98
+ readonly encoding?: E;
99
+ }) => Effect.Effect<EmbedManyResponse<E>, AiError>;
88
100
  };
89
101
  declare const EmbeddingModel_base: Context.ServiceClass<EmbeddingModel, "@betalyra/effect-uai/EmbeddingModel", EmbeddingModelService>;
90
102
  declare class EmbeddingModel extends EmbeddingModel_base {}
91
103
  /** Embed a single input. */
92
- declare const embed: (request: CommonEmbedRequest) => Effect.Effect<EmbedResponse, AiError, EmbeddingModel>;
104
+ declare const embed: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedRequest, "encoding"> & {
105
+ readonly encoding?: E;
106
+ }) => Effect.Effect<EmbedResponse<E>, AiError, EmbeddingModel>;
93
107
  /** Embed a batch in one provider call. Same `task` for every input. */
94
- declare const embedMany: (request: CommonEmbedManyRequest) => Effect.Effect<EmbedManyResponse, AiError, EmbeddingModel>;
108
+ declare const embedMany: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedManyRequest, "encoding"> & {
109
+ readonly encoding?: E;
110
+ }) => Effect.Effect<EmbedManyResponse<E>, AiError, EmbeddingModel>;
95
111
  //#endregion
96
- export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, Encoding, embed, embedMany, EmbeddingModel_d_exports as t };
112
+ export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedEncoding, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, embed, embedMany, EmbeddingModel_d_exports as t };
97
113
  //# sourceMappingURL=EmbeddingModel.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingModel.d.mts","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;;AAYA;;KAZY,QAAA;;;;;;;;;;;KAYA,kBAAA;EAAA,SACD,KAAA,EAAO,UAAA;;;;;WAKP,KAAA;EA6BqB;;;;;;;EAAA,SArBrB,IAAA;EAqBgC;AAG3C;;;;EAH2C,SAfhC,UAAA;EAmBW;;;;;EAAA,SAbX,QAAA,GAAW,QAAA;AAAA;;;;;;KAQV,sBAAA,GAAyB,IAAA,CAAK,kBAAA;EAAA,SAC/B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;AAAA,KAGrB,aAAA;EAAA,SACD,SAAA,EAAW,SAAA;EAAA,SACX,KAAA,EAAO,KAAA;AAAA;AAAA,KAGN,iBAAA;EAAA,SACD,UAAA,EAAY,aAAA,CAAc,SAAA;EAAA,SAC1B,KAAA,EAAO,KAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACD,KAAA,GAAQ,OAAA,EAAS,kBAAA,KAAuB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA;EAAA,SACrE,SAAA,GACP,OAAA,EAAS,sBAAA,KACN,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,OAAA;AAAA;AAAA,cACvC,mBAAA;cAEY,cAAA,SAAuB,mBAAA;;cAKvB,KAAA,GACX,OAAA,EAAS,kBAAA,KACR,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA,EAAiB,cAAA;;cAIpC,SAAA,GACX,OAAA,EAAS,sBAAA,KACR,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,OAAA,EAAiB,cAAA"}
1
+ {"version":3,"file":"EmbeddingModel.d.mts","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;;AAYA;;KAZY,aAAA;;;;;;;;;;;KAYA,kBAAA;EAAA,SACD,KAAA,EAAO,UAAA;;;;;WAKP,KAAA;EA6BqB;;;;;;;EAAA,SArBrB,IAAA;EAqBgC;AAU3C;;;;EAV2C,SAfhC,UAAA;EA0BW;;;;;EAAA,SApBX,QAAA,GAAW,aAAA;AAAA;;;;;;KAQV,sBAAA,GAAyB,IAAA,CAAK,kBAAA;EAAA,SAC/B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;;;;;;;;KAUrB,aAAA,WAAwB,aAAA;EAAA,SACzB,SAAA,EAAW,YAAA,CAAa,CAAA;EAAA,SACxB,KAAA,EAAO,KAAA;AAAA;;KAIN,iBAAA,WAA4B,aAAA;EAAA,SAC7B,UAAA,EAAY,aAAA,CAAc,YAAA,CAAa,CAAA;EAAA,SACvC,KAAA,EAAO,KAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACD,KAAA,aAAkB,aAAA,0BACzB,OAAA,EAAS,IAAA,CAAK,kBAAA;IAAA,SAA6C,QAAA,GAAW,CAAA;EAAA,MACnE,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,CAAA,GAAI,OAAA;EAAA,SAC5B,SAAA,aAAsB,aAAA,0BAC7B,OAAA,EAAS,IAAA,CAAK,sBAAA;IAAA,SAAiD,QAAA,GAAW,CAAA;EAAA,MACvE,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,CAAA,GAAI,OAAA;AAAA;AAAA,cAC1C,mBAAA;cAEY,cAAA,SAAuB,mBAAA;;cAKvB,KAAA,aAAmB,aAAA,0BAC9B,OAAA,EAAS,IAAA,CAAK,kBAAA;EAAA,SAA6C,QAAA,GAAW,CAAA;AAAA,MACrE,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,CAAA,GAAI,OAAA,EAAiB,cAAA;;cAIvC,SAAA,aAAuB,aAAA,0BAClC,OAAA,EAAS,IAAA,CAAK,sBAAA;EAAA,SAAiD,QAAA,GAAW,CAAA;AAAA,MACzE,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,CAAA,GAAI,OAAA,EAAiB,cAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingModel.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"sourcesContent":["import { Context, Effect } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { Embedding, EmbedInput, Usage } from \"./Embedding.js\"\n\n/**\n * Output representation requested from the provider.\n *\n * Dense quantizations - same vector at different storage cost:\n * - `float32` — universal default.\n * - `int8` — ~4x smaller; minimal recall loss on most benchmarks.\n * - `binary` — ~32x smaller; meaningful recall loss but pairs well with\n * a float32 reranker pass over a small candidate set.\n *\n * Non-dense representations:\n * - `sparse` — learned sparse vector for hybrid (dense + lexical) search.\n * Currently Jina ELSER only on hosted APIs.\n * - `multivector` — one vector per token for late-interaction (ColBERT-\n * style) scoring via `Vector.maxSim`. Currently Jina v4 only.\n *\n * Each provider's typed request narrows this to its supported set at\n * compile time (e.g. `JinaEncoding = \"float32\" | \"binary\" | \"sparse\" |\n * \"multivector\"`). On the generic `EmbeddingModel` path, callers can\n * pass any `Encoding` and the provider's API will reject mismatches at\n * runtime.\n */\nexport type Encoding = \"float32\" | \"int8\" | \"binary\" | \"sparse\" | \"multivector\"\n\n/**\n * Cross-provider single-embed request. Mirrors the shape of\n * `LanguageModel.CommonRequest`: cross-cutting fields here, vendor\n * specifics in the provider's typed request.\n *\n * Provider-specific extensions (Cohere widened `task` enum, Jina LoRA\n * tasks, Mixedbread free-form `prompt`, etc.) live in that provider's own\n * request interface, which extends this and narrows `model` / widens\n * `task`.\n */\nexport type CommonEmbedRequest = {\n readonly input: EmbedInput\n /**\n * Model identifier. Each provider narrows this to its typed literal\n * union, so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n /**\n * Retrieval-task hint. Applies to the input. OpenAI ignores this;\n * Mixedbread doesn't have it; Cohere v3+ requires it on the wire (typed\n * as required in `CohereEmbedRequest`). Provider-specific task enums\n * (classification, clustering, code retrieval, …) live on the\n * provider's own request type.\n */\n readonly task?: \"query\" | \"document\"\n /**\n * Matryoshka truncation. Default: provider's native dimension.\n * Discrete-value providers (Cohere, Vertex `multimodalembedding@001`)\n * narrow this to a literal union in their typed request.\n */\n readonly dimensions?: number\n /**\n * Output representation - see {@link Encoding}. Dense float32 is the\n * default; provider layers reject unsupported values up front with\n * `InvalidRequest`.\n */\n readonly encoding?: Encoding\n}\n\n/**\n * Cross-provider batch-embed request. One `task` for the whole batch -\n * mixed-task batches aren't a real provider feature (rerankers exist for\n * that).\n */\nexport type CommonEmbedManyRequest = Omit<CommonEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\nexport type EmbedResponse = {\n readonly embedding: Embedding\n readonly usage: Usage\n}\n\nexport type EmbedManyResponse = {\n readonly embeddings: ReadonlyArray<Embedding>\n readonly usage: Usage\n}\n\nexport type EmbeddingModelService = {\n readonly embed: (request: CommonEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>\n readonly embedMany: (\n request: CommonEmbedManyRequest,\n ) => Effect.Effect<EmbedManyResponse, AiError.AiError>\n}\n\nexport class EmbeddingModel extends Context.Service<EmbeddingModel, EmbeddingModelService>()(\n \"@betalyra/effect-uai/EmbeddingModel\",\n) {}\n\n/** Embed a single input. */\nexport const embed = (\n request: CommonEmbedRequest,\n): Effect.Effect<EmbedResponse, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embed(request))\n\n/** Embed a batch in one provider call. Same `task` for every input. */\nexport const embedMany = (\n request: CommonEmbedManyRequest,\n): Effect.Effect<EmbedManyResponse, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embedMany(request))\n"],"mappings":";;;;;;;;AA4FA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;AAGF,MAAa,SACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAGpE,MAAa,aACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,UAAU,QAAQ,CAAC"}
1
+ {"version":3,"file":"EmbeddingModel.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"sourcesContent":["import { Context, Effect } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { EmbeddingFor, EmbedInput, Usage } from \"./Embedding.js\"\n\n/**\n * Output representation requested from the provider.\n *\n * Dense quantizations - same vector at different storage cost:\n * - `float32` — universal default.\n * - `int8` — ~4x smaller; minimal recall loss on most benchmarks.\n * - `binary` — ~32x smaller; meaningful recall loss but pairs well with\n * a float32 reranker pass over a small candidate set.\n *\n * Non-dense representations:\n * - `sparse` — learned sparse vector for hybrid (dense + lexical) search.\n * Currently Jina ELSER only on hosted APIs.\n * - `multivector` — one vector per token for late-interaction (ColBERT-\n * style) scoring via `Vector.maxSim`. Currently Jina v4 only.\n *\n * Each provider's typed request narrows this to its supported set at\n * compile time (e.g. `JinaEmbedEncoding = \"float32\" | \"binary\" | \"sparse\" |\n * \"multivector\"`). On the generic `EmbeddingModel` path, callers can\n * pass any `EmbedEncoding` and the provider's API will reject mismatches at\n * runtime.\n */\nexport type EmbedEncoding = \"float32\" | \"int8\" | \"binary\" | \"sparse\" | \"multivector\"\n\n/**\n * Cross-provider single-embed request. Mirrors the shape of\n * `LanguageModel.CommonRequest`: cross-cutting fields here, vendor\n * specifics in the provider's typed request.\n *\n * Provider-specific extensions (Cohere widened `task` enum, Jina LoRA\n * tasks, Mixedbread free-form `prompt`, etc.) live in that provider's own\n * request interface, which extends this and narrows `model` / widens\n * `task`.\n */\nexport type CommonEmbedRequest = {\n readonly input: EmbedInput\n /**\n * Model identifier. Each provider narrows this to its typed literal\n * union, so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n /**\n * Retrieval-task hint. Applies to the input. OpenAI ignores this;\n * Mixedbread doesn't have it; Cohere v3+ requires it on the wire (typed\n * as required in `CohereEmbedRequest`). Provider-specific task enums\n * (classification, clustering, code retrieval, …) live on the\n * provider's own request type.\n */\n readonly task?: \"query\" | \"document\"\n /**\n * Matryoshka truncation. Default: provider's native dimension.\n * Discrete-value providers (Cohere, Vertex `multimodalembedding@001`)\n * narrow this to a literal union in their typed request.\n */\n readonly dimensions?: number\n /**\n * Output representation - see {@link EmbedEncoding}. Dense float32 is the\n * default; provider layers reject unsupported values up front with\n * `InvalidRequest`.\n */\n readonly encoding?: EmbedEncoding\n}\n\n/**\n * Cross-provider batch-embed request. One `task` for the whole batch -\n * mixed-task batches aren't a real provider feature (rerankers exist for\n * that).\n */\nexport type CommonEmbedManyRequest = Omit<CommonEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\n/**\n * Single-embed response. The `embedding` type is determined by the\n * request's `encoding` field via `EmbeddingFor<E>` — callers that don't\n * specify an encoding get a `Float32Embedding` directly with no runtime\n * narrowing. Defaults to `undefined` for back-compat with consumers that\n * use the bare `EmbedResponse` name.\n */\nexport type EmbedResponse<E extends EmbedEncoding | undefined = undefined> = {\n readonly embedding: EmbeddingFor<E>\n readonly usage: Usage\n}\n\n/** Batch-embed response. Same encoding rule as {@link EmbedResponse}. */\nexport type EmbedManyResponse<E extends EmbedEncoding | undefined = undefined> = {\n readonly embeddings: ReadonlyArray<EmbeddingFor<E>>\n readonly usage: Usage\n}\n\nexport type EmbeddingModelService = {\n readonly embed: <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedResponse<E>, AiError.AiError>\n readonly embedMany: <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\n}\n\nexport class EmbeddingModel extends Context.Service<EmbeddingModel, EmbeddingModelService>()(\n \"@betalyra/effect-uai/EmbeddingModel\",\n) {}\n\n/** Embed a single input. */\nexport const embed = <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n): Effect.Effect<EmbedResponse<E>, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embed(request))\n\n/** Embed a batch in one provider call. Same `task` for every input. */\nexport const embedMany = <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n): Effect.Effect<EmbedManyResponse<E>, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embedMany(request))\n"],"mappings":";;;;;;;;AAsGA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;AAGF,MAAa,SACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAGpE,MAAa,aACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,UAAU,QAAQ,CAAC"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,59 @@
1
+ import { embed, embedMany } from "./EmbeddingModel.mjs";
2
+ import { i as it, r as describe, t as import_dist } from "../dist-DV5ISja1.mjs";
3
+ //#region src/embedding-model/EmbeddingModel.test.ts
4
+ describe("EmbedResponse<E> conditional narrowing", () => {
5
+ it("defaults to Float32Embedding when E is undefined", () => {
6
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
7
+ });
8
+ it("maps each EmbedEncoding literal to the matching Embedding variant", () => {
9
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
10
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
11
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
12
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
13
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
14
+ });
15
+ it("falls back to the open Embedding union when E is the full EmbedEncoding", () => {
16
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
17
+ });
18
+ });
19
+ describe("EmbedManyResponse<E> mirrors EmbedResponse<E>", () => {
20
+ it("defaults to ReadonlyArray<Float32Embedding>", () => {
21
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
22
+ });
23
+ it("narrows per encoding", () => {
24
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
25
+ });
26
+ });
27
+ describe("embed / embedMany free exports preserve E in their return type", () => {
28
+ it("embed with no encoding returns EmbedResponse<undefined> = Float32", () => {
29
+ (0, import_dist.expectTypeOf)(embed({
30
+ input: "x",
31
+ model: "m"
32
+ })).toEqualTypeOf();
33
+ });
34
+ it("embed with encoding: int8 returns EmbedResponse<int8>", () => {
35
+ (0, import_dist.expectTypeOf)(embed({
36
+ input: "x",
37
+ model: "m",
38
+ encoding: "int8"
39
+ })).toEqualTypeOf();
40
+ });
41
+ it("embedMany with encoding: multivector returns EmbedManyResponse<multivector>", () => {
42
+ (0, import_dist.expectTypeOf)(embedMany({
43
+ inputs: ["x"],
44
+ model: "m",
45
+ encoding: "multivector"
46
+ })).toEqualTypeOf();
47
+ });
48
+ it("when the request is widened to CommonEmbedRequest, the response is the open union", () => {
49
+ (0, import_dist.expectTypeOf)(embed({
50
+ input: "x",
51
+ model: "m",
52
+ encoding: "int8"
53
+ })).toEqualTypeOf();
54
+ });
55
+ });
56
+ //#endregion
57
+ export {};
58
+
59
+ //# sourceMappingURL=EmbeddingModel.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbeddingModel.test.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.test.ts"],"sourcesContent":["import { Effect } from \"effect\"\nimport { describe, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"../domain/AiError.js\"\nimport type {\n BinaryEmbedding,\n Float32Embedding,\n Int8Embedding,\n MultivectorEmbedding,\n SparseEmbedding,\n} from \"./Embedding.js\"\nimport {\n type EmbedEncoding,\n embed,\n embedMany,\n type EmbedManyResponse,\n type EmbedResponse,\n EmbeddingModel,\n} from \"./EmbeddingModel.js\"\n\n// Type-level tests only. The actual narrowing happens at the type system\n// boundary — the runtime impl is exercised in provider-specific test files.\ndescribe(\"EmbedResponse<E> conditional narrowing\", () => {\n it(\"defaults to Float32Embedding when E is undefined\", () => {\n expectTypeOf<EmbedResponse>().toEqualTypeOf<{\n readonly embedding: Float32Embedding\n readonly usage: import(\"./Embedding.js\").Usage\n }>()\n })\n\n it(\"maps each EmbedEncoding literal to the matching Embedding variant\", () => {\n expectTypeOf<EmbedResponse<\"float32\">[\"embedding\"]>().toEqualTypeOf<Float32Embedding>()\n expectTypeOf<EmbedResponse<\"int8\">[\"embedding\"]>().toEqualTypeOf<Int8Embedding>()\n expectTypeOf<EmbedResponse<\"binary\">[\"embedding\"]>().toEqualTypeOf<BinaryEmbedding>()\n expectTypeOf<EmbedResponse<\"sparse\">[\"embedding\"]>().toEqualTypeOf<SparseEmbedding>()\n expectTypeOf<EmbedResponse<\"multivector\">[\"embedding\"]>().toEqualTypeOf<MultivectorEmbedding>()\n })\n\n it(\"falls back to the open Embedding union when E is the full EmbedEncoding\", () => {\n expectTypeOf<EmbedResponse<EmbedEncoding>[\"embedding\"]>().toEqualTypeOf<\n Float32Embedding | Int8Embedding | BinaryEmbedding | SparseEmbedding | MultivectorEmbedding\n >()\n })\n})\n\ndescribe(\"EmbedManyResponse<E> mirrors EmbedResponse<E>\", () => {\n it(\"defaults to ReadonlyArray<Float32Embedding>\", () => {\n expectTypeOf<EmbedManyResponse[\"embeddings\"]>().toEqualTypeOf<ReadonlyArray<Float32Embedding>>()\n })\n\n it(\"narrows per encoding\", () => {\n expectTypeOf<EmbedManyResponse<\"int8\">[\"embeddings\"]>().toEqualTypeOf<\n ReadonlyArray<Int8Embedding>\n >()\n })\n})\n\ndescribe(\"embed / embedMany free exports preserve E in their return type\", () => {\n it(\"embed with no encoding returns EmbedResponse<undefined> = Float32\", () => {\n const result = embed({ input: \"x\", model: \"m\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<undefined>, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"embed with encoding: int8 returns EmbedResponse<int8>\", () => {\n const result = embed({ input: \"x\", model: \"m\", encoding: \"int8\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<\"int8\">, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"embedMany with encoding: multivector returns EmbedManyResponse<multivector>\", () => {\n const result = embedMany({ inputs: [\"x\"], model: \"m\", encoding: \"multivector\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedManyResponse<\"multivector\">, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"when the request is widened to CommonEmbedRequest, the response is the open union\", () => {\n // Demonstrates the documented case: annotating the request type erases\n // the literal `encoding` and the response falls back to `Embedding`.\n const req: { input: string; model: string; encoding?: EmbedEncoding } = {\n input: \"x\",\n model: \"m\",\n encoding: \"int8\",\n }\n const result = embed(req)\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<EmbedEncoding>, AiError.AiError, EmbeddingModel>\n >()\n })\n})\n"],"mappings":";;;AAqBA,SAAS,gDAAgD;AACvD,IAAG,0DAA0D;AAC3D,GAAA,GAAA,YAAA,eAA6B,CAAC,eAG1B;GACJ;AAEF,IAAG,2EAA2E;AAC5E,GAAA,GAAA,YAAA,eAAqD,CAAC,eAAiC;AACvF,GAAA,GAAA,YAAA,eAAkD,CAAC,eAA8B;AACjF,GAAA,GAAA,YAAA,eAAoD,CAAC,eAAgC;AACrF,GAAA,GAAA,YAAA,eAAoD,CAAC,eAAgC;AACrF,GAAA,GAAA,YAAA,eAAyD,CAAC,eAAqC;GAC/F;AAEF,IAAG,iFAAiF;AAClF,GAAA,GAAA,YAAA,eAAyD,CAAC,eAEvD;GACH;EACF;AAEF,SAAS,uDAAuD;AAC9D,IAAG,qDAAqD;AACtD,GAAA,GAAA,YAAA,eAA+C,CAAC,eAAgD;GAChG;AAEF,IAAG,8BAA8B;AAC/B,GAAA,GAAA,YAAA,eAAuD,CAAC,eAErD;GACH;EACF;AAEF,SAAS,wEAAwE;AAC/E,IAAG,2EAA2E;AAE5E,GAAA,GAAA,YAAA,cADe,MAAM;GAAE,OAAO;GAAK,OAAO;GAAK,CAC5B,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,+DAA+D;AAEhE,GAAA,GAAA,YAAA,cADe,MAAM;GAAE,OAAO;GAAK,OAAO;GAAK,UAAU;GAAQ,CAC9C,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,qFAAqF;AAEtF,GAAA,GAAA,YAAA,cADe,UAAU;GAAE,QAAQ,CAAC,IAAI;GAAE,OAAO;GAAK,UAAU;GAAe,CAC5D,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,2FAA2F;AAS5F,GAAA,GAAA,YAAA,cADe,MAAM;GAJnB,OAAO;GACP,OAAO;GACP,UAAU;GAEY,CACL,CAAC,CAAC,eAElB;GACH;EACF"}