@effect-uai/jina 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,7 +22,7 @@ declare namespace JinaEmbedding_d_exports {
22
22
  type JinaTask = "retrieval.query" | "retrieval.passage" | "text-matching" | "code.query" | "code.passage" | "classification" | "separation" | (string & {});
23
23
  /**
24
24
  * Jina's supported `encoding` values. Provider-narrowed from the cross-
25
- * provider `Encoding` union.
25
+ * provider `EmbedEncoding` union.
26
26
  *
27
27
  * Compatibility with models is checked at the response level rather than
28
28
  * pre-flight: if you ask for `multivector` against a model that doesn't
@@ -54,8 +54,12 @@ type JinaEmbedManyRequest = Omit<JinaEmbedRequest, "input"> & {
54
54
  readonly inputs: ReadonlyArray<EmbedInput>;
55
55
  };
56
56
  type JinaEmbeddingService = {
57
- readonly embed: (request: JinaEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>;
58
- readonly embedMany: (request: JinaEmbedManyRequest) => Effect.Effect<EmbedManyResponse, AiError.AiError>;
57
+ readonly embed: <E extends JinaEncoding | undefined = undefined>(request: Omit<JinaEmbedRequest, "encoding"> & {
58
+ readonly encoding?: E;
59
+ }) => Effect.Effect<EmbedResponse<E>, AiError.AiError>;
60
+ readonly embedMany: <E extends JinaEncoding | undefined = undefined>(request: Omit<JinaEmbedManyRequest, "encoding"> & {
61
+ readonly encoding?: E;
62
+ }) => Effect.Effect<EmbedManyResponse<E>, AiError.AiError>;
59
63
  };
60
64
  declare const JinaEmbedding_base: Context.ServiceClass<JinaEmbedding, "@betalyra/effect-uai/providers/jina/JinaEmbedding", JinaEmbeddingService>;
61
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"JinaEmbedding.d.mts","names":[],"sources":["../src/JinaEmbedding.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;KA6BY,QAAA;;;;AAAZ;;;;;AA8BA;;;;;AAEA;;;;;;KAFY,YAAA;AAAA,KAEA,gBAAA,GAAmB,IAAA,CAAK,kBAAA;EAUF,2EARvB,KAAA,EAAO,kBAAA;EAFa;;;;;EAAA,SAQpB,IAAA,GAAO,QAAA,EAEP;EAAA,SAAA,QAAA,GAAW,YAAA;AAAA;AAAA,KAGV,oBAAA,GAAuB,IAAA,CAAK,gBAAA;EAAA,SAC7B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;AAAA,KAGrB,oBAAA;EAAA,SACD,KAAA,GAAQ,OAAA,EAAS,gBAAA,KAAqB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,OAAA,CAAQ,OAAA;EAAA,SAC3E,SAAA,GACP,OAAA,EAAS,oBAAA,KACN,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,OAAA,CAAQ,OAAA;AAAA;AAAA,cAC/C,kBAAA;;;;;;cAOY,aAAA,SAAsB,kBAAA;AAAA,KAIvB,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;AAAA;;;;;cAoYE,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,oBAAA,SAA6B,UAAA,CAAW,UAAA;;;;;;;;cAe5C,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,aAAA,GAAgB,cAAA,SAAuB,UAAA,CAAW,UAAA"}
1
+ {"version":3,"file":"JinaEmbedding.d.mts","names":[],"sources":["../src/JinaEmbedding.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;KAmCY,QAAA;;;;AAAZ;;;;;AA8BA;;;;;AAEA;;;;;;KAFY,YAAA;AAAA,KAEA,gBAAA,GAAmB,IAAA,CAAK,kBAAA;EAUF,2EARvB,KAAA,EAAO,kBAAA;EAFa;;;;;EAAA,SAQpB,IAAA,GAAO,QAAA,EAEP;EAAA,SAAA,QAAA,GAAW,YAAA;AAAA;AAAA,KAGV,oBAAA,GAAuB,IAAA,CAAK,gBAAA;EAAA,SAC7B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;AAAA,KAGrB,oBAAA;EAAA,SACD,KAAA,aAAkB,YAAA,0BACzB,OAAA,EAAS,IAAA,CAAK,gBAAA;IAAA,SAA2C,QAAA,GAAW,CAAA;EAAA,MACjE,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,CAAA,GAAI,OAAA,CAAQ,OAAA;EAAA,SACpC,SAAA,aAAsB,YAAA,0BAC7B,OAAA,EAAS,IAAA,CAAK,oBAAA;IAAA,SAA+C,QAAA,GAAW,CAAA;EAAA,MACrE,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,CAAA,GAAI,OAAA,CAAQ,OAAA;AAAA;AAAA,cAClD,kBAAA;;;;;;cAOY,aAAA,SAAsB,kBAAA;AAAA,KAIvB,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;AAAA;;;;;cAyYE,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,oBAAA,SAA6B,UAAA,CAAW,UAAA;;;;;;;;cAuB5C,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,aAAA,GAAgB,cAAA,SAAuB,UAAA,CAAW,UAAA"}
@@ -191,7 +191,7 @@ const postEmbed = (cfg, body) => Effect.gen(function* () {
191
191
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
192
192
  if (response.status >= 400) {
193
193
  const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
194
- return yield* Effect.fail(httpStatusError(response.status, text));
194
+ return yield* httpStatusError(response.status, text);
195
195
  }
196
196
  const json = yield* response.json.pipe(Effect.mapError(transportFailure));
197
197
  return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(Effect.mapError(transportFailure));
@@ -1 +1 @@
1
- {"version":3,"file":"JinaEmbedding.mjs","names":[],"sources":["../src/JinaEmbedding.ts"],"sourcesContent":["import { Context, Effect, Encoding, Layer, Match, Redacted, Result, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { Embedding, EmbedContentPart, EmbedInput, Usage } from \"@effect-uai/core/Embedding\"\nimport {\n type CommonEmbedManyRequest,\n type CommonEmbedRequest,\n EmbeddingModel,\n type EmbeddingModelService,\n type EmbedManyResponse,\n type EmbedResponse,\n} from \"@effect-uai/core/EmbeddingModel\"\nimport type { ImageSource } from \"@effect-uai/core/Image\"\nimport type { JinaEmbeddingModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Jina's task vocabulary. v4 and v3/v5 share the dotted-pair form:\n * - `retrieval.query` / `retrieval.passage` — asymmetric retrieval.\n * - `text-matching` — symmetric similarity between two texts.\n * - `code.query` / `code.passage` — code retrieval / matching (v4-only).\n * - `classification` / `separation` — v3 / v5 only.\n *\n * The `(string & {})` tail accepts any string so newly-released task\n * types work without an SDK update.\n */\nexport type JinaTask =\n | \"retrieval.query\"\n | \"retrieval.passage\"\n | \"text-matching\"\n | \"code.query\"\n | \"code.passage\"\n | \"classification\"\n | \"separation\"\n // eslint-disable-next-line @typescript-eslint/ban-types\n | (string & {})\n\n/**\n * Jina's supported `encoding` values. Provider-narrowed from the cross-\n * provider `Encoding` union.\n *\n * Compatibility with models is checked at the response level rather than\n * pre-flight: if you ask for `multivector` against a model that doesn't\n * produce one, Jina's API rejects the request with a clear error; if\n * you ask for `sparse` and the chosen model returns dense, our decoder\n * fails with a typed mismatch. No hardcoded model-encoding table to\n * maintain.\n *\n * Wire mapping:\n * - `multivector` → request sets `return_multivector: true`\n * (`jina-embeddings-v4` + ColBERT models).\n * - `sparse` → no flag; the model itself (`elser-v2`) determines the\n * output shape.\n * - `binary` → request sets `embedding_type: \"binary\"`.\n * - `float32` → no flag; default JSON `number[]`.\n */\nexport type JinaEncoding = \"float32\" | \"binary\" | \"sparse\" | \"multivector\"\n\nexport type JinaEmbedRequest = Omit<CommonEmbedRequest, \"model\" | \"task\" | \"encoding\"> & {\n /** Narrows `CommonEmbedRequest.model` to the typed Jina union. */\n readonly model: JinaEmbeddingModel\n /**\n * Widens the cross-provider `\"query\" | \"document\"` to Jina's task enum.\n * Required by v3+ for retrieval-quality results. v4 unifies query and\n * document under `retrieval`.\n */\n readonly task?: JinaTask\n /** Narrows `CommonEmbedRequest.encoding` to Jina's supported set. */\n readonly encoding?: JinaEncoding\n}\n\nexport type JinaEmbedManyRequest = Omit<JinaEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\nexport type JinaEmbeddingService = {\n readonly embed: (request: JinaEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>\n readonly embedMany: (\n request: JinaEmbedManyRequest,\n ) => Effect.Effect<EmbedManyResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Jina-typed embedding\n * requests; yield the generic `EmbeddingModel` tag for provider-portable\n * code. Both are registered by `layer`.\n */\nexport class JinaEmbedding extends Context.Service<JinaEmbedding, JinaEmbeddingService>()(\n \"@betalyra/effect-uai/providers/jina/JinaEmbedding\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireTextItem = {\n readonly text: string\n}\ntype WireImageItem = {\n readonly image: string\n}\ntype WireItem = WireTextItem | WireImageItem\n\n/**\n * Jina v4 accepts `{ image: <url> }` or `{ image: <base64-string> }`.\n * The server detects which form by looking at the value. We pass URLs\n * through directly and base64-encode raw bytes.\n */\nconst imageSourceToItem: (s: ImageSource) => WireItem = Match.type<ImageSource>().pipe(\n Match.tag(\"url\", (s): WireItem => ({ image: s.url })),\n Match.tag(\"base64\", (s): WireItem => ({ image: s.base64 })),\n Match.tag(\"bytes\", (s): WireItem => ({ image: Encoding.encodeBase64(s.bytes) })),\n Match.exhaustive,\n)\n\nconst contentPartToItem: (part: EmbedContentPart) => WireItem = Match.type<EmbedContentPart>().pipe(\n Match.when({ text: Match.string }, ({ text }): WireItem => ({ text })),\n Match.when({ image: Match.any }, ({ image }) => imageSourceToItem(image)),\n Match.exhaustive,\n)\n\nconst multiPartContentRejected: AiError.AiError = new AiError.InvalidRequest({\n provider: \"jina\",\n param: \"input.content\",\n raw: \"Jina treats each input[] entry as one item; multi-part `content[]` would lose the grouping. Split into separate `inputs[]` entries.\",\n})\n\n/**\n * Project an `EmbedInput` to one wire item. A single-part `content[]` is\n * unwrapped; multi-part `content[]` is rejected because Jina's flat\n * `input[]` would lose the grouping.\n */\nconst inputToItem: (input: EmbedInput) => Effect.Effect<WireItem, AiError.AiError> =\n Match.type<EmbedInput>().pipe(\n Match.when(\n Match.string,\n (s): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text: s }),\n ),\n Match.when(\n { text: Match.string },\n ({ text }): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text }),\n ),\n Match.when(\n { image: Match.any },\n ({ image }): Effect.Effect<WireItem, AiError.AiError> =>\n Effect.succeed(imageSourceToItem(image)),\n ),\n Match.when(\n { content: Match.any },\n ({ content }): Effect.Effect<WireItem, AiError.AiError> =>\n content.length === 1\n ? Effect.succeed(contentPartToItem(content[0]!))\n : Effect.fail(multiPartContentRejected),\n ),\n Match.exhaustive,\n )\n\ntype WireBody = {\n readonly model: string\n readonly input: ReadonlyArray<WireItem>\n readonly task?: string\n readonly dimensions?: number\n readonly embedding_type?: \"float\" | \"base64\" | \"binary\" | \"ubinary\"\n readonly return_multivector?: boolean\n}\n\ntype WireQuant = WireBody[\"embedding_type\"]\n\n/**\n * Map our `JinaEncoding` to Jina's `embedding_type` wire field. Only\n * `binary` actually changes the wire transport; the others either omit\n * the field (`float32`) or ignore it (`sparse` / `multivector` use\n * separate signalling — model choice and `return_multivector`).\n */\nconst encodingToQuant: (encoding: JinaEncoding | undefined) => WireQuant = Match.type<\n JinaEncoding | undefined\n>().pipe(\n Match.when(\"binary\", (): WireQuant => \"binary\"),\n Match.orElse((): WireQuant => undefined),\n)\n\nconst buildBody = (request: {\n readonly model: JinaEmbeddingModel\n readonly task?: JinaTask\n readonly dimensions?: number\n readonly encoding?: JinaEncoding\n readonly items: ReadonlyArray<WireItem>\n}): WireBody => {\n const quant = encodingToQuant(request.encoding)\n return {\n model: request.model,\n input: request.items,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(quant !== undefined && { embedding_type: quant }),\n ...(request.encoding === \"multivector\" && { return_multivector: true }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\n/**\n * Wire response payloads. Split into four discriminated schemas so the\n * decoder can narrow precisely via `Schema.is`:\n *\n * - **Dense float** (`object: \"embedding\"`, `embedding: number[]`) —\n * default JSON form.\n * - **Dense base64** (`object: \"embedding\"`, `embedding: string`) — set\n * when `embedding_type` was `base64` or `binary`.\n * - **Sparse** (`object: \"embedding\"`, `embedding: Record<string,\n * number>`) — token-keyed weights from an ELSER-style model.\n * - **Multivector** (`object: \"embeddings\"`, plural) — token-level\n * vectors when `return_multivector: true` or a ColBERT model is used.\n */\nconst WireDenseFloatPayload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.Array(Schema.Number),\n})\ntype WireDenseFloatPayload = typeof WireDenseFloatPayload.Type\n\nconst WireDenseBase64Payload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.String,\n})\ntype WireDenseBase64Payload = typeof WireDenseBase64Payload.Type\n\nconst WireSparsePayload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.Record(Schema.String, Schema.Number),\n})\ntype WireSparsePayload = typeof WireSparsePayload.Type\n\nconst WireMultiPayload = Schema.Struct({\n object: Schema.Literal(\"embeddings\"),\n index: Schema.Number,\n embeddings: Schema.Array(Schema.Array(Schema.Number)),\n tokenized_input: Schema.optional(Schema.Array(Schema.String)),\n})\ntype WireMultiPayload = typeof WireMultiPayload.Type\n\n// Order matters: more specific schemas first. Dense float (array) and\n// multivector (different `object`) are unambiguous; sparse vs base64\n// disambiguate by the `embedding` field type.\nconst WirePayload = Schema.Union([\n WireDenseFloatPayload,\n WireSparsePayload,\n WireDenseBase64Payload,\n WireMultiPayload,\n])\ntype WirePayload = typeof WirePayload.Type\n\nconst isMultiPayload = Schema.is(WireMultiPayload)\nconst isSparsePayload = Schema.is(WireSparsePayload)\nconst isDenseBase64Payload = Schema.is(WireDenseBase64Payload)\n\nconst WireUsage = Schema.Struct({\n total_tokens: Schema.Number,\n prompt_tokens: Schema.optional(Schema.Number),\n})\ntype WireUsage = typeof WireUsage.Type\n\nconst WireResponse = Schema.Struct({\n data: Schema.Array(WirePayload),\n model: Schema.String,\n usage: WireUsage,\n})\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"jina\", raw: cause })\n\nconst encodingMismatch = (expected: JinaEncoding, got: string): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"jina\",\n param: \"encoding\",\n raw: `requested encoding=\"${expected}\" but the response contains a ${got} embedding - confirm the chosen model supports the requested encoding`,\n })\n\n/** Decode a base64 wire string to the right dense `Embedding` arm. */\nconst decodeBase64Embedding = (\n b64: string,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<Embedding, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onFailure: (cause) => Effect.fail(transportFailure(cause)),\n onSuccess: (bytes) =>\n Match.value(encoding).pipe(\n Match.when(\n \"binary\",\n (): Effect.Effect<Embedding, AiError.AiError> =>\n Effect.succeed({ _tag: \"binary\", vector: bytes }),\n ),\n Match.orElse(\n (): Effect.Effect<Embedding, AiError.AiError> =>\n Effect.succeed({\n _tag: \"float32\",\n vector: new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4),\n }),\n ),\n ),\n })\n\n/**\n * Project a wire payload to our `Embedding` discriminated union and\n * verify the requested encoding matches what came back.\n */\nconst payloadToEmbedding = (\n payload: WirePayload,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<Embedding, AiError.AiError> => {\n if (isMultiPayload(payload)) {\n return encoding === \"multivector\"\n ? Effect.succeed<Embedding>({\n _tag: \"multivector\",\n vectors: payload.embeddings.map((v) => Float32Array.from(v)),\n })\n : Effect.fail(encodingMismatch(encoding ?? \"float32\", \"multivector\"))\n }\n if (isSparsePayload(payload)) {\n return encoding === \"sparse\"\n ? Effect.succeed<Embedding>({ _tag: \"sparse\", weights: payload.embedding })\n : Effect.fail(encodingMismatch(encoding ?? \"float32\", \"sparse\"))\n }\n // Dense (float array or base64 string).\n if (encoding === \"multivector\" || encoding === \"sparse\") {\n return Effect.fail(encodingMismatch(encoding, \"dense\"))\n }\n return isDenseBase64Payload(payload)\n ? decodeBase64Embedding(payload.embedding, encoding)\n : Effect.succeed<Embedding>({\n _tag: \"float32\",\n vector: Float32Array.from(payload.embedding),\n })\n}\n\nconst indexOf = (p: WirePayload): number => p.index\n\nconst usageOf = (u: WireUsage): Usage => ({ inputTokens: u.total_tokens })\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"jina\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.jina.ai/v1\"\n\nconst postEmbed = (\n cfg: Config,\n body: WireBody,\n): Effect.Effect<typeof WireResponse.Type, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/embeddings`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst orderedEmbeddings = (\n data: ReadonlyArray<WirePayload>,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<ReadonlyArray<Embedding>, AiError.AiError> =>\n Effect.forEach(\n [...data].sort((a, b) => indexOf(a) - indexOf(b)),\n (item) => payloadToEmbedding(item, encoding),\n )\n\nconst embedImpl =\n (cfg: Config) =>\n (\n request: JinaEmbedRequest,\n ): Effect.Effect<EmbedResponse, AiError.AiError, HttpClient.HttpClient> =>\n inputToItem(request.input).pipe(\n Effect.flatMap((item) =>\n postEmbed(\n cfg,\n buildBody({\n model: request.model,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(request.encoding !== undefined && { encoding: request.encoding }),\n items: [item],\n }),\n ),\n ),\n Effect.flatMap((decoded) => {\n const first = decoded.data[0]\n if (first === undefined) {\n return Effect.fail(transportFailure(\"Jina returned empty `data` array\"))\n }\n return payloadToEmbedding(first, request.encoding).pipe(\n Effect.map((embedding): EmbedResponse => ({ embedding, usage: usageOf(decoded.usage) })),\n )\n }),\n )\n\nconst embedManyImpl =\n (cfg: Config) =>\n (\n request: JinaEmbedManyRequest,\n ): Effect.Effect<EmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.forEach(request.inputs, inputToItem).pipe(\n Effect.flatMap((items) =>\n postEmbed(\n cfg,\n buildBody({\n model: request.model,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(request.encoding !== undefined && { encoding: request.encoding }),\n items,\n }),\n ),\n ),\n Effect.flatMap((decoded) =>\n orderedEmbeddings(decoded.data, request.encoding).pipe(\n Effect.map(\n (embeddings): EmbedManyResponse => ({ embeddings, usage: usageOf(decoded.usage) }),\n ),\n ),\n ),\n )\n\n/**\n * Map cross-provider `query`/`document` to Jina's dotted-pair task\n * vocabulary. `undefined` stays `undefined`.\n */\nconst mapGenericTask: (t: \"query\" | \"document\" | undefined) => JinaTask | undefined = Match.type<\n \"query\" | \"document\" | undefined\n>().pipe(\n Match.when(\"query\", (): JinaTask => \"retrieval.query\"),\n Match.when(\"document\", (): JinaTask => \"retrieval.passage\"),\n Match.orElse((): JinaTask | undefined => undefined),\n)\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `JinaEmbeddingService` value. For Layer-based setup, prefer\n * `layer`.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<JinaEmbeddingService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({\n embed: (request) =>\n embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n embedMany: (request) =>\n embedManyImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer that registers both the provider-specific `JinaEmbedding` tag\n * and the generic `EmbeddingModel` tag, sharing one underlying\n * implementation. The generic registration maps cross-provider\n * `query`/`document` to Jina's `retrieval.query` / `retrieval.passage`\n * task strings; other request fields pass through.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<JinaEmbedding | EmbeddingModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(JinaEmbedding, make(cfg))\n const generic = Layer.effect(\n EmbeddingModel,\n Effect.map(\n make(cfg),\n (s): EmbeddingModelService => ({\n embed: (req: CommonEmbedRequest) => {\n const task = mapGenericTask(req.task)\n return s.embed({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedRequest)\n },\n embedMany: (req: CommonEmbedManyRequest) => {\n const task = mapGenericTask(req.task)\n return s.embedMany({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedManyRequest)\n },\n }),\n ),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"JinaEmbedding.mjs","names":[],"sources":["../src/JinaEmbedding.ts"],"sourcesContent":["import { Context, Effect, Encoding, Layer, Match, Redacted, Result, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { Embedding, EmbedContentPart, EmbedInput, Usage } from \"@effect-uai/core/Embedding\"\n\n// Internal open-union response shape — impls return this; the typed\n// `make()` wrapper casts to the narrowed `EmbedResponse<E>`.\ntype AnyEmbedResponse = { readonly embedding: Embedding; readonly usage: Usage }\ntype AnyEmbedManyResponse = { readonly embeddings: ReadonlyArray<Embedding>; readonly usage: Usage }\nimport {\n type CommonEmbedManyRequest,\n type CommonEmbedRequest,\n type EmbedEncoding,\n EmbeddingModel,\n type EmbeddingModelService,\n type EmbedManyResponse,\n type EmbedResponse,\n} from \"@effect-uai/core/EmbeddingModel\"\nimport type { ImageSource } from \"@effect-uai/core/Image\"\nimport type { JinaEmbeddingModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Jina's task vocabulary. v4 and v3/v5 share the dotted-pair form:\n * - `retrieval.query` / `retrieval.passage` — asymmetric retrieval.\n * - `text-matching` — symmetric similarity between two texts.\n * - `code.query` / `code.passage` — code retrieval / matching (v4-only).\n * - `classification` / `separation` — v3 / v5 only.\n *\n * The `(string & {})` tail accepts any string so newly-released task\n * types work without an SDK update.\n */\nexport type JinaTask =\n | \"retrieval.query\"\n | \"retrieval.passage\"\n | \"text-matching\"\n | \"code.query\"\n | \"code.passage\"\n | \"classification\"\n | \"separation\"\n // eslint-disable-next-line @typescript-eslint/ban-types\n | (string & {})\n\n/**\n * Jina's supported `encoding` values. Provider-narrowed from the cross-\n * provider `EmbedEncoding` union.\n *\n * Compatibility with models is checked at the response level rather than\n * pre-flight: if you ask for `multivector` against a model that doesn't\n * produce one, Jina's API rejects the request with a clear error; if\n * you ask for `sparse` and the chosen model returns dense, our decoder\n * fails with a typed mismatch. No hardcoded model-encoding table to\n * maintain.\n *\n * Wire mapping:\n * - `multivector` → request sets `return_multivector: true`\n * (`jina-embeddings-v4` + ColBERT models).\n * - `sparse` → no flag; the model itself (`elser-v2`) determines the\n * output shape.\n * - `binary` → request sets `embedding_type: \"binary\"`.\n * - `float32` → no flag; default JSON `number[]`.\n */\nexport type JinaEncoding = \"float32\" | \"binary\" | \"sparse\" | \"multivector\"\n\nexport type JinaEmbedRequest = Omit<CommonEmbedRequest, \"model\" | \"task\" | \"encoding\"> & {\n /** Narrows `CommonEmbedRequest.model` to the typed Jina union. */\n readonly model: JinaEmbeddingModel\n /**\n * Widens the cross-provider `\"query\" | \"document\"` to Jina's task enum.\n * Required by v3+ for retrieval-quality results. v4 unifies query and\n * document under `retrieval`.\n */\n readonly task?: JinaTask\n /** Narrows `CommonEmbedRequest.encoding` to Jina's supported set. */\n readonly encoding?: JinaEncoding\n}\n\nexport type JinaEmbedManyRequest = Omit<JinaEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\nexport type JinaEmbeddingService = {\n readonly embed: <E extends JinaEncoding | undefined = undefined>(\n request: Omit<JinaEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedResponse<E>, AiError.AiError>\n readonly embedMany: <E extends JinaEncoding | undefined = undefined>(\n request: Omit<JinaEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Jina-typed embedding\n * requests; yield the generic `EmbeddingModel` tag for provider-portable\n * code. Both are registered by `layer`.\n */\nexport class JinaEmbedding extends Context.Service<JinaEmbedding, JinaEmbeddingService>()(\n \"@betalyra/effect-uai/providers/jina/JinaEmbedding\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireTextItem = {\n readonly text: string\n}\ntype WireImageItem = {\n readonly image: string\n}\ntype WireItem = WireTextItem | WireImageItem\n\n/**\n * Jina v4 accepts `{ image: <url> }` or `{ image: <base64-string> }`.\n * The server detects which form by looking at the value. We pass URLs\n * through directly and base64-encode raw bytes.\n */\nconst imageSourceToItem: (s: ImageSource) => WireItem = Match.type<ImageSource>().pipe(\n Match.tag(\"url\", (s): WireItem => ({ image: s.url })),\n Match.tag(\"base64\", (s): WireItem => ({ image: s.base64 })),\n Match.tag(\"bytes\", (s): WireItem => ({ image: Encoding.encodeBase64(s.bytes) })),\n Match.exhaustive,\n)\n\nconst contentPartToItem: (part: EmbedContentPart) => WireItem = Match.type<EmbedContentPart>().pipe(\n Match.when({ text: Match.string }, ({ text }): WireItem => ({ text })),\n Match.when({ image: Match.any }, ({ image }) => imageSourceToItem(image)),\n Match.exhaustive,\n)\n\nconst multiPartContentRejected: AiError.AiError = new AiError.InvalidRequest({\n provider: \"jina\",\n param: \"input.content\",\n raw: \"Jina treats each input[] entry as one item; multi-part `content[]` would lose the grouping. Split into separate `inputs[]` entries.\",\n})\n\n/**\n * Project an `EmbedInput` to one wire item. A single-part `content[]` is\n * unwrapped; multi-part `content[]` is rejected because Jina's flat\n * `input[]` would lose the grouping.\n */\nconst inputToItem: (input: EmbedInput) => Effect.Effect<WireItem, AiError.AiError> =\n Match.type<EmbedInput>().pipe(\n Match.when(\n Match.string,\n (s): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text: s }),\n ),\n Match.when(\n { text: Match.string },\n ({ text }): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text }),\n ),\n Match.when(\n { image: Match.any },\n ({ image }): Effect.Effect<WireItem, AiError.AiError> =>\n Effect.succeed(imageSourceToItem(image)),\n ),\n Match.when(\n { content: Match.any },\n ({ content }): Effect.Effect<WireItem, AiError.AiError> =>\n content.length === 1\n ? Effect.succeed(contentPartToItem(content[0]!))\n : Effect.fail(multiPartContentRejected),\n ),\n Match.exhaustive,\n )\n\ntype WireBody = {\n readonly model: string\n readonly input: ReadonlyArray<WireItem>\n readonly task?: string\n readonly dimensions?: number\n readonly embedding_type?: \"float\" | \"base64\" | \"binary\" | \"ubinary\"\n readonly return_multivector?: boolean\n}\n\ntype WireQuant = WireBody[\"embedding_type\"]\n\n/**\n * Map our `JinaEncoding` to Jina's `embedding_type` wire field. Only\n * `binary` actually changes the wire transport; the others either omit\n * the field (`float32`) or ignore it (`sparse` / `multivector` use\n * separate signalling — model choice and `return_multivector`).\n */\nconst encodingToQuant: (encoding: JinaEncoding | undefined) => WireQuant = Match.type<\n JinaEncoding | undefined\n>().pipe(\n Match.when(\"binary\", (): WireQuant => \"binary\"),\n Match.orElse((): WireQuant => undefined),\n)\n\nconst buildBody = (request: {\n readonly model: JinaEmbeddingModel\n readonly task?: JinaTask\n readonly dimensions?: number\n readonly encoding?: JinaEncoding\n readonly items: ReadonlyArray<WireItem>\n}): WireBody => {\n const quant = encodingToQuant(request.encoding)\n return {\n model: request.model,\n input: request.items,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(quant !== undefined && { embedding_type: quant }),\n ...(request.encoding === \"multivector\" && { return_multivector: true }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\n/**\n * Wire response payloads. Split into four discriminated schemas so the\n * decoder can narrow precisely via `Schema.is`:\n *\n * - **Dense float** (`object: \"embedding\"`, `embedding: number[]`) —\n * default JSON form.\n * - **Dense base64** (`object: \"embedding\"`, `embedding: string`) — set\n * when `embedding_type` was `base64` or `binary`.\n * - **Sparse** (`object: \"embedding\"`, `embedding: Record<string,\n * number>`) — token-keyed weights from an ELSER-style model.\n * - **Multivector** (`object: \"embeddings\"`, plural) — token-level\n * vectors when `return_multivector: true` or a ColBERT model is used.\n */\nconst WireDenseFloatPayload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.Array(Schema.Number),\n})\ntype WireDenseFloatPayload = typeof WireDenseFloatPayload.Type\n\nconst WireDenseBase64Payload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.String,\n})\ntype WireDenseBase64Payload = typeof WireDenseBase64Payload.Type\n\nconst WireSparsePayload = Schema.Struct({\n object: Schema.Literal(\"embedding\"),\n index: Schema.Number,\n embedding: Schema.Record(Schema.String, Schema.Number),\n})\ntype WireSparsePayload = typeof WireSparsePayload.Type\n\nconst WireMultiPayload = Schema.Struct({\n object: Schema.Literal(\"embeddings\"),\n index: Schema.Number,\n embeddings: Schema.Array(Schema.Array(Schema.Number)),\n tokenized_input: Schema.optional(Schema.Array(Schema.String)),\n})\ntype WireMultiPayload = typeof WireMultiPayload.Type\n\n// Order matters: more specific schemas first. Dense float (array) and\n// multivector (different `object`) are unambiguous; sparse vs base64\n// disambiguate by the `embedding` field type.\nconst WirePayload = Schema.Union([\n WireDenseFloatPayload,\n WireSparsePayload,\n WireDenseBase64Payload,\n WireMultiPayload,\n])\ntype WirePayload = typeof WirePayload.Type\n\nconst isMultiPayload = Schema.is(WireMultiPayload)\nconst isSparsePayload = Schema.is(WireSparsePayload)\nconst isDenseBase64Payload = Schema.is(WireDenseBase64Payload)\n\nconst WireUsage = Schema.Struct({\n total_tokens: Schema.Number,\n prompt_tokens: Schema.optional(Schema.Number),\n})\ntype WireUsage = typeof WireUsage.Type\n\nconst WireResponse = Schema.Struct({\n data: Schema.Array(WirePayload),\n model: Schema.String,\n usage: WireUsage,\n})\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"jina\", raw: cause })\n\nconst encodingMismatch = (expected: JinaEncoding, got: string): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"jina\",\n param: \"encoding\",\n raw: `requested encoding=\"${expected}\" but the response contains a ${got} embedding - confirm the chosen model supports the requested encoding`,\n })\n\n/** Decode a base64 wire string to the right dense `Embedding` arm. */\nconst decodeBase64Embedding = (\n b64: string,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<Embedding, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onFailure: (cause) => Effect.fail(transportFailure(cause)),\n onSuccess: (bytes) =>\n Match.value(encoding).pipe(\n Match.when(\n \"binary\",\n (): Effect.Effect<Embedding, AiError.AiError> =>\n Effect.succeed({ _tag: \"binary\", vector: bytes }),\n ),\n Match.orElse(\n (): Effect.Effect<Embedding, AiError.AiError> =>\n Effect.succeed({\n _tag: \"float32\",\n vector: new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4),\n }),\n ),\n ),\n })\n\n/**\n * Project a wire payload to our `Embedding` discriminated union and\n * verify the requested encoding matches what came back.\n */\nconst payloadToEmbedding = (\n payload: WirePayload,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<Embedding, AiError.AiError> => {\n if (isMultiPayload(payload)) {\n return encoding === \"multivector\"\n ? Effect.succeed<Embedding>({\n _tag: \"multivector\",\n vectors: payload.embeddings.map((v) => Float32Array.from(v)),\n })\n : Effect.fail(encodingMismatch(encoding ?? \"float32\", \"multivector\"))\n }\n if (isSparsePayload(payload)) {\n return encoding === \"sparse\"\n ? Effect.succeed<Embedding>({ _tag: \"sparse\", weights: payload.embedding })\n : Effect.fail(encodingMismatch(encoding ?? \"float32\", \"sparse\"))\n }\n // Dense (float array or base64 string).\n if (encoding === \"multivector\" || encoding === \"sparse\") {\n return Effect.fail(encodingMismatch(encoding, \"dense\"))\n }\n return isDenseBase64Payload(payload)\n ? decodeBase64Embedding(payload.embedding, encoding)\n : Effect.succeed<Embedding>({\n _tag: \"float32\",\n vector: Float32Array.from(payload.embedding),\n })\n}\n\nconst indexOf = (p: WirePayload): number => p.index\n\nconst usageOf = (u: WireUsage): Usage => ({ inputTokens: u.total_tokens })\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"jina\"\n const raw = body\n if (status === 429) return new AiError.RateLimited({ provider, raw })\n if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })\n if (status === 401) return new AiError.AuthFailed({ provider, subtype: \"auth\", raw })\n if (status === 403) return new AiError.AuthFailed({ provider, subtype: \"permission\", raw })\n if (status === 402) return new AiError.AuthFailed({ provider, subtype: \"billing\", raw })\n if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })\n if (status >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.jina.ai/v1\"\n\nconst postEmbed = (\n cfg: Config,\n body: WireBody,\n): Effect.Effect<typeof WireResponse.Type, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/embeddings`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst orderedEmbeddings = (\n data: ReadonlyArray<WirePayload>,\n encoding: JinaEncoding | undefined,\n): Effect.Effect<ReadonlyArray<Embedding>, AiError.AiError> =>\n Effect.forEach(\n [...data].sort((a, b) => indexOf(a) - indexOf(b)),\n (item) => payloadToEmbedding(item, encoding),\n )\n\nconst embedImpl =\n (cfg: Config) =>\n (\n request: JinaEmbedRequest,\n ): Effect.Effect<AnyEmbedResponse, AiError.AiError, HttpClient.HttpClient> =>\n inputToItem(request.input).pipe(\n Effect.flatMap((item) =>\n postEmbed(\n cfg,\n buildBody({\n model: request.model,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(request.encoding !== undefined && { encoding: request.encoding }),\n items: [item],\n }),\n ),\n ),\n Effect.flatMap((decoded) => {\n const first = decoded.data[0]\n if (first === undefined) {\n return Effect.fail(transportFailure(\"Jina returned empty `data` array\"))\n }\n return payloadToEmbedding(first, request.encoding).pipe(\n Effect.map(\n (embedding): AnyEmbedResponse => ({ embedding, usage: usageOf(decoded.usage) }),\n ),\n )\n }),\n )\n\nconst embedManyImpl =\n (cfg: Config) =>\n (\n request: JinaEmbedManyRequest,\n ): Effect.Effect<AnyEmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.forEach(request.inputs, inputToItem).pipe(\n Effect.flatMap((items) =>\n postEmbed(\n cfg,\n buildBody({\n model: request.model,\n ...(request.task !== undefined && { task: request.task }),\n ...(request.dimensions !== undefined && { dimensions: request.dimensions }),\n ...(request.encoding !== undefined && { encoding: request.encoding }),\n items,\n }),\n ),\n ),\n Effect.flatMap((decoded) =>\n orderedEmbeddings(decoded.data, request.encoding).pipe(\n Effect.map(\n (embeddings): AnyEmbedManyResponse => ({\n embeddings,\n usage: usageOf(decoded.usage),\n }),\n ),\n ),\n ),\n )\n\n/**\n * Map cross-provider `query`/`document` to Jina's dotted-pair task\n * vocabulary. `undefined` stays `undefined`.\n */\nconst mapGenericTask: (t: \"query\" | \"document\" | undefined) => JinaTask | undefined = Match.type<\n \"query\" | \"document\" | undefined\n>().pipe(\n Match.when(\"query\", (): JinaTask => \"retrieval.query\"),\n Match.when(\"document\", (): JinaTask => \"retrieval.passage\"),\n Match.orElse((): JinaTask | undefined => undefined),\n)\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `JinaEmbeddingService` value. For Layer-based setup, prefer\n * `layer`.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<JinaEmbeddingService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({\n embed: <E extends JinaEncoding | undefined = undefined>(\n request: Omit<JinaEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n embedImpl(cfg)(request as JinaEmbedRequest).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ) as Effect.Effect<EmbedResponse<E>, AiError.AiError>,\n embedMany: <E extends JinaEncoding | undefined = undefined>(\n request: Omit<JinaEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) =>\n embedManyImpl(cfg)(request as JinaEmbedManyRequest).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>,\n }))\n\n/**\n * Layer that registers both the provider-specific `JinaEmbedding` tag\n * and the generic `EmbeddingModel` tag, sharing one underlying\n * implementation. The generic registration maps cross-provider\n * `query`/`document` to Jina's `retrieval.query` / `retrieval.passage`\n * task strings; other request fields pass through.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<JinaEmbedding | EmbeddingModel, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(JinaEmbedding, make(cfg))\n const generic = Layer.effect(\n EmbeddingModel,\n Effect.map(\n make(cfg),\n (s): EmbeddingModelService => ({\n embed: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => {\n const task = mapGenericTask(req.task)\n return s.embed({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedRequest) as Effect.Effect<EmbedResponse<E>, AiError.AiError>\n },\n embedMany: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) => {\n const task = mapGenericTask(req.task)\n return s.embedMany({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedManyRequest) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\n },\n }),\n ),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/jina",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Jina AI provider for @effect-uai/core (embeddings, rerank).",
5
5
  "keywords": [
6
6
  "ai",
@@ -45,7 +45,7 @@
45
45
  "devDependencies": {
46
46
  "effect": "4.0.0-beta.57",
47
47
  "typescript": "^6.0.3",
48
- "@effect-uai/core": "0.4.0"
48
+ "@effect-uai/core": "0.5.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@effect-uai/core": ">=0.2.0 <1",
@@ -2,9 +2,15 @@ import { Context, Effect, Encoding, Layer, Match, Redacted, Result, Schema } fro
2
2
  import { HttpClient, HttpClientRequest } from "effect/unstable/http"
3
3
  import * as AiError from "@effect-uai/core/AiError"
4
4
  import type { Embedding, EmbedContentPart, EmbedInput, Usage } from "@effect-uai/core/Embedding"
5
+
6
+ // Internal open-union response shape — impls return this; the typed
7
+ // `make()` wrapper casts to the narrowed `EmbedResponse<E>`.
8
+ type AnyEmbedResponse = { readonly embedding: Embedding; readonly usage: Usage }
9
+ type AnyEmbedManyResponse = { readonly embeddings: ReadonlyArray<Embedding>; readonly usage: Usage }
5
10
  import {
6
11
  type CommonEmbedManyRequest,
7
12
  type CommonEmbedRequest,
13
+ type EmbedEncoding,
8
14
  EmbeddingModel,
9
15
  type EmbeddingModelService,
10
16
  type EmbedManyResponse,
@@ -40,7 +46,7 @@ export type JinaTask =
40
46
 
41
47
  /**
42
48
  * Jina's supported `encoding` values. Provider-narrowed from the cross-
43
- * provider `Encoding` union.
49
+ * provider `EmbedEncoding` union.
44
50
  *
45
51
  * Compatibility with models is checked at the response level rather than
46
52
  * pre-flight: if you ask for `multivector` against a model that doesn't
@@ -77,10 +83,12 @@ export type JinaEmbedManyRequest = Omit<JinaEmbedRequest, "input"> & {
77
83
  }
78
84
 
79
85
  export type JinaEmbeddingService = {
80
- readonly embed: (request: JinaEmbedRequest) => Effect.Effect<EmbedResponse, AiError.AiError>
81
- readonly embedMany: (
82
- request: JinaEmbedManyRequest,
83
- ) => Effect.Effect<EmbedManyResponse, AiError.AiError>
86
+ readonly embed: <E extends JinaEncoding | undefined = undefined>(
87
+ request: Omit<JinaEmbedRequest, "encoding"> & { readonly encoding?: E },
88
+ ) => Effect.Effect<EmbedResponse<E>, AiError.AiError>
89
+ readonly embedMany: <E extends JinaEncoding | undefined = undefined>(
90
+ request: Omit<JinaEmbedManyRequest, "encoding"> & { readonly encoding?: E },
91
+ ) => Effect.Effect<EmbedManyResponse<E>, AiError.AiError>
84
92
  }
85
93
 
86
94
  /**
@@ -385,7 +393,7 @@ const postEmbed = (
385
393
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))
386
394
  if (response.status >= 400) {
387
395
  const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""))
388
- return yield* Effect.fail(httpStatusError(response.status, text))
396
+ return yield* httpStatusError(response.status, text)
389
397
  }
390
398
  const json = yield* response.json.pipe(Effect.mapError(transportFailure))
391
399
  return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(
@@ -410,7 +418,7 @@ const embedImpl =
410
418
  (cfg: Config) =>
411
419
  (
412
420
  request: JinaEmbedRequest,
413
- ): Effect.Effect<EmbedResponse, AiError.AiError, HttpClient.HttpClient> =>
421
+ ): Effect.Effect<AnyEmbedResponse, AiError.AiError, HttpClient.HttpClient> =>
414
422
  inputToItem(request.input).pipe(
415
423
  Effect.flatMap((item) =>
416
424
  postEmbed(
@@ -430,7 +438,9 @@ const embedImpl =
430
438
  return Effect.fail(transportFailure("Jina returned empty `data` array"))
431
439
  }
432
440
  return payloadToEmbedding(first, request.encoding).pipe(
433
- Effect.map((embedding): EmbedResponse => ({ embedding, usage: usageOf(decoded.usage) })),
441
+ Effect.map(
442
+ (embedding): AnyEmbedResponse => ({ embedding, usage: usageOf(decoded.usage) }),
443
+ ),
434
444
  )
435
445
  }),
436
446
  )
@@ -439,7 +449,7 @@ const embedManyImpl =
439
449
  (cfg: Config) =>
440
450
  (
441
451
  request: JinaEmbedManyRequest,
442
- ): Effect.Effect<EmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>
452
+ ): Effect.Effect<AnyEmbedManyResponse, AiError.AiError, HttpClient.HttpClient> =>
443
453
  Effect.forEach(request.inputs, inputToItem).pipe(
444
454
  Effect.flatMap((items) =>
445
455
  postEmbed(
@@ -456,7 +466,10 @@ const embedManyImpl =
456
466
  Effect.flatMap((decoded) =>
457
467
  orderedEmbeddings(decoded.data, request.encoding).pipe(
458
468
  Effect.map(
459
- (embeddings): EmbedManyResponse => ({ embeddings, usage: usageOf(decoded.usage) }),
469
+ (embeddings): AnyEmbedManyResponse => ({
470
+ embeddings,
471
+ usage: usageOf(decoded.usage),
472
+ }),
460
473
  ),
461
474
  ),
462
475
  ),
@@ -486,10 +499,18 @@ export const make = (
486
499
  cfg: Config,
487
500
  ): Effect.Effect<JinaEmbeddingService, never, HttpClient.HttpClient> =>
488
501
  Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
489
- embed: (request) =>
490
- embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
491
- embedMany: (request) =>
492
- embedManyImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
502
+ embed: <E extends JinaEncoding | undefined = undefined>(
503
+ request: Omit<JinaEmbedRequest, "encoding"> & { readonly encoding?: E },
504
+ ) =>
505
+ embedImpl(cfg)(request as JinaEmbedRequest).pipe(
506
+ Effect.provideService(HttpClient.HttpClient, client),
507
+ ) as Effect.Effect<EmbedResponse<E>, AiError.AiError>,
508
+ embedMany: <E extends JinaEncoding | undefined = undefined>(
509
+ request: Omit<JinaEmbedManyRequest, "encoding"> & { readonly encoding?: E },
510
+ ) =>
511
+ embedManyImpl(cfg)(request as JinaEmbedManyRequest).pipe(
512
+ Effect.provideService(HttpClient.HttpClient, client),
513
+ ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>,
493
514
  }))
494
515
 
495
516
  /**
@@ -508,21 +529,25 @@ export const layer = (
508
529
  Effect.map(
509
530
  make(cfg),
510
531
  (s): EmbeddingModelService => ({
511
- embed: (req: CommonEmbedRequest) => {
532
+ embed: <E extends EmbedEncoding | undefined = undefined>(
533
+ req: Omit<CommonEmbedRequest, "encoding"> & { readonly encoding?: E },
534
+ ) => {
512
535
  const task = mapGenericTask(req.task)
513
536
  return s.embed({
514
537
  ...req,
515
538
  model: req.model as JinaEmbeddingModel,
516
539
  ...(task !== undefined && { task }),
517
- } as JinaEmbedRequest)
540
+ } as JinaEmbedRequest) as Effect.Effect<EmbedResponse<E>, AiError.AiError>
518
541
  },
519
- embedMany: (req: CommonEmbedManyRequest) => {
542
+ embedMany: <E extends EmbedEncoding | undefined = undefined>(
543
+ req: Omit<CommonEmbedManyRequest, "encoding"> & { readonly encoding?: E },
544
+ ) => {
520
545
  const task = mapGenericTask(req.task)
521
546
  return s.embedMany({
522
547
  ...req,
523
548
  model: req.model as JinaEmbeddingModel,
524
549
  ...(task !== undefined && { task }),
525
- } as JinaEmbedManyRequest)
550
+ } as JinaEmbedManyRequest) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>
526
551
  },
527
552
  }),
528
553
  ),