@effect-uai/jina 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/JinaEmbedding.d.mts +4 -4
- package/dist/JinaEmbedding.d.mts.map +1 -1
- package/dist/JinaEmbedding.mjs +1 -1
- package/dist/JinaEmbedding.mjs.map +1 -1
- package/dist/JinaReader.d.mts +7 -5
- package/dist/JinaReader.d.mts.map +1 -1
- package/dist/JinaReader.mjs +1 -1
- package/dist/JinaReader.mjs.map +1 -1
- package/dist/JinaReranker.d.mts +53 -0
- package/dist/JinaReranker.d.mts.map +1 -0
- package/dist/JinaReranker.mjs +120 -0
- package/dist/JinaReranker.mjs.map +1 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +2 -1
- package/dist/models.d.mts +12 -1
- package/dist/models.d.mts.map +1 -1
- package/package.json +9 -5
- package/src/JinaEmbedding.ts +60 -73
- package/src/JinaReranker.ts +203 -0
- package/src/index.ts +1 -0
- package/src/models.ts +17 -0
- /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
package/dist/JinaEmbedding.d.mts
CHANGED
|
@@ -4,8 +4,6 @@ import { HttpClient } from "effect/unstable/http";
|
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import { CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel } from "@effect-uai/core/EmbeddingModel";
|
|
6
6
|
import { EmbedInput } from "@effect-uai/core/Embedding";
|
|
7
|
-
|
|
8
|
-
//#region src/JinaEmbedding.d.ts
|
|
9
7
|
declare namespace JinaEmbedding_d_exports {
|
|
10
8
|
export { Config, JinaEmbedManyRequest, JinaEmbedRequest, JinaEmbedding, JinaEmbeddingService, JinaEncoding, JinaTask, layer, make };
|
|
11
9
|
}
|
|
@@ -41,13 +39,15 @@ type JinaTask = "retrieval.query" | "retrieval.passage" | "text-matching" | "cod
|
|
|
41
39
|
*/
|
|
42
40
|
type JinaEncoding = "float32" | "binary" | "sparse" | "multivector";
|
|
43
41
|
type JinaEmbedRequest = Omit<CommonEmbedRequest, "model" | "task" | "encoding"> & {
|
|
44
|
-
/** Narrows `CommonEmbedRequest.model` to the typed Jina union. */
|
|
42
|
+
/** Narrows `CommonEmbedRequest.model` to the typed Jina union. */
|
|
43
|
+
readonly model: JinaEmbeddingModel;
|
|
45
44
|
/**
|
|
46
45
|
* Widens the cross-provider `"query" | "document"` to Jina's task enum.
|
|
47
46
|
* Required by v3+ for retrieval-quality results. v4 unifies query and
|
|
48
47
|
* document under `retrieval`.
|
|
49
48
|
*/
|
|
50
|
-
readonly task?: JinaTask;
|
|
49
|
+
readonly task?: JinaTask;
|
|
50
|
+
/** Narrows `CommonEmbedRequest.encoding` to Jina's supported set. */
|
|
51
51
|
readonly encoding?: JinaEncoding;
|
|
52
52
|
};
|
|
53
53
|
type JinaEmbedManyRequest = Omit<JinaEmbedRequest, "input"> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JinaEmbedding.d.mts","names":[],"sources":["../src/JinaEmbedding.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"JinaEmbedding.d.mts","names":[],"sources":["../src/JinaEmbedding.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KAoCY;;;;;;;;;;;;;;;;;;;;KA8BA;KAEA,mBAAmB,KAAK;;WAEzB,OAAO;;;;;;WAMP,OAAO;;WAEP,WAAW;;KAGV,uBAAuB,KAAK;WAC7B,QAAQ,cAAc;;KAGrB;WACD,QAAQ,UAAU,sCACzB,SAAS,KAAK;aAA2C,WAAW;QACjE,OAAO,OAAO,cAAc,IAAI,QAAQ;WACpC,YAAY,UAAU,sCAC7B,SAAS,KAAK;aAA+C,WAAW;QACrE,OAAO,OAAO,kBAAkB,IAAI,QAAQ;;;;;;;;cAQtC,sBAAsB;KAIvB;WACD,QAAQ,SAAS;WACjB;;;;;;cAoYE,OAAI,KACV,WACJ,OAAO,OAAO,6BAA6B,WAAW;;;;;;;;cAuB5C,QAAK,KACX,WACJ,MAAM,MAAM,gBAAgB,uBAAuB,WAAW"}
|
package/dist/JinaEmbedding.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { Context, Effect, Encoding, Layer, Match, Result, Schema } from "effect";
|
|
3
3
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
@@ -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\"\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 assertEncoding,\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\n// Jina's flat `input[]` can't fuse a multi-part `content[]` into one vector.\n// Bucket 1: silently splitting or dropping parts would change what the vector\n// represents (and the vector count). Reject as `Unsupported` (capability gap),\n// not `InvalidRequest` (wire-shape) - Jina does carry images, just not fused.\nconst multiPartContentRejected: AiError.AiError = new AiError.Unsupported({\n provider: \"jina\",\n capability: \"multiPartInput\",\n reason:\n \"Jina treats each input[] entry as one item; multi-part `content[]` can't be fused into one vector. 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, (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 // Jina emits dense float32 / binary (binary is bit-quantized, packed\n // into int8 bytes on the wire - NOT scalar int8 per dimension; sparse /\n // multivector live on the provider-typed JinaEncoding, off the\n // cross-provider set). Reject a scalar `int8` encoding (bucket 1) so it\n // isn't returned as a mislabeled vector, matching OpenAI / Gemini.\n embed: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => {\n const task = mapGenericTask(req.task)\n return assertEncoding(req.encoding, [\"float32\", \"binary\"], \"jina\").pipe(\n Effect.andThen(\n s.embed({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedRequest),\n ),\n ) 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 assertEncoding(req.encoding, [\"float32\", \"binary\"], \"jina\").pipe(\n Effect.andThen(\n s.embedMany({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedManyRequest),\n ),\n ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\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 assertEncoding,\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\n// Jina's flat `input[]` can't fuse a multi-part `content[]` into one vector.\n// Bucket 1: silently splitting or dropping parts would change what the vector\n// represents (and the vector count). Reject as `Unsupported` (capability gap),\n// not `InvalidRequest` (wire-shape) - Jina does carry images, just not fused.\nconst multiPartContentRejected: AiError.AiError = new AiError.Unsupported({\n provider: \"jina\",\n capability: \"multiPartInput\",\n reason:\n \"Jina treats each input[] entry as one item; multi-part `content[]` can't be fused into one vector. 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(Match.string, (s): Effect.Effect<WireItem, AiError.AiError> =>\n Effect.succeed({ text: s }),\n ),\n Match.when({ text: Match.string }, ({ text }): Effect.Effect<WireItem, AiError.AiError> =>\n Effect.succeed({ text }),\n ),\n Match.when({ image: Match.any }, ({ image }): Effect.Effect<WireItem, AiError.AiError> =>\n Effect.succeed(imageSourceToItem(image)),\n ),\n Match.when({ content: Match.any }, ({ 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(\"binary\", (): Effect.Effect<Embedding, AiError.AiError> =>\n Effect.succeed({ _tag: \"binary\", vector: bytes }),\n ),\n Match.orElse((): 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((embedding): AnyEmbedResponse => ({\n embedding,\n 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((embeddings): AnyEmbedManyResponse => ({\n embeddings,\n 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, (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(make(cfg), (s): EmbeddingModelService => ({\n // Jina emits dense float32 / binary (binary is bit-quantized, packed\n // into int8 bytes on the wire - NOT scalar int8 per dimension; sparse /\n // multivector live on the provider-typed JinaEncoding, off the\n // cross-provider set). Reject a scalar `int8` encoding (bucket 1) so it\n // isn't returned as a mislabeled vector, matching OpenAI / Gemini.\n embed: <E extends EmbedEncoding | undefined = undefined>(\n req: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => {\n const task = mapGenericTask(req.task)\n return assertEncoding(req.encoding, [\"float32\", \"binary\"], \"jina\").pipe(\n Effect.andThen(\n s.embed({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedRequest),\n ),\n ) 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 assertEncoding(req.encoding, [\"float32\", \"binary\"], \"jina\").pipe(\n Effect.andThen(\n s.embedMany({\n ...req,\n model: req.model as JinaEmbeddingModel,\n ...(task !== undefined && { task }),\n } as JinaEmbedManyRequest),\n ),\n ) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\n },\n })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
package/dist/JinaReader.d.mts
CHANGED
|
@@ -3,8 +3,6 @@ import { Context, Effect, Layer, Redacted } from "effect";
|
|
|
3
3
|
import { HttpClient } from "effect/unstable/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import { CommonReadRequest, ReadResponse, WebRead } from "@effect-uai/core/WebRead";
|
|
6
|
-
|
|
7
|
-
//#region src/JinaReader.d.ts
|
|
8
6
|
declare namespace JinaReader_d_exports {
|
|
9
7
|
export { Config, JinaReadRequest, JinaReader, JinaReaderService, layer, make };
|
|
10
8
|
}
|
|
@@ -14,9 +12,13 @@ declare namespace JinaReader_d_exports {
|
|
|
14
12
|
* the codec below).
|
|
15
13
|
*/
|
|
16
14
|
type JinaReadRequest = CommonReadRequest & {
|
|
17
|
-
/** Fetching engine - see {@link JinaEngine}. */
|
|
18
|
-
readonly
|
|
19
|
-
|
|
15
|
+
/** Fetching engine - see {@link JinaEngine}. */
|
|
16
|
+
readonly engine?: JinaEngine;
|
|
17
|
+
/** CSS selector to extract instead of the whole page (`X-Target-Selector`). */
|
|
18
|
+
readonly targetSelector?: string;
|
|
19
|
+
/** Bypass Jina's page cache (`X-No-Cache`). */
|
|
20
|
+
readonly noCache?: boolean;
|
|
21
|
+
/** Append a deduplicated link list, populating {@link ReadResponse.links}. */
|
|
20
22
|
readonly withLinksSummary?: boolean;
|
|
21
23
|
};
|
|
22
24
|
type JinaReaderService = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JinaReader.d.mts","names":[],"sources":["../src/JinaReader.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"JinaReader.d.mts","names":[],"sources":["../src/JinaReader.ts"],"mappings":";;;;;;;;;;;;;KAoBY,kBAAkB;;WAEnB,SAAS;;WAET;;WAEA;;WAEA;;KAGC;WACD,OAAO,SAAS,oBAAoB,OAAO,OAAO,cAAc,QAAQ;;;;;;;;cAQtE,mBAAmB;KAIpB;WACD,QAAQ,SAAS;WACjB;;;;;;cAkIE,OAAI,KAAS,WAAS,OAAO,OAAO,0BAA0B,WAAW;;;;;;;cAYzE,QAAK,KACX,WACJ,MAAM,MAAM,aAAa,gBAAgB,WAAW"}
|
package/dist/JinaReader.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { Context, Duration, Effect, Layer, Schema } from "effect";
|
|
3
3
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
package/dist/JinaReader.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JinaReader.mjs","names":[],"sources":["../src/JinaReader.ts"],"sourcesContent":["import { Context, Duration, Effect, Layer, type Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonReadRequest,\n type ReadResponse,\n WebRead,\n type WebReadService,\n} from \"@effect-uai/core/WebRead\"\nimport type { JinaEngine } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Jina-typed read request. Widens {@link CommonReadRequest} with Jina\n * Reader's own header knobs; the common fields map onto Reader headers (see\n * the codec below).\n */\nexport type JinaReadRequest = CommonReadRequest & {\n /** Fetching engine - see {@link JinaEngine}. */\n readonly engine?: JinaEngine\n /** CSS selector to extract instead of the whole page (`X-Target-Selector`). */\n readonly targetSelector?: string\n /** Bypass Jina's page cache (`X-No-Cache`). */\n readonly noCache?: boolean\n /** Append a deduplicated link list, populating {@link ReadResponse.links}. */\n readonly withLinksSummary?: boolean\n}\n\nexport type JinaReaderService = {\n readonly read: (request: JinaReadRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Jina-typed reads (its own\n * Reader headers); yield the generic `WebRead` tag for provider-portable\n * code. Both are registered by {@link layer}.\n */\nexport class JinaReader extends Context.Service<JinaReader, JinaReaderService>()(\n \"@betalyra/effect-uai/providers/jina/JinaReader\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request headers\n// ---------------------------------------------------------------------------\n\n// Reader is header-driven: the URL is the path, everything else is a header.\n// `markdown` / `html` map one-to-one onto `X-Return-Format`.\nconst buildHeaders = (request: JinaReadRequest): Record<string, string> => {\n const format = request.format ?? \"markdown\"\n return {\n Accept: \"application/json\",\n \"X-Return-Format\": format,\n ...(request.timeout !== undefined && {\n // `X-Timeout` is whole seconds, capped at 180 by Jina.\n \"X-Timeout\": String(Math.max(1, Math.round(Duration.toMillis(request.timeout) / 1000))),\n }),\n ...(request.engine !== undefined && { \"X-Engine\": request.engine }),\n ...(request.targetSelector !== undefined && { \"X-Target-Selector\": request.targetSelector }),\n ...(request.noCache === true && { \"X-No-Cache\": \"true\" }),\n ...(request.withLinksSummary === true && { \"X-With-Links-Summary\": \"true\" }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireData = Schema.Struct({\n title: Schema.optional(Schema.NullOr(Schema.String)),\n url: Schema.optional(Schema.NullOr(Schema.String)),\n // `content` carries markdown/text; `html` carries html. Exactly one is set,\n // depending on the requested format.\n content: Schema.optional(Schema.NullOr(Schema.String)),\n html: Schema.optional(Schema.NullOr(Schema.String)),\n // Present only with `X-With-Links-Summary`; a { anchorText: url } record.\n links: Schema.optional(Schema.Unknown),\n})\n\nconst WireResponse = Schema.Struct({\n code: Schema.optional(Schema.Number),\n status: Schema.optional(Schema.Number),\n data: WireData,\n})\ntype WireResponse = typeof WireResponse.Type\n\n// Jina reports links as a { text: url } record; take the URLs. Tolerate an\n// array shape too, and omit when empty.\nconst extractLinks = (links: unknown): ReadonlyArray<string> | undefined => {\n const values = Array.isArray(links)\n ? links\n : links !== null && typeof links === \"object\"\n ? Object.values(links as Record<string, unknown>)\n : []\n const urls = values.filter((v): v is string => typeof v === \"string\")\n return urls.length > 0 ? urls : undefined\n}\n\nconst toResponse = (wire: WireResponse, request: JinaReadRequest): ReadResponse => {\n const { data } = wire\n const format = request.format ?? \"markdown\"\n const content = (format === \"html\" ? data.html : data.content) ?? \"\"\n const title = data.title ?? undefined\n const links = extractLinks(data.links)\n return {\n url: data.url ?? request.url,\n content,\n ...(title !== undefined && { title }),\n ...(links !== undefined && { links }),\n raw: wire,\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"jina\", raw: cause })\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 >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://r.jina.ai\"\n\nconst getRead = (\n cfg: Config,\n request: JinaReadRequest,\n): Effect.Effect<WireResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n // The target URL is appended as the path: `https://r.jina.ai/<url>`.\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/${request.url}`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.setHeaders(buildHeaders(request)),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst readImpl =\n (cfg: Config) =>\n (request: JinaReadRequest): Effect.Effect<ReadResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.map(getRead(cfg, request), (wire) => toResponse(wire, request))\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `JinaReaderService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (cfg: Config): Effect.Effect<JinaReaderService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n read: (request) =>\n readImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer registering both the provider-typed `JinaReader` tag and the generic\n * `WebRead` tag over one implementation. A `CommonReadRequest` is\n * structurally a `JinaReadRequest` with the extra knobs unset, so the generic\n * registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<JinaReader | WebRead, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(JinaReader, make(cfg))\n const generic = Layer.effect(\n WebRead,\n Effect.map(make(cfg), (s): WebReadService => ({ read: (request) => s.read(request) })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"JinaReader.mjs","names":[],"sources":["../src/JinaReader.ts"],"sourcesContent":["import { Context, Duration, Effect, Layer, type Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonReadRequest,\n type ReadResponse,\n WebRead,\n type WebReadService,\n} from \"@effect-uai/core/WebRead\"\nimport type { JinaEngine } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Jina-typed read request. Widens {@link CommonReadRequest} with Jina\n * Reader's own header knobs; the common fields map onto Reader headers (see\n * the codec below).\n */\nexport type JinaReadRequest = CommonReadRequest & {\n /** Fetching engine - see {@link JinaEngine}. */\n readonly engine?: JinaEngine\n /** CSS selector to extract instead of the whole page (`X-Target-Selector`). */\n readonly targetSelector?: string\n /** Bypass Jina's page cache (`X-No-Cache`). */\n readonly noCache?: boolean\n /** Append a deduplicated link list, populating {@link ReadResponse.links}. */\n readonly withLinksSummary?: boolean\n}\n\nexport type JinaReaderService = {\n readonly read: (request: JinaReadRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Jina-typed reads (its own\n * Reader headers); yield the generic `WebRead` tag for provider-portable\n * code. Both are registered by {@link layer}.\n */\nexport class JinaReader extends Context.Service<JinaReader, JinaReaderService>()(\n \"@betalyra/effect-uai/providers/jina/JinaReader\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request headers\n// ---------------------------------------------------------------------------\n\n// Reader is header-driven: the URL is the path, everything else is a header.\n// `markdown` / `html` map one-to-one onto `X-Return-Format`.\nconst buildHeaders = (request: JinaReadRequest): Record<string, string> => {\n const format = request.format ?? \"markdown\"\n return {\n Accept: \"application/json\",\n \"X-Return-Format\": format,\n ...(request.timeout !== undefined && {\n // `X-Timeout` is whole seconds, capped at 180 by Jina.\n \"X-Timeout\": String(Math.max(1, Math.round(Duration.toMillis(request.timeout) / 1000))),\n }),\n ...(request.engine !== undefined && { \"X-Engine\": request.engine }),\n ...(request.targetSelector !== undefined && { \"X-Target-Selector\": request.targetSelector }),\n ...(request.noCache === true && { \"X-No-Cache\": \"true\" }),\n ...(request.withLinksSummary === true && { \"X-With-Links-Summary\": \"true\" }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireData = Schema.Struct({\n title: Schema.optional(Schema.NullOr(Schema.String)),\n url: Schema.optional(Schema.NullOr(Schema.String)),\n // `content` carries markdown/text; `html` carries html. Exactly one is set,\n // depending on the requested format.\n content: Schema.optional(Schema.NullOr(Schema.String)),\n html: Schema.optional(Schema.NullOr(Schema.String)),\n // Present only with `X-With-Links-Summary`; a { anchorText: url } record.\n links: Schema.optional(Schema.Unknown),\n})\n\nconst WireResponse = Schema.Struct({\n code: Schema.optional(Schema.Number),\n status: Schema.optional(Schema.Number),\n data: WireData,\n})\ntype WireResponse = typeof WireResponse.Type\n\n// Jina reports links as a { text: url } record; take the URLs. Tolerate an\n// array shape too, and omit when empty.\nconst extractLinks = (links: unknown): ReadonlyArray<string> | undefined => {\n const values = Array.isArray(links)\n ? links\n : links !== null && typeof links === \"object\"\n ? Object.values(links as Record<string, unknown>)\n : []\n const urls = values.filter((v): v is string => typeof v === \"string\")\n return urls.length > 0 ? urls : undefined\n}\n\nconst toResponse = (wire: WireResponse, request: JinaReadRequest): ReadResponse => {\n const { data } = wire\n const format = request.format ?? \"markdown\"\n const content = (format === \"html\" ? data.html : data.content) ?? \"\"\n const title = data.title ?? undefined\n const links = extractLinks(data.links)\n return {\n url: data.url ?? request.url,\n content,\n ...(title !== undefined && { title }),\n ...(links !== undefined && { links }),\n raw: wire,\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"jina\", raw: cause })\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 >= 500) return new AiError.Unavailable({ provider, status, raw })\n return new AiError.InvalidRequest({ provider, raw })\n}\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://r.jina.ai\"\n\nconst getRead = (\n cfg: Config,\n request: JinaReadRequest,\n): Effect.Effect<WireResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n // The target URL is appended as the path: `https://r.jina.ai/<url>`.\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/${request.url}`).pipe(\n HttpClientRequest.bearerToken(cfg.apiKey),\n HttpClientRequest.setHeaders(buildHeaders(request)),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst readImpl =\n (cfg: Config) =>\n (request: JinaReadRequest): Effect.Effect<ReadResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.map(getRead(cfg, request), (wire) => toResponse(wire, request))\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `JinaReaderService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (cfg: Config): Effect.Effect<JinaReaderService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n read: (request) =>\n readImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer registering both the provider-typed `JinaReader` tag and the generic\n * `WebRead` tag over one implementation. A `CommonReadRequest` is\n * structurally a `JinaReadRequest` with the extra knobs unset, so the generic\n * registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<JinaReader | WebRead, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(JinaReader, make(cfg))\n const generic = Layer.effect(\n WebRead,\n Effect.map(make(cfg), (s): WebReadService => ({ read: (request) => s.read(request) })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { JinaRerankerModel } from "./models.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Redacted } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
5
|
+
import { CommonRerankRequest, RerankResponse, Reranker } from "@effect-uai/core/Reranker";
|
|
6
|
+
import { ImageSource } from "@effect-uai/core/Image";
|
|
7
|
+
declare namespace JinaReranker_d_exports {
|
|
8
|
+
export { Config, JinaRerankDocument, JinaRerankRequest, JinaReranker, JinaRerankerService, layer, make };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A candidate document. Text models take strings; `jina-reranker-m0` also
|
|
12
|
+
* takes `{ text }` / `{ image }`. One entry is one modality, and image
|
|
13
|
+
* *queries* are local-inference only, so `query` stays a string.
|
|
14
|
+
*/
|
|
15
|
+
type JinaRerankDocument = string | {
|
|
16
|
+
readonly text: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly image: ImageSource;
|
|
19
|
+
};
|
|
20
|
+
/** Widens {@link CommonRerankRequest} with Jina's multimodal documents. */
|
|
21
|
+
type JinaRerankRequest = Omit<CommonRerankRequest, "model" | "documents"> & {
|
|
22
|
+
readonly model: JinaRerankerModel;
|
|
23
|
+
readonly documents: ReadonlyArray<JinaRerankDocument>;
|
|
24
|
+
};
|
|
25
|
+
type JinaRerankerService = {
|
|
26
|
+
readonly rerank: (request: JinaRerankRequest) => Effect.Effect<RerankResponse, AiError.AiError>;
|
|
27
|
+
};
|
|
28
|
+
declare const JinaReranker_base: Context.ServiceClass<JinaReranker, "@betalyra/effect-uai/providers/jina/JinaReranker", JinaRerankerService>;
|
|
29
|
+
/**
|
|
30
|
+
* Provider-typed service tag. Yield this for multimodal documents; yield the
|
|
31
|
+
* generic `Reranker` tag for provider-portable code. Both are registered by
|
|
32
|
+
* {@link layer}.
|
|
33
|
+
*/
|
|
34
|
+
declare class JinaReranker extends JinaReranker_base {}
|
|
35
|
+
type Config = {
|
|
36
|
+
readonly apiKey: Redacted.Redacted;
|
|
37
|
+
readonly baseUrl?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build a `JinaRerankerService` value. For Layer-based setup, prefer
|
|
41
|
+
* {@link layer}.
|
|
42
|
+
*/
|
|
43
|
+
declare const make: (cfg: Config) => Effect.Effect<JinaRerankerService, never, HttpClient.HttpClient>;
|
|
44
|
+
/**
|
|
45
|
+
* Layer registering both the provider-typed `JinaReranker` tag and the generic
|
|
46
|
+
* `Reranker` tag over one implementation. A `CommonRerankRequest` is
|
|
47
|
+
* structurally a `JinaRerankRequest` with string documents, so the generic
|
|
48
|
+
* registration forwards directly.
|
|
49
|
+
*/
|
|
50
|
+
declare const layer: (cfg: Config) => Layer.Layer<JinaReranker | Reranker, never, HttpClient.HttpClient>;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { Config, JinaRerankDocument, JinaRerankRequest, JinaReranker, JinaRerankerService, layer, make, JinaReranker_d_exports as t };
|
|
53
|
+
//# sourceMappingURL=JinaReranker.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JinaReranker.d.mts","names":[],"sources":["../src/JinaReranker.ts"],"mappings":";;;;;;;;;;;;;;KAsBY;WAEG;;WACA,OAAO;;;KAGV,oBAAoB,KAAK;WAC1B,OAAO;WACP,WAAW,cAAc;;KAGxB;WACD,SAAS,SAAS,sBAAsB,OAAO,OAAO,gBAAgB,QAAQ;;;;;;;;cAQ5E,qBAAqB;KAItB;WACD,QAAQ,SAAS;WACjB;;;;;;cAmIE,OAAI,KACV,WACJ,OAAO,OAAO,4BAA4B,WAAW;;;;;;;cAY3C,QAAK,KACX,WACJ,MAAM,MAAM,eAAe,iBAAiB,WAAW"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Context, Effect, Encoding, Layer, Match, Schema } from "effect";
|
|
3
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
5
|
+
import { Reranker } from "@effect-uai/core/Reranker";
|
|
6
|
+
//#region src/JinaReranker.ts
|
|
7
|
+
var JinaReranker_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
JinaReranker: () => JinaReranker,
|
|
9
|
+
layer: () => layer,
|
|
10
|
+
make: () => make
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Provider-typed service tag. Yield this for multimodal documents; yield the
|
|
14
|
+
* generic `Reranker` tag for provider-portable code. Both are registered by
|
|
15
|
+
* {@link layer}.
|
|
16
|
+
*/
|
|
17
|
+
var JinaReranker = class extends Context.Service()("@betalyra/effect-uai/providers/jina/JinaReranker") {};
|
|
18
|
+
const imageSourceToWire = Match.type().pipe(Match.tag("url", (s) => ({ image: s.url })), Match.tag("base64", (s) => ({ image: s.base64 })), Match.tag("bytes", (s) => ({ image: Encoding.encodeBase64(s.bytes) })), Match.exhaustive);
|
|
19
|
+
const documentToWire = Match.type().pipe(Match.when(Match.string, (s) => s), Match.when({ text: Match.string }, ({ text }) => ({ text })), Match.when({ image: Match.any }, ({ image }) => imageSourceToWire(image)), Match.exhaustive);
|
|
20
|
+
const buildBody = (request) => ({
|
|
21
|
+
model: request.model,
|
|
22
|
+
query: request.query,
|
|
23
|
+
documents: request.documents.map(documentToWire),
|
|
24
|
+
...request.topN !== void 0 && { top_n: request.topN }
|
|
25
|
+
});
|
|
26
|
+
const WireResult = Schema.Struct({
|
|
27
|
+
index: Schema.Number,
|
|
28
|
+
relevance_score: Schema.Number
|
|
29
|
+
});
|
|
30
|
+
const WireUsage = Schema.Struct({ total_tokens: Schema.optional(Schema.Number) });
|
|
31
|
+
const WireResponse = Schema.Struct({
|
|
32
|
+
model: Schema.optional(Schema.String),
|
|
33
|
+
usage: Schema.optional(WireUsage),
|
|
34
|
+
results: Schema.Array(WireResult)
|
|
35
|
+
});
|
|
36
|
+
const usageOf = (usage) => usage?.total_tokens !== void 0 ? { totalTokens: usage.total_tokens } : {};
|
|
37
|
+
const toResponse = (wire) => ({
|
|
38
|
+
results: [...wire.results].sort((a, b) => b.relevance_score - a.relevance_score).map((r) => ({
|
|
39
|
+
index: r.index,
|
|
40
|
+
score: r.relevance_score
|
|
41
|
+
})),
|
|
42
|
+
usage: usageOf(wire.usage)
|
|
43
|
+
});
|
|
44
|
+
const transportFailure = (cause) => new AiError.Unavailable({
|
|
45
|
+
provider: "jina",
|
|
46
|
+
raw: cause
|
|
47
|
+
});
|
|
48
|
+
const httpStatusError = (status, body) => {
|
|
49
|
+
const provider = "jina";
|
|
50
|
+
const raw = body;
|
|
51
|
+
if (status === 429) return new AiError.RateLimited({
|
|
52
|
+
provider,
|
|
53
|
+
raw
|
|
54
|
+
});
|
|
55
|
+
if (status === 408 || status === 504) return new AiError.Timeout({
|
|
56
|
+
provider,
|
|
57
|
+
raw
|
|
58
|
+
});
|
|
59
|
+
if (status === 401) return new AiError.AuthFailed({
|
|
60
|
+
provider,
|
|
61
|
+
subtype: "auth",
|
|
62
|
+
raw
|
|
63
|
+
});
|
|
64
|
+
if (status === 403) return new AiError.AuthFailed({
|
|
65
|
+
provider,
|
|
66
|
+
subtype: "permission",
|
|
67
|
+
raw
|
|
68
|
+
});
|
|
69
|
+
if (status === 402) return new AiError.AuthFailed({
|
|
70
|
+
provider,
|
|
71
|
+
subtype: "billing",
|
|
72
|
+
raw
|
|
73
|
+
});
|
|
74
|
+
if (status === 413) return new AiError.ContextLengthExceeded({
|
|
75
|
+
provider,
|
|
76
|
+
raw
|
|
77
|
+
});
|
|
78
|
+
if (status >= 500) return new AiError.Unavailable({
|
|
79
|
+
provider,
|
|
80
|
+
status,
|
|
81
|
+
raw
|
|
82
|
+
});
|
|
83
|
+
return new AiError.InvalidRequest({
|
|
84
|
+
provider,
|
|
85
|
+
raw
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
const baseUrl = (cfg) => cfg.baseUrl ?? "https://api.jina.ai/v1";
|
|
89
|
+
const postRerank = (cfg, body) => Effect.gen(function* () {
|
|
90
|
+
const client = yield* HttpClient.HttpClient;
|
|
91
|
+
const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/rerank`).pipe(HttpClientRequest.bearerToken(cfg.apiKey), HttpClientRequest.bodyJsonUnsafe(body));
|
|
92
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
|
|
93
|
+
if (response.status >= 400) {
|
|
94
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
95
|
+
return yield* httpStatusError(response.status, text);
|
|
96
|
+
}
|
|
97
|
+
const json = yield* response.json.pipe(Effect.mapError(transportFailure));
|
|
98
|
+
return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(Effect.mapError(transportFailure));
|
|
99
|
+
});
|
|
100
|
+
const rerankImpl = (cfg) => (request) => Effect.map(postRerank(cfg, buildBody(request)), toResponse);
|
|
101
|
+
/**
|
|
102
|
+
* Build a `JinaRerankerService` value. For Layer-based setup, prefer
|
|
103
|
+
* {@link layer}.
|
|
104
|
+
*/
|
|
105
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient, (client) => ({ rerank: (request) => rerankImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)) }));
|
|
106
|
+
/**
|
|
107
|
+
* Layer registering both the provider-typed `JinaReranker` tag and the generic
|
|
108
|
+
* `Reranker` tag over one implementation. A `CommonRerankRequest` is
|
|
109
|
+
* structurally a `JinaRerankRequest` with string documents, so the generic
|
|
110
|
+
* registration forwards directly.
|
|
111
|
+
*/
|
|
112
|
+
const layer = (cfg) => {
|
|
113
|
+
const typed = Layer.effect(JinaReranker, make(cfg));
|
|
114
|
+
const generic = Layer.effect(Reranker, Effect.map(make(cfg), (s) => ({ rerank: (request) => s.rerank(request) })));
|
|
115
|
+
return Layer.merge(typed, generic);
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
export { JinaReranker, layer, make, JinaReranker_exports as t };
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=JinaReranker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JinaReranker.mjs","names":[],"sources":["../src/JinaReranker.ts"],"sourcesContent":["import { Context, Effect, Encoding, Layer, Match, type Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { ImageSource } from \"@effect-uai/core/Image\"\nimport {\n type CommonRerankRequest,\n Reranker,\n type RerankerService,\n type RerankResponse,\n type RerankUsage,\n} from \"@effect-uai/core/Reranker\"\nimport type { JinaRerankerModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A candidate document. Text models take strings; `jina-reranker-m0` also\n * takes `{ text }` / `{ image }`. One entry is one modality, and image\n * *queries* are local-inference only, so `query` stays a string.\n */\nexport type JinaRerankDocument =\n | string\n | { readonly text: string }\n | { readonly image: ImageSource }\n\n/** Widens {@link CommonRerankRequest} with Jina's multimodal documents. */\nexport type JinaRerankRequest = Omit<CommonRerankRequest, \"model\" | \"documents\"> & {\n readonly model: JinaRerankerModel\n readonly documents: ReadonlyArray<JinaRerankDocument>\n}\n\nexport type JinaRerankerService = {\n readonly rerank: (request: JinaRerankRequest) => Effect.Effect<RerankResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for multimodal documents; yield the\n * generic `Reranker` tag for provider-portable code. Both are registered by\n * {@link layer}.\n */\nexport class JinaReranker extends Context.Service<JinaReranker, JinaRerankerService>()(\n \"@betalyra/effect-uai/providers/jina/JinaReranker\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireDocument = string | { readonly text: string } | { readonly image: string }\n\n// Jina detects URL vs base64 from the value itself, so both go in `image`.\nconst imageSourceToWire: (s: ImageSource) => WireDocument = Match.type<ImageSource>().pipe(\n Match.tag(\"url\", (s): WireDocument => ({ image: s.url })),\n Match.tag(\"base64\", (s): WireDocument => ({ image: s.base64 })),\n Match.tag(\"bytes\", (s): WireDocument => ({ image: Encoding.encodeBase64(s.bytes) })),\n Match.exhaustive,\n)\n\nconst documentToWire: (doc: JinaRerankDocument) => WireDocument =\n Match.type<JinaRerankDocument>().pipe(\n Match.when(Match.string, (s): WireDocument => s),\n Match.when({ text: Match.string }, ({ text }): WireDocument => ({ text })),\n Match.when({ image: Match.any }, ({ image }) => imageSourceToWire(image)),\n Match.exhaustive,\n )\n\ntype WireBody = {\n readonly model: string\n readonly query: string\n readonly documents: ReadonlyArray<WireDocument>\n readonly top_n?: number\n}\n\n// No `return_documents`: the caller holds `documents` and `results[].index`\n// points back into it.\nconst buildBody = (request: JinaRerankRequest): WireBody => ({\n model: request.model,\n query: request.query,\n documents: request.documents.map(documentToWire),\n ...(request.topN !== undefined && { top_n: request.topN }),\n})\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n index: Schema.Number,\n relevance_score: Schema.Number,\n})\n\nconst WireUsage = Schema.Struct({\n total_tokens: Schema.optional(Schema.Number),\n})\ntype WireUsage = typeof WireUsage.Type\n\nconst WireResponse = Schema.Struct({\n model: Schema.optional(Schema.String),\n usage: Schema.optional(WireUsage),\n results: Schema.Array(WireResult),\n})\ntype WireResponse = typeof WireResponse.Type\n\nconst usageOf = (usage: WireUsage | undefined): RerankUsage =>\n usage?.total_tokens !== undefined ? { totalTokens: usage.total_tokens } : {}\n\n// Sort rather than trust wire order: the score contract promises descending.\nconst toResponse = (wire: WireResponse): RerankResponse => ({\n results: [...wire.results]\n .sort((a, b) => b.relevance_score - a.relevance_score)\n .map((r) => ({ index: r.index, score: r.relevance_score })),\n usage: usageOf(wire.usage),\n})\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"jina\", raw: cause })\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\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.jina.ai/v1\"\n\nconst postRerank = (\n cfg: Config,\n body: WireBody,\n): Effect.Effect<WireResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/rerank`).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\nconst rerankImpl =\n (cfg: Config) =>\n (\n request: JinaRerankRequest,\n ): Effect.Effect<RerankResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.map(postRerank(cfg, buildBody(request)), toResponse)\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `JinaRerankerService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<JinaRerankerService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n rerank: (request) =>\n rerankImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer registering both the provider-typed `JinaReranker` tag and the generic\n * `Reranker` tag over one implementation. A `CommonRerankRequest` is\n * structurally a `JinaRerankRequest` with string documents, so the generic\n * registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<JinaReranker | Reranker, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(JinaReranker, make(cfg))\n const generic = Layer.effect(\n Reranker,\n Effect.map(make(cfg), (s): RerankerService => ({ rerank: (request) => s.rerank(request) })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { JinaEmbeddingModel, JinaEngine } from "./models.mjs";
|
|
1
|
+
import { JinaEmbeddingModel, JinaEngine, JinaRerankerModel } from "./models.mjs";
|
|
2
2
|
import { t as JinaEmbedding_d_exports } from "./JinaEmbedding.mjs";
|
|
3
3
|
import { t as JinaReader_d_exports } from "./JinaReader.mjs";
|
|
4
|
-
|
|
4
|
+
import { t as JinaReranker_d_exports } from "./JinaReranker.mjs";
|
|
5
|
+
export { JinaEmbedding_d_exports as JinaEmbedding, JinaEmbeddingModel, JinaEngine, JinaReader_d_exports as JinaReader, JinaReranker_d_exports as JinaReranker, JinaRerankerModel };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as JinaEmbedding_exports } from "./JinaEmbedding.mjs";
|
|
2
2
|
import { t as JinaReader_exports } from "./JinaReader.mjs";
|
|
3
|
+
import { t as JinaReranker_exports } from "./JinaReranker.mjs";
|
|
3
4
|
import "./models.mjs";
|
|
4
|
-
export { JinaEmbedding_exports as JinaEmbedding, JinaReader_exports as JinaReader };
|
|
5
|
+
export { JinaEmbedding_exports as JinaEmbedding, JinaReader_exports as JinaReader, JinaReranker_exports as JinaReranker };
|
package/dist/models.d.mts
CHANGED
|
@@ -19,6 +19,17 @@
|
|
|
19
19
|
* Reference: https://jina.ai/embeddings/
|
|
20
20
|
*/
|
|
21
21
|
type JinaEmbeddingModel = "jina-embeddings-v4" | "jina-embeddings-v5-text-small" | "jina-embeddings-v5-text-nano" | "jina-embeddings-v3" | "jina-clip-v2" | (string & {});
|
|
22
|
+
/**
|
|
23
|
+
* Known Jina reranker model identifiers.
|
|
24
|
+
*
|
|
25
|
+
* - `jina-reranker-v3.5`: flagship listwise reranker, 131k context.
|
|
26
|
+
* - `jina-reranker-v3`: previous generation, schema-identical.
|
|
27
|
+
* - `jina-reranker-m0`: multimodal, takes `{ text }` / `{ image }`
|
|
28
|
+
* documents (see `JinaRerankRequest`).
|
|
29
|
+
*
|
|
30
|
+
* Reference: https://jina.ai/reranker/
|
|
31
|
+
*/
|
|
32
|
+
type JinaRerankerModel = "jina-reranker-v3.5" | "jina-reranker-v3" | "jina-reranker-m0" | (string & {});
|
|
22
33
|
/**
|
|
23
34
|
* Reader fetching engine (`X-Engine`). `browser` renders JavaScript,
|
|
24
35
|
* `curl` is a fast static fetch, `auto` picks per page. Provider-specific,
|
|
@@ -26,5 +37,5 @@ type JinaEmbeddingModel = "jina-embeddings-v4" | "jina-embeddings-v5-text-small"
|
|
|
26
37
|
*/
|
|
27
38
|
type JinaEngine = "browser" | "curl" | "auto" | (string & {});
|
|
28
39
|
//#endregion
|
|
29
|
-
export { JinaEmbeddingModel, JinaEngine };
|
|
40
|
+
export { JinaEmbeddingModel, JinaEngine, JinaRerankerModel };
|
|
30
41
|
//# sourceMappingURL=models.d.mts.map
|
package/dist/models.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;KAmBY;;;;;;;;;;;KAmBA;;;;;;KAYA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/jina",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Jina AI provider for @effect-uai/core (embeddings, rerank, web read).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -42,19 +42,23 @@
|
|
|
42
42
|
"./JinaReader": {
|
|
43
43
|
"types": "./dist/JinaReader.d.mts",
|
|
44
44
|
"import": "./dist/JinaReader.mjs"
|
|
45
|
+
},
|
|
46
|
+
"./JinaReranker": {
|
|
47
|
+
"types": "./dist/JinaReranker.d.mts",
|
|
48
|
+
"import": "./dist/JinaReranker.mjs"
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
"publishConfig": {
|
|
48
52
|
"access": "public"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
51
|
-
"effect": "4.0.0-
|
|
52
|
-
"typescript": "^
|
|
53
|
-
"@effect-uai/core": "0.
|
|
55
|
+
"effect": "4.0.0-rc.111",
|
|
56
|
+
"typescript": "^7.0.2",
|
|
57
|
+
"@effect-uai/core": "0.13.0"
|
|
54
58
|
},
|
|
55
59
|
"peerDependencies": {
|
|
56
60
|
"@effect-uai/core": ">=0.2.0 <1",
|
|
57
|
-
"effect": ">=4.0.0-
|
|
61
|
+
"effect": ">=4.0.0-rc.111 <5.0.0"
|
|
58
62
|
},
|
|
59
63
|
"scripts": {
|
|
60
64
|
"build": "tsdown",
|
package/src/JinaEmbedding.ts
CHANGED
|
@@ -154,25 +154,19 @@ const multiPartContentRejected: AiError.AiError = new AiError.Unsupported({
|
|
|
154
154
|
*/
|
|
155
155
|
const inputToItem: (input: EmbedInput) => Effect.Effect<WireItem, AiError.AiError> =
|
|
156
156
|
Match.type<EmbedInput>().pipe(
|
|
157
|
-
Match.when(
|
|
158
|
-
|
|
159
|
-
(s): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text: s }),
|
|
157
|
+
Match.when(Match.string, (s): Effect.Effect<WireItem, AiError.AiError> =>
|
|
158
|
+
Effect.succeed({ text: s }),
|
|
160
159
|
),
|
|
161
|
-
Match.when(
|
|
162
|
-
{ text
|
|
163
|
-
({ text }): Effect.Effect<WireItem, AiError.AiError> => Effect.succeed({ text }),
|
|
160
|
+
Match.when({ text: Match.string }, ({ text }): Effect.Effect<WireItem, AiError.AiError> =>
|
|
161
|
+
Effect.succeed({ text }),
|
|
164
162
|
),
|
|
165
|
-
Match.when(
|
|
166
|
-
|
|
167
|
-
({ image }): Effect.Effect<WireItem, AiError.AiError> =>
|
|
168
|
-
Effect.succeed(imageSourceToItem(image)),
|
|
163
|
+
Match.when({ image: Match.any }, ({ image }): Effect.Effect<WireItem, AiError.AiError> =>
|
|
164
|
+
Effect.succeed(imageSourceToItem(image)),
|
|
169
165
|
),
|
|
170
|
-
Match.when(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
? Effect.succeed(contentPartToItem(content[0]!))
|
|
175
|
-
: Effect.fail(multiPartContentRejected),
|
|
166
|
+
Match.when({ content: Match.any }, ({ content }): Effect.Effect<WireItem, AiError.AiError> =>
|
|
167
|
+
content.length === 1
|
|
168
|
+
? Effect.succeed(contentPartToItem(content[0]!))
|
|
169
|
+
: Effect.fail(multiPartContentRejected),
|
|
176
170
|
),
|
|
177
171
|
Match.exhaustive,
|
|
178
172
|
)
|
|
@@ -311,17 +305,14 @@ const decodeBase64Embedding = (
|
|
|
311
305
|
onFailure: (cause) => Effect.fail(transportFailure(cause)),
|
|
312
306
|
onSuccess: (bytes) =>
|
|
313
307
|
Match.value(encoding).pipe(
|
|
314
|
-
Match.when(
|
|
315
|
-
"binary",
|
|
316
|
-
(): Effect.Effect<Embedding, AiError.AiError> =>
|
|
317
|
-
Effect.succeed({ _tag: "binary", vector: bytes }),
|
|
308
|
+
Match.when("binary", (): Effect.Effect<Embedding, AiError.AiError> =>
|
|
309
|
+
Effect.succeed({ _tag: "binary", vector: bytes }),
|
|
318
310
|
),
|
|
319
|
-
Match.orElse(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}),
|
|
311
|
+
Match.orElse((): Effect.Effect<Embedding, AiError.AiError> =>
|
|
312
|
+
Effect.succeed({
|
|
313
|
+
_tag: "float32",
|
|
314
|
+
vector: new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4),
|
|
315
|
+
}),
|
|
325
316
|
),
|
|
326
317
|
),
|
|
327
318
|
})
|
|
@@ -444,9 +435,10 @@ const embedImpl =
|
|
|
444
435
|
return Effect.fail(transportFailure("Jina returned empty `data` array"))
|
|
445
436
|
}
|
|
446
437
|
return payloadToEmbedding(first, request.encoding).pipe(
|
|
447
|
-
Effect.map(
|
|
448
|
-
|
|
449
|
-
|
|
438
|
+
Effect.map((embedding): AnyEmbedResponse => ({
|
|
439
|
+
embedding,
|
|
440
|
+
usage: usageOf(decoded.usage),
|
|
441
|
+
})),
|
|
450
442
|
)
|
|
451
443
|
}),
|
|
452
444
|
)
|
|
@@ -471,12 +463,10 @@ const embedManyImpl =
|
|
|
471
463
|
),
|
|
472
464
|
Effect.flatMap((decoded) =>
|
|
473
465
|
orderedEmbeddings(decoded.data, request.encoding).pipe(
|
|
474
|
-
Effect.map(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}),
|
|
479
|
-
),
|
|
466
|
+
Effect.map((embeddings): AnyEmbedManyResponse => ({
|
|
467
|
+
embeddings,
|
|
468
|
+
usage: usageOf(decoded.usage),
|
|
469
|
+
})),
|
|
480
470
|
),
|
|
481
471
|
),
|
|
482
472
|
)
|
|
@@ -532,44 +522,41 @@ export const layer = (
|
|
|
532
522
|
const typed = Layer.effect(JinaEmbedding, make(cfg))
|
|
533
523
|
const generic = Layer.effect(
|
|
534
524
|
EmbeddingModel,
|
|
535
|
-
Effect.map(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
},
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
},
|
|
571
|
-
}),
|
|
572
|
-
),
|
|
525
|
+
Effect.map(make(cfg), (s): EmbeddingModelService => ({
|
|
526
|
+
// Jina emits dense float32 / binary (binary is bit-quantized, packed
|
|
527
|
+
// into int8 bytes on the wire - NOT scalar int8 per dimension; sparse /
|
|
528
|
+
// multivector live on the provider-typed JinaEncoding, off the
|
|
529
|
+
// cross-provider set). Reject a scalar `int8` encoding (bucket 1) so it
|
|
530
|
+
// isn't returned as a mislabeled vector, matching OpenAI / Gemini.
|
|
531
|
+
embed: <E extends EmbedEncoding | undefined = undefined>(
|
|
532
|
+
req: Omit<CommonEmbedRequest, "encoding"> & { readonly encoding?: E },
|
|
533
|
+
) => {
|
|
534
|
+
const task = mapGenericTask(req.task)
|
|
535
|
+
return assertEncoding(req.encoding, ["float32", "binary"], "jina").pipe(
|
|
536
|
+
Effect.andThen(
|
|
537
|
+
s.embed({
|
|
538
|
+
...req,
|
|
539
|
+
model: req.model as JinaEmbeddingModel,
|
|
540
|
+
...(task !== undefined && { task }),
|
|
541
|
+
} as JinaEmbedRequest),
|
|
542
|
+
),
|
|
543
|
+
) as Effect.Effect<EmbedResponse<E>, AiError.AiError>
|
|
544
|
+
},
|
|
545
|
+
embedMany: <E extends EmbedEncoding | undefined = undefined>(
|
|
546
|
+
req: Omit<CommonEmbedManyRequest, "encoding"> & { readonly encoding?: E },
|
|
547
|
+
) => {
|
|
548
|
+
const task = mapGenericTask(req.task)
|
|
549
|
+
return assertEncoding(req.encoding, ["float32", "binary"], "jina").pipe(
|
|
550
|
+
Effect.andThen(
|
|
551
|
+
s.embedMany({
|
|
552
|
+
...req,
|
|
553
|
+
model: req.model as JinaEmbeddingModel,
|
|
554
|
+
...(task !== undefined && { task }),
|
|
555
|
+
} as JinaEmbedManyRequest),
|
|
556
|
+
),
|
|
557
|
+
) as Effect.Effect<EmbedManyResponse<E>, AiError.AiError>
|
|
558
|
+
},
|
|
559
|
+
})),
|
|
573
560
|
)
|
|
574
561
|
return Layer.merge(typed, generic)
|
|
575
562
|
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Context, Effect, Encoding, Layer, Match, type Redacted, Schema } from "effect"
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
|
3
|
+
import * as AiError from "@effect-uai/core/AiError"
|
|
4
|
+
import type { ImageSource } from "@effect-uai/core/Image"
|
|
5
|
+
import {
|
|
6
|
+
type CommonRerankRequest,
|
|
7
|
+
Reranker,
|
|
8
|
+
type RerankerService,
|
|
9
|
+
type RerankResponse,
|
|
10
|
+
type RerankUsage,
|
|
11
|
+
} from "@effect-uai/core/Reranker"
|
|
12
|
+
import type { JinaRerankerModel } from "./models.js"
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Public types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A candidate document. Text models take strings; `jina-reranker-m0` also
|
|
20
|
+
* takes `{ text }` / `{ image }`. One entry is one modality, and image
|
|
21
|
+
* *queries* are local-inference only, so `query` stays a string.
|
|
22
|
+
*/
|
|
23
|
+
export type JinaRerankDocument =
|
|
24
|
+
| string
|
|
25
|
+
| { readonly text: string }
|
|
26
|
+
| { readonly image: ImageSource }
|
|
27
|
+
|
|
28
|
+
/** Widens {@link CommonRerankRequest} with Jina's multimodal documents. */
|
|
29
|
+
export type JinaRerankRequest = Omit<CommonRerankRequest, "model" | "documents"> & {
|
|
30
|
+
readonly model: JinaRerankerModel
|
|
31
|
+
readonly documents: ReadonlyArray<JinaRerankDocument>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type JinaRerankerService = {
|
|
35
|
+
readonly rerank: (request: JinaRerankRequest) => Effect.Effect<RerankResponse, AiError.AiError>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Provider-typed service tag. Yield this for multimodal documents; yield the
|
|
40
|
+
* generic `Reranker` tag for provider-portable code. Both are registered by
|
|
41
|
+
* {@link layer}.
|
|
42
|
+
*/
|
|
43
|
+
export class JinaReranker extends Context.Service<JinaReranker, JinaRerankerService>()(
|
|
44
|
+
"@betalyra/effect-uai/providers/jina/JinaReranker",
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
export type Config = {
|
|
48
|
+
readonly apiKey: Redacted.Redacted
|
|
49
|
+
readonly baseUrl?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Codec - request body
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
type WireDocument = string | { readonly text: string } | { readonly image: string }
|
|
57
|
+
|
|
58
|
+
// Jina detects URL vs base64 from the value itself, so both go in `image`.
|
|
59
|
+
const imageSourceToWire: (s: ImageSource) => WireDocument = Match.type<ImageSource>().pipe(
|
|
60
|
+
Match.tag("url", (s): WireDocument => ({ image: s.url })),
|
|
61
|
+
Match.tag("base64", (s): WireDocument => ({ image: s.base64 })),
|
|
62
|
+
Match.tag("bytes", (s): WireDocument => ({ image: Encoding.encodeBase64(s.bytes) })),
|
|
63
|
+
Match.exhaustive,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const documentToWire: (doc: JinaRerankDocument) => WireDocument =
|
|
67
|
+
Match.type<JinaRerankDocument>().pipe(
|
|
68
|
+
Match.when(Match.string, (s): WireDocument => s),
|
|
69
|
+
Match.when({ text: Match.string }, ({ text }): WireDocument => ({ text })),
|
|
70
|
+
Match.when({ image: Match.any }, ({ image }) => imageSourceToWire(image)),
|
|
71
|
+
Match.exhaustive,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
type WireBody = {
|
|
75
|
+
readonly model: string
|
|
76
|
+
readonly query: string
|
|
77
|
+
readonly documents: ReadonlyArray<WireDocument>
|
|
78
|
+
readonly top_n?: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// No `return_documents`: the caller holds `documents` and `results[].index`
|
|
82
|
+
// points back into it.
|
|
83
|
+
const buildBody = (request: JinaRerankRequest): WireBody => ({
|
|
84
|
+
model: request.model,
|
|
85
|
+
query: request.query,
|
|
86
|
+
documents: request.documents.map(documentToWire),
|
|
87
|
+
...(request.topN !== undefined && { top_n: request.topN }),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Codec - response
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const WireResult = Schema.Struct({
|
|
95
|
+
index: Schema.Number,
|
|
96
|
+
relevance_score: Schema.Number,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const WireUsage = Schema.Struct({
|
|
100
|
+
total_tokens: Schema.optional(Schema.Number),
|
|
101
|
+
})
|
|
102
|
+
type WireUsage = typeof WireUsage.Type
|
|
103
|
+
|
|
104
|
+
const WireResponse = Schema.Struct({
|
|
105
|
+
model: Schema.optional(Schema.String),
|
|
106
|
+
usage: Schema.optional(WireUsage),
|
|
107
|
+
results: Schema.Array(WireResult),
|
|
108
|
+
})
|
|
109
|
+
type WireResponse = typeof WireResponse.Type
|
|
110
|
+
|
|
111
|
+
const usageOf = (usage: WireUsage | undefined): RerankUsage =>
|
|
112
|
+
usage?.total_tokens !== undefined ? { totalTokens: usage.total_tokens } : {}
|
|
113
|
+
|
|
114
|
+
// Sort rather than trust wire order: the score contract promises descending.
|
|
115
|
+
const toResponse = (wire: WireResponse): RerankResponse => ({
|
|
116
|
+
results: [...wire.results]
|
|
117
|
+
.sort((a, b) => b.relevance_score - a.relevance_score)
|
|
118
|
+
.map((r) => ({ index: r.index, score: r.relevance_score })),
|
|
119
|
+
usage: usageOf(wire.usage),
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// HTTP
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
const transportFailure = (cause: unknown): AiError.AiError =>
|
|
127
|
+
new AiError.Unavailable({ provider: "jina", raw: cause })
|
|
128
|
+
|
|
129
|
+
const httpStatusError = (status: number, body: string): AiError.AiError => {
|
|
130
|
+
const provider = "jina"
|
|
131
|
+
const raw = body
|
|
132
|
+
if (status === 429) return new AiError.RateLimited({ provider, raw })
|
|
133
|
+
if (status === 408 || status === 504) return new AiError.Timeout({ provider, raw })
|
|
134
|
+
if (status === 401) return new AiError.AuthFailed({ provider, subtype: "auth", raw })
|
|
135
|
+
if (status === 403) return new AiError.AuthFailed({ provider, subtype: "permission", raw })
|
|
136
|
+
if (status === 402) return new AiError.AuthFailed({ provider, subtype: "billing", raw })
|
|
137
|
+
if (status === 413) return new AiError.ContextLengthExceeded({ provider, raw })
|
|
138
|
+
if (status >= 500) return new AiError.Unavailable({ provider, status, raw })
|
|
139
|
+
return new AiError.InvalidRequest({ provider, raw })
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const baseUrl = (cfg: Config): string => cfg.baseUrl ?? "https://api.jina.ai/v1"
|
|
143
|
+
|
|
144
|
+
const postRerank = (
|
|
145
|
+
cfg: Config,
|
|
146
|
+
body: WireBody,
|
|
147
|
+
): Effect.Effect<WireResponse, AiError.AiError, HttpClient.HttpClient> =>
|
|
148
|
+
Effect.gen(function* () {
|
|
149
|
+
const client = yield* HttpClient.HttpClient
|
|
150
|
+
const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/rerank`).pipe(
|
|
151
|
+
HttpClientRequest.bearerToken(cfg.apiKey),
|
|
152
|
+
HttpClientRequest.bodyJsonUnsafe(body),
|
|
153
|
+
)
|
|
154
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))
|
|
155
|
+
if (response.status >= 400) {
|
|
156
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""))
|
|
157
|
+
return yield* httpStatusError(response.status, text)
|
|
158
|
+
}
|
|
159
|
+
const json = yield* response.json.pipe(Effect.mapError(transportFailure))
|
|
160
|
+
return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(
|
|
161
|
+
Effect.mapError(transportFailure),
|
|
162
|
+
)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const rerankImpl =
|
|
166
|
+
(cfg: Config) =>
|
|
167
|
+
(
|
|
168
|
+
request: JinaRerankRequest,
|
|
169
|
+
): Effect.Effect<RerankResponse, AiError.AiError, HttpClient.HttpClient> =>
|
|
170
|
+
Effect.map(postRerank(cfg, buildBody(request)), toResponse)
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Constructors
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build a `JinaRerankerService` value. For Layer-based setup, prefer
|
|
178
|
+
* {@link layer}.
|
|
179
|
+
*/
|
|
180
|
+
export const make = (
|
|
181
|
+
cfg: Config,
|
|
182
|
+
): Effect.Effect<JinaRerankerService, never, HttpClient.HttpClient> =>
|
|
183
|
+
Effect.map(HttpClient.HttpClient, (client) => ({
|
|
184
|
+
rerank: (request) =>
|
|
185
|
+
rerankImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
186
|
+
}))
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Layer registering both the provider-typed `JinaReranker` tag and the generic
|
|
190
|
+
* `Reranker` tag over one implementation. A `CommonRerankRequest` is
|
|
191
|
+
* structurally a `JinaRerankRequest` with string documents, so the generic
|
|
192
|
+
* registration forwards directly.
|
|
193
|
+
*/
|
|
194
|
+
export const layer = (
|
|
195
|
+
cfg: Config,
|
|
196
|
+
): Layer.Layer<JinaReranker | Reranker, never, HttpClient.HttpClient> => {
|
|
197
|
+
const typed = Layer.effect(JinaReranker, make(cfg))
|
|
198
|
+
const generic = Layer.effect(
|
|
199
|
+
Reranker,
|
|
200
|
+
Effect.map(make(cfg), (s): RerankerService => ({ rerank: (request) => s.rerank(request) })),
|
|
201
|
+
)
|
|
202
|
+
return Layer.merge(typed, generic)
|
|
203
|
+
}
|
package/src/index.ts
CHANGED
package/src/models.ts
CHANGED
|
@@ -26,6 +26,23 @@ export type JinaEmbeddingModel =
|
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
27
27
|
| (string & {})
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Known Jina reranker model identifiers.
|
|
31
|
+
*
|
|
32
|
+
* - `jina-reranker-v3.5`: flagship listwise reranker, 131k context.
|
|
33
|
+
* - `jina-reranker-v3`: previous generation, schema-identical.
|
|
34
|
+
* - `jina-reranker-m0`: multimodal, takes `{ text }` / `{ image }`
|
|
35
|
+
* documents (see `JinaRerankRequest`).
|
|
36
|
+
*
|
|
37
|
+
* Reference: https://jina.ai/reranker/
|
|
38
|
+
*/
|
|
39
|
+
export type JinaRerankerModel =
|
|
40
|
+
| "jina-reranker-v3.5"
|
|
41
|
+
| "jina-reranker-v3"
|
|
42
|
+
| "jina-reranker-m0"
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
44
|
+
| (string & {})
|
|
45
|
+
|
|
29
46
|
/**
|
|
30
47
|
* Reader fetching engine (`X-Engine`). `browser` renders JavaScript,
|
|
31
48
|
* `curl` is a fast static fetch, `auto` picks per page. Provider-specific,
|
|
File without changes
|