@effect-uai/exa 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/ExaContents.d.mts +3 -3
- package/dist/ExaContents.d.mts.map +1 -1
- package/dist/ExaContents.mjs +1 -1
- package/dist/ExaContents.mjs.map +1 -1
- package/dist/ExaSearch.d.mts +3 -3
- package/dist/ExaSearch.d.mts.map +1 -1
- package/dist/ExaSearch.mjs +4 -2
- package/dist/ExaSearch.mjs.map +1 -1
- package/dist/models.d.mts.map +1 -1
- package/package.json +5 -5
- /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
package/dist/ExaContents.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/ExaContents.d.ts
|
|
8
6
|
declare namespace ExaContents_d_exports {
|
|
9
7
|
export { Config, ExaContents, ExaContentsRequest, ExaContentsService, layer, make };
|
|
10
8
|
}
|
|
@@ -14,7 +12,9 @@ declare namespace ExaContents_d_exports {
|
|
|
14
12
|
* codec below).
|
|
15
13
|
*/
|
|
16
14
|
type ExaContentsRequest = CommonReadRequest & {
|
|
17
|
-
/** Freshness / cache policy - see {@link ExaLivecrawl}. */
|
|
15
|
+
/** Freshness / cache policy - see {@link ExaLivecrawl}. */
|
|
16
|
+
readonly livecrawl?: ExaLivecrawl;
|
|
17
|
+
/** Cap on the returned content length (Exa `text.maxCharacters`). */
|
|
18
18
|
readonly maxCharacters?: number;
|
|
19
19
|
};
|
|
20
20
|
type ExaContentsService = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExaContents.d.mts","names":[],"sources":["../src/ExaContents.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExaContents.d.mts","names":[],"sources":["../src/ExaContents.ts"],"mappings":";;;;;;;;;;;;;KAoBY,qBAAqB;;WAEtB,YAAY;;WAEZ;;KAGC;WACD,OAAO,SAAS,uBAAuB,OAAO,OAAO,cAAc,QAAQ;;;;;;;;cAQzE,oBAAoB;KAIrB;WACD,QAAQ,SAAS;WACjB;;;;;;cAsIE,OAAI,KACV,WACJ,OAAO,OAAO,2BAA2B,WAAW;;;;;;;cAY1C,QAAK,KACX,WACJ,MAAM,MAAM,cAAc,gBAAgB,WAAW"}
|
package/dist/ExaContents.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, Layer, Redacted, Schema } from "effect";
|
|
3
3
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
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/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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"}
|
package/dist/ExaSearch.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 { CommonSearchRequest, SearchResponse, WebSearch } from "@effect-uai/core/WebSearch";
|
|
6
|
-
|
|
7
|
-
//#region src/ExaSearch.d.ts
|
|
8
6
|
declare namespace ExaSearch_d_exports {
|
|
9
7
|
export { Config, ExaSearch, ExaSearchRequest, ExaSearchService, layer, make };
|
|
10
8
|
}
|
|
@@ -15,7 +13,9 @@ declare namespace ExaSearch_d_exports {
|
|
|
15
13
|
* `type` choosing the retrieval strategy.
|
|
16
14
|
*/
|
|
17
15
|
type ExaSearchRequest = CommonSearchRequest & {
|
|
18
|
-
/** Retrieval strategy - see {@link ExaSearchType}. Defaults to `auto`. */
|
|
16
|
+
/** Retrieval strategy - see {@link ExaSearchType}. Defaults to `auto`. */
|
|
17
|
+
readonly type?: ExaSearchType;
|
|
18
|
+
/** Content-vertical filter - see {@link ExaCategory}. */
|
|
19
19
|
readonly category?: ExaCategory;
|
|
20
20
|
};
|
|
21
21
|
type ExaSearchService = {
|
package/dist/ExaSearch.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExaSearch.d.mts","names":[],"sources":["../src/ExaSearch.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExaSearch.d.mts","names":[],"sources":["../src/ExaSearch.ts"],"mappings":";;;;;;;;;;;;;;KAwBY,mBAAmB;;WAEpB,OAAO;;WAEP,WAAW;;KAGV;WACD,SAAS,SAAS,qBAAqB,OAAO,OAAO,gBAAgB,QAAQ;;;;;;;;cAQ3E,kBAAkB;KAInB;WACD,QAAQ,SAAS;WACjB;;;;;;cA4KE,OAAI,KAAS,WAAS,OAAO,OAAO,yBAAyB,WAAW;;;;;;;cAYxE,QAAK,KACX,WACJ,MAAM,MAAM,YAAY,kBAAkB,WAAW"}
|
package/dist/ExaSearch.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, DateTime, Effect, Layer, Option, Redacted, Schema } from "effect";
|
|
3
3
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError";
|
|
@@ -126,7 +126,9 @@ const searchImpl = (cfg) => (request) => Effect.gen(function* () {
|
|
|
126
126
|
field: "language",
|
|
127
127
|
reason: "Exa /search has no language filter; the hint is ignored."
|
|
128
128
|
});
|
|
129
|
-
|
|
129
|
+
const now = yield* DateTime.now;
|
|
130
|
+
const wire = yield* postSearch(cfg, buildBody(request, now));
|
|
131
|
+
return toResponse(wire);
|
|
130
132
|
});
|
|
131
133
|
/**
|
|
132
134
|
* Build an `ExaSearchService` value. For Layer-based setup, prefer
|
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/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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"}
|
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":";;;;;;;;;;;;;;;KAcY;;;;;KAgBA;;;;;;KAeA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/exa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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.0.0-
|
|
52
|
-
"typescript": "^
|
|
53
|
-
"@effect-uai/core": "0.
|
|
51
|
+
"effect": "4.0.0-rc.111",
|
|
52
|
+
"typescript": "^7.0.2",
|
|
53
|
+
"@effect-uai/core": "0.13.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@effect-uai/core": ">=0.2.0 <1",
|
|
57
|
-
"effect": ">=4.0.0-
|
|
57
|
+
"effect": ">=4.0.0-rc.111 <5.0.0"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsdown",
|
|
File without changes
|