@effect-uai/exa 0.17.0 → 0.19.0-dev-20261109183959
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/ExaContents.d.mts +1 -1
- package/dist/ExaContents.mjs +1 -1
- package/dist/ExaContents.mjs.map +1 -1
- package/dist/ExaSearch.d.mts +1 -1
- package/dist/ExaSearch.mjs +1 -1
- package/dist/ExaSearch.mjs.map +1 -1
- package/package.json +5 -5
- package/src/ExaContents.ts +1 -1
- package/src/ExaSearch.ts +1 -1
package/dist/ExaContents.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExaLivecrawl } from "./models.mjs";
|
|
2
2
|
import { Context, Effect, Layer, Redacted } from "effect";
|
|
3
|
-
import { HttpClient } from "effect/
|
|
3
|
+
import { HttpClient } from "effect/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import { CommonReadRequest, ReadResponse, WebRead } from "@effect-uai/core/WebRead";
|
|
6
6
|
declare namespace ExaContents_d_exports {
|
package/dist/ExaContents.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { Context, Effect, Layer, Redacted, Schema } from "effect";
|
|
3
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
3
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import { WebRead } from "@effect-uai/core/WebRead";
|
|
6
6
|
//#region src/ExaContents.ts
|
package/dist/ExaContents.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExaContents.mjs","names":[],"sources":["../src/ExaContents.ts"],"sourcesContent":["import { Context, Effect, Layer, 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 { ExaLivecrawl } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Exa-typed read request. Widens {@link CommonReadRequest} with Exa\n * `/contents` knobs; the common fields map onto Exa's wire body (see the\n * codec below).\n */\nexport type ExaContentsRequest = CommonReadRequest & {\n /** Freshness / cache policy - see {@link ExaLivecrawl}. */\n readonly livecrawl?: ExaLivecrawl\n /** Cap on the returned content length (Exa `text.maxCharacters`). */\n readonly maxCharacters?: number\n}\n\nexport type ExaContentsService = {\n readonly read: (request: ExaContentsRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Exa-typed reads (its own\n * `/contents` knobs); yield the generic `WebRead` tag for provider-portable\n * code. Both are registered by {@link layer}.\n */\nexport class ExaContents extends Context.Service<ExaContents, ExaContentsService>()(\n \"@betalyra/effect-uai/providers/exa/ExaContents\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireText = {\n readonly maxCharacters?: number\n readonly includeHtmlTags?: boolean\n}\n\ntype WireBody = {\n readonly urls: ReadonlyArray<string>\n readonly text: WireText\n readonly livecrawl?: string\n}\n\n// Exa returns markdown by default; `includeHtmlTags: true` returns HTML tags\n// instead. Both land on the single `text` field of the result.\nconst buildBody = (request: ExaContentsRequest): WireBody => {\n const text: WireText = {\n ...(request.maxCharacters !== undefined && { maxCharacters: request.maxCharacters }),\n ...(request.format === \"html\" && { includeHtmlTags: true }),\n }\n return {\n urls: [request.url],\n text,\n ...(request.livecrawl !== undefined && { livecrawl: request.livecrawl }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n id: Schema.optional(Schema.String),\n url: Schema.String,\n title: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireStatus = Schema.Struct({\n id: Schema.optional(Schema.String),\n status: Schema.optional(Schema.String),\n source: Schema.optional(Schema.String),\n})\n\nconst WireResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n results: Schema.Array(WireResult),\n statuses: Schema.optional(Schema.Array(WireStatus)),\n})\ntype WireResponse = typeof WireResponse.Type\n\nconst toResponse = (wire: WireResponse, result: WireResult): ReadResponse => {\n const title = result.title ?? undefined\n return {\n url: result.url,\n content: result.text ?? \"\",\n ...(title !== undefined && { title }),\n raw: wire,\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"exa\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"exa\"\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://api.exa.ai\"\n\nconst postContents = (\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)}/contents`).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst readImpl =\n (cfg: Config) =>\n (\n request: ExaContentsRequest,\n ): Effect.Effect<ReadResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const wire = yield* postContents(cfg, buildBody(request))\n // Exa reports per-URL crawl failures in `results` / `statuses` rather\n // than an HTTP error; a missing first result means the single URL could\n // not be read.\n const result = wire.results[0]\n if (result === undefined) {\n return yield* new AiError.Unavailable({ provider: \"exa\", raw: wire })\n }\n return toResponse(wire, result)\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `ExaContentsService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<ExaContentsService, 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 `ExaContents` tag and the generic\n * `WebRead` tag over one implementation. A `CommonReadRequest` is structurally\n * an `ExaContentsRequest` with the extra knobs unset, so the generic\n * registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<ExaContents | WebRead, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(ExaContents, 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":"ExaContents.mjs","names":[],"sources":["../src/ExaContents.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/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 { ExaLivecrawl } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Exa-typed read request. Widens {@link CommonReadRequest} with Exa\n * `/contents` knobs; the common fields map onto Exa's wire body (see the\n * codec below).\n */\nexport type ExaContentsRequest = CommonReadRequest & {\n /** Freshness / cache policy - see {@link ExaLivecrawl}. */\n readonly livecrawl?: ExaLivecrawl\n /** Cap on the returned content length (Exa `text.maxCharacters`). */\n readonly maxCharacters?: number\n}\n\nexport type ExaContentsService = {\n readonly read: (request: ExaContentsRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Exa-typed reads (its own\n * `/contents` knobs); yield the generic `WebRead` tag for provider-portable\n * code. Both are registered by {@link layer}.\n */\nexport class ExaContents extends Context.Service<ExaContents, ExaContentsService>()(\n \"@betalyra/effect-uai/providers/exa/ExaContents\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireText = {\n readonly maxCharacters?: number\n readonly includeHtmlTags?: boolean\n}\n\ntype WireBody = {\n readonly urls: ReadonlyArray<string>\n readonly text: WireText\n readonly livecrawl?: string\n}\n\n// Exa returns markdown by default; `includeHtmlTags: true` returns HTML tags\n// instead. Both land on the single `text` field of the result.\nconst buildBody = (request: ExaContentsRequest): WireBody => {\n const text: WireText = {\n ...(request.maxCharacters !== undefined && { maxCharacters: request.maxCharacters }),\n ...(request.format === \"html\" && { includeHtmlTags: true }),\n }\n return {\n urls: [request.url],\n text,\n ...(request.livecrawl !== undefined && { livecrawl: request.livecrawl }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n id: Schema.optional(Schema.String),\n url: Schema.String,\n title: Schema.optional(Schema.NullOr(Schema.String)),\n text: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireStatus = Schema.Struct({\n id: Schema.optional(Schema.String),\n status: Schema.optional(Schema.String),\n source: Schema.optional(Schema.String),\n})\n\nconst WireResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n results: Schema.Array(WireResult),\n statuses: Schema.optional(Schema.Array(WireStatus)),\n})\ntype WireResponse = typeof WireResponse.Type\n\nconst toResponse = (wire: WireResponse, result: WireResult): ReadResponse => {\n const title = result.title ?? undefined\n return {\n url: result.url,\n content: result.text ?? \"\",\n ...(title !== undefined && { title }),\n raw: wire,\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"exa\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"exa\"\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://api.exa.ai\"\n\nconst postContents = (\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)}/contents`).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst readImpl =\n (cfg: Config) =>\n (\n request: ExaContentsRequest,\n ): Effect.Effect<ReadResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const wire = yield* postContents(cfg, buildBody(request))\n // Exa reports per-URL crawl failures in `results` / `statuses` rather\n // than an HTTP error; a missing first result means the single URL could\n // not be read.\n const result = wire.results[0]\n if (result === undefined) {\n return yield* new AiError.Unavailable({ provider: \"exa\", raw: wire })\n }\n return toResponse(wire, result)\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `ExaContentsService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<ExaContentsService, 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 `ExaContents` tag and the generic\n * `WebRead` tag over one implementation. A `CommonReadRequest` is structurally\n * an `ExaContentsRequest` with the extra knobs unset, so the generic\n * registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<ExaContents | WebRead, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(ExaContents, 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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"}
|
package/dist/ExaSearch.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ExaCategory, ExaSearchType } from "./models.mjs";
|
|
2
2
|
import { Context, Effect, Layer, Redacted } from "effect";
|
|
3
|
-
import { HttpClient } from "effect/
|
|
3
|
+
import { HttpClient } from "effect/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import { CommonSearchRequest, SearchResponse, WebSearch } from "@effect-uai/core/WebSearch";
|
|
6
6
|
declare namespace ExaSearch_d_exports {
|
package/dist/ExaSearch.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { Context, DateTime, Effect, Layer, Option, Redacted, Schema } from "effect";
|
|
3
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
3
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
5
5
|
import * as Capabilities from "@effect-uai/core/Capabilities";
|
|
6
6
|
import { WebSearch } from "@effect-uai/core/WebSearch";
|
package/dist/ExaSearch.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExaSearch.mjs","names":[],"sources":["../src/ExaSearch.ts"],"sourcesContent":["import { Context, DateTime, type Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport {\n type CommonSearchRequest,\n type SearchRecency,\n type SearchResponse,\n type SearchResult,\n WebSearch,\n type WebSearchService,\n} from \"@effect-uai/core/WebSearch\"\nimport type { ExaCategory, ExaSearchType } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Exa-typed search request. Widens {@link CommonSearchRequest} with Exa's\n * own knobs; the common fields map onto Exa's wire filters (see the codec\n * below). There is no `model` - `/search` ranks the web directly, with\n * `type` choosing the retrieval strategy.\n */\nexport type ExaSearchRequest = CommonSearchRequest & {\n /** Retrieval strategy - see {@link ExaSearchType}. Defaults to `auto`. */\n readonly type?: ExaSearchType\n /** Content-vertical filter - see {@link ExaCategory}. */\n readonly category?: ExaCategory\n}\n\nexport type ExaSearchService = {\n readonly search: (request: ExaSearchRequest) => Effect.Effect<SearchResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Exa-typed search (`type`,\n * `category`, the per-result `score`); yield the generic `WebSearch` tag\n * for provider-portable code. Both are registered by {@link layer}.\n */\nexport class ExaSearch extends Context.Service<ExaSearch, ExaSearchService>()(\n \"@betalyra/effect-uai/providers/exa/ExaSearch\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireBody = {\n readonly query: string\n readonly numResults?: number\n readonly type?: string\n readonly category?: string\n readonly includeDomains?: ReadonlyArray<string>\n readonly excludeDomains?: ReadonlyArray<string>\n readonly startPublishedDate?: string\n readonly endPublishedDate?: string\n readonly userLocation?: string\n}\n\n// Exa has no recency enum; map the coarse enum onto a published-after date\n// computed from \"now\" (per the capabilities policy: lossless translation).\nconst recencyDuration: Record<SearchRecency, Duration.Input> = {\n hour: \"1 hour\",\n day: \"1 day\",\n week: \"7 days\",\n month: \"30 days\",\n year: \"365 days\",\n}\n\nconst buildBody = (request: ExaSearchRequest, now: DateTime.DateTime): WireBody => {\n // Explicit `startDate` wins over the `recency` sugar; otherwise derive the\n // published-after bound from the recency window.\n const startPublishedDate =\n request.startDate !== undefined\n ? DateTime.formatIso(request.startDate)\n : request.recency !== undefined\n ? DateTime.formatIso(DateTime.subtractDuration(now, recencyDuration[request.recency]))\n : undefined\n\n return {\n query: request.query,\n ...(request.maxResults !== undefined && { numResults: request.maxResults }),\n ...(request.type !== undefined && { type: request.type }),\n ...(request.category !== undefined && { category: request.category }),\n ...(request.includeDomains !== undefined && { includeDomains: request.includeDomains }),\n ...(request.excludeDomains !== undefined && { excludeDomains: request.excludeDomains }),\n ...(startPublishedDate !== undefined && { startPublishedDate }),\n ...(request.endDate !== undefined && { endPublishedDate: DateTime.formatIso(request.endDate) }),\n ...(request.country !== undefined && { userLocation: request.country }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n id: Schema.optional(Schema.String),\n url: Schema.String,\n title: Schema.optional(Schema.NullOr(Schema.String)),\n score: Schema.optional(Schema.Number),\n publishedDate: Schema.optional(Schema.NullOr(Schema.String)),\n author: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireCost = Schema.Struct({\n total: Schema.optional(Schema.Number),\n})\n\nconst WireResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n results: Schema.Array(WireResult),\n costDollars: Schema.optional(WireCost),\n})\ntype WireResponse = typeof WireResponse.Type\n\n/** Lenient parse of Exa's ISO date strings; unparseable -> omitted. */\nconst parseDate = (s: string | null | undefined): DateTime.DateTime | undefined =>\n s == null ? undefined : Option.getOrUndefined(DateTime.make(s))\n\n// Exa's pure `/search` returns no text excerpt unless you request `contents`\n// (the out-of-scope extract feature), so `snippet` stays undefined here -\n// the honest result is url + title + score + publishedDate. `author` and\n// the full `raw` object remain on `raw` for typed-tag callers.\nconst toResult = (r: WireResult): SearchResult => {\n const publishedDate = parseDate(r.publishedDate)\n return {\n url: r.url,\n ...(r.title != null && { title: r.title }),\n ...(r.score !== undefined && { score: r.score }),\n ...(publishedDate !== undefined && { publishedDate }),\n raw: r,\n }\n}\n\n// `costDollars` is still decoded (so it survives on `raw`) but not promoted\n// to a typed cost field: search cost/usage is deferred to the unified usage\n// design (see plans/usage-tracking.md).\nconst toResponse = (wire: WireResponse): SearchResponse => ({\n results: wire.results.map(toResult),\n raw: wire,\n})\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"exa\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"exa\"\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://api.exa.ai\"\n\nconst postSearch = (\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)}/search`).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst searchImpl =\n (cfg: Config) =>\n (\n request: ExaSearchRequest,\n ): Effect.Effect<SearchResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n // Exa has no language filter (bucket 2: warn, don't fail).\n yield* Capabilities.warnDroppedWhen(request.language, {\n provider: \"exa\",\n capability: \"language\",\n field: \"language\",\n reason: \"Exa /search has no language filter; the hint is ignored.\",\n })\n const now = yield* DateTime.now\n const wire = yield* postSearch(cfg, buildBody(request, now))\n return toResponse(wire)\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `ExaSearchService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (cfg: Config): Effect.Effect<ExaSearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n search: (request) =>\n searchImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer registering both the provider-typed `ExaSearch` tag and the\n * generic `WebSearch` tag over one implementation. A `CommonSearchRequest`\n * is structurally an `ExaSearchRequest` with `type` / `category` unset, so\n * the generic registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<ExaSearch | WebSearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(ExaSearch, make(cfg))\n const generic = Layer.effect(\n WebSearch,\n Effect.map(make(cfg), (s): WebSearchService => ({ search: (request) => s.search(request) })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExaSearch.mjs","names":[],"sources":["../src/ExaSearch.ts"],"sourcesContent":["import { Context, DateTime, type Duration, Effect, Layer, Option, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport {\n type CommonSearchRequest,\n type SearchRecency,\n type SearchResponse,\n type SearchResult,\n WebSearch,\n type WebSearchService,\n} from \"@effect-uai/core/WebSearch\"\nimport type { ExaCategory, ExaSearchType } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Exa-typed search request. Widens {@link CommonSearchRequest} with Exa's\n * own knobs; the common fields map onto Exa's wire filters (see the codec\n * below). There is no `model` - `/search` ranks the web directly, with\n * `type` choosing the retrieval strategy.\n */\nexport type ExaSearchRequest = CommonSearchRequest & {\n /** Retrieval strategy - see {@link ExaSearchType}. Defaults to `auto`. */\n readonly type?: ExaSearchType\n /** Content-vertical filter - see {@link ExaCategory}. */\n readonly category?: ExaCategory\n}\n\nexport type ExaSearchService = {\n readonly search: (request: ExaSearchRequest) => Effect.Effect<SearchResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Exa-typed search (`type`,\n * `category`, the per-result `score`); yield the generic `WebSearch` tag\n * for provider-portable code. Both are registered by {@link layer}.\n */\nexport class ExaSearch extends Context.Service<ExaSearch, ExaSearchService>()(\n \"@betalyra/effect-uai/providers/exa/ExaSearch\",\n) {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n}\n\n// ---------------------------------------------------------------------------\n// Codec - request body\n// ---------------------------------------------------------------------------\n\ntype WireBody = {\n readonly query: string\n readonly numResults?: number\n readonly type?: string\n readonly category?: string\n readonly includeDomains?: ReadonlyArray<string>\n readonly excludeDomains?: ReadonlyArray<string>\n readonly startPublishedDate?: string\n readonly endPublishedDate?: string\n readonly userLocation?: string\n}\n\n// Exa has no recency enum; map the coarse enum onto a published-after date\n// computed from \"now\" (per the capabilities policy: lossless translation).\nconst recencyDuration: Record<SearchRecency, Duration.Input> = {\n hour: \"1 hour\",\n day: \"1 day\",\n week: \"7 days\",\n month: \"30 days\",\n year: \"365 days\",\n}\n\nconst buildBody = (request: ExaSearchRequest, now: DateTime.DateTime): WireBody => {\n // Explicit `startDate` wins over the `recency` sugar; otherwise derive the\n // published-after bound from the recency window.\n const startPublishedDate =\n request.startDate !== undefined\n ? DateTime.formatIso(request.startDate)\n : request.recency !== undefined\n ? DateTime.formatIso(DateTime.subtractDuration(now, recencyDuration[request.recency]))\n : undefined\n\n return {\n query: request.query,\n ...(request.maxResults !== undefined && { numResults: request.maxResults }),\n ...(request.type !== undefined && { type: request.type }),\n ...(request.category !== undefined && { category: request.category }),\n ...(request.includeDomains !== undefined && { includeDomains: request.includeDomains }),\n ...(request.excludeDomains !== undefined && { excludeDomains: request.excludeDomains }),\n ...(startPublishedDate !== undefined && { startPublishedDate }),\n ...(request.endDate !== undefined && { endPublishedDate: DateTime.formatIso(request.endDate) }),\n ...(request.country !== undefined && { userLocation: request.country }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n id: Schema.optional(Schema.String),\n url: Schema.String,\n title: Schema.optional(Schema.NullOr(Schema.String)),\n score: Schema.optional(Schema.Number),\n publishedDate: Schema.optional(Schema.NullOr(Schema.String)),\n author: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireCost = Schema.Struct({\n total: Schema.optional(Schema.Number),\n})\n\nconst WireResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n results: Schema.Array(WireResult),\n costDollars: Schema.optional(WireCost),\n})\ntype WireResponse = typeof WireResponse.Type\n\n/** Lenient parse of Exa's ISO date strings; unparseable -> omitted. */\nconst parseDate = (s: string | null | undefined): DateTime.DateTime | undefined =>\n s == null ? undefined : Option.getOrUndefined(DateTime.make(s))\n\n// Exa's pure `/search` returns no text excerpt unless you request `contents`\n// (the out-of-scope extract feature), so `snippet` stays undefined here -\n// the honest result is url + title + score + publishedDate. `author` and\n// the full `raw` object remain on `raw` for typed-tag callers.\nconst toResult = (r: WireResult): SearchResult => {\n const publishedDate = parseDate(r.publishedDate)\n return {\n url: r.url,\n ...(r.title != null && { title: r.title }),\n ...(r.score !== undefined && { score: r.score }),\n ...(publishedDate !== undefined && { publishedDate }),\n raw: r,\n }\n}\n\n// `costDollars` is still decoded (so it survives on `raw`) but not promoted\n// to a typed cost field: search cost/usage is deferred to the unified usage\n// design (see plans/usage-tracking.md).\nconst toResponse = (wire: WireResponse): SearchResponse => ({\n results: wire.results.map(toResult),\n raw: wire,\n})\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"exa\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"exa\"\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://api.exa.ai\"\n\nconst postSearch = (\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)}/search`).pipe(\n HttpClientRequest.setHeader(\"x-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* httpStatusError(response.status, text)\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n return yield* Schema.decodeUnknownEffect(WireResponse)(json).pipe(\n Effect.mapError(transportFailure),\n )\n })\n\nconst searchImpl =\n (cfg: Config) =>\n (\n request: ExaSearchRequest,\n ): Effect.Effect<SearchResponse, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n // Exa has no language filter (bucket 2: warn, don't fail).\n yield* Capabilities.warnDroppedWhen(request.language, {\n provider: \"exa\",\n capability: \"language\",\n field: \"language\",\n reason: \"Exa /search has no language filter; the hint is ignored.\",\n })\n const now = yield* DateTime.now\n const wire = yield* postSearch(cfg, buildBody(request, now))\n return toResponse(wire)\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build an `ExaSearchService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (cfg: Config): Effect.Effect<ExaSearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => ({\n search: (request) =>\n searchImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n }))\n\n/**\n * Layer registering both the provider-typed `ExaSearch` tag and the\n * generic `WebSearch` tag over one implementation. A `CommonSearchRequest`\n * is structurally an `ExaSearchRequest` with `type` / `category` unset, so\n * the generic registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<ExaSearch | WebSearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(ExaSearch, make(cfg))\n const generic = Layer.effect(\n WebSearch,\n Effect.map(make(cfg), (s): WebSearchService => ({ search: (request) => s.search(request) })),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/exa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0-dev-20261109183959",
|
|
4
4
|
"description": "Exa provider for @effect-uai/core (web search, web read).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"effect": "4.
|
|
51
|
+
"effect": "4.1.2",
|
|
52
52
|
"typescript": "^7.0.2",
|
|
53
|
-
"@effect-uai/core": "0.
|
|
53
|
+
"@effect-uai/core": "0.19.0-dev-20261109183959"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@effect-uai/core": "
|
|
57
|
-
"effect": ">=4.0.
|
|
56
|
+
"@effect-uai/core": "0.19.0-dev-20261109183959",
|
|
57
|
+
"effect": ">=4.0.1 <5.0.0"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsdown",
|
package/src/ExaContents.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Redacted, Schema } from "effect"
|
|
2
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http"
|
|
3
3
|
import * as AiError from "@effect-uai/core/AiError"
|
|
4
4
|
import {
|
|
5
5
|
type CommonReadRequest,
|
package/src/ExaSearch.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, DateTime, type Duration, Effect, Layer, Option, Redacted, Schema } from "effect"
|
|
2
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
2
|
+
import { HttpClient, HttpClientRequest } from "effect/http"
|
|
3
3
|
import * as AiError from "@effect-uai/core/AiError"
|
|
4
4
|
import * as Capabilities from "@effect-uai/core/Capabilities"
|
|
5
5
|
import {
|