@effect-uai/perplexity 0.16.0 → 0.18.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.
@@ -1,6 +1,6 @@
1
1
  import { PerplexityReasoningEffort, PerplexityResearchModel } from "./models.mjs";
2
2
  import { Context, Effect, Layer, Redacted } from "effect";
3
- import { HttpClient } from "effect/unstable/http";
3
+ import { HttpClient } from "effect/http";
4
4
  import { DeepResearch, DeepResearchServiceShape } from "@effect-uai/core/DeepResearch";
5
5
  import * as Job from "@effect-uai/core/Job";
6
6
  import { ResearchRequest } from "@effect-uai/core/Research";
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { httpStatusError, transportFailure } from "./http.mjs";
3
3
  import { Context, Effect, Layer, Match, Redacted, Schema } from "effect";
4
- import { HttpClient, HttpClientRequest } from "effect/unstable/http";
4
+ import { HttpClient, HttpClientRequest } from "effect/http";
5
5
  import * as AiError from "@effect-uai/core/AiError";
6
6
  import { DeepResearch, fromJob } from "@effect-uai/core/DeepResearch";
7
7
  import * as Job from "@effect-uai/core/Job";
@@ -1 +1 @@
1
- {"version":3,"file":"PerplexityDeepResearch.mjs","names":[],"sources":["../src/PerplexityDeepResearch.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport type { Annotation, ContentBlock, HistoryItem, Message } from \"@effect-uai/core/Items\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\nimport { httpStatusError, transportFailure } from \"./http.js\"\nimport type { PerplexityReasoningEffort, PerplexityResearchModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Perplexity-typed research request. Narrows `model` to the sonar research\n * models and adds `reasoningEffort` (a Perplexity knob that also steers how\n * many searches `sonar-deep-research` runs). Provider-portable code uses the\n * generic `DeepResearch` tag instead.\n */\nexport type PerplexityResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: PerplexityResearchModel\n readonly reasoningEffort?: PerplexityReasoningEffort\n}\n\nexport type PerplexityDeepResearchService = DeepResearchServiceShape<PerplexityResearchRequest>\n\n/**\n * Provider-typed tag. Yield this for the Perplexity model / `reasoningEffort`\n * knobs; yield the generic `DeepResearch` tag for provider-portable code. The\n * async job is poll-only, so it has no live event stream: `researchStream` /\n * `streamFrom` return the synthesized progress {@link fromJob} builds (a leading\n * search event, then the terminal report). `cancel` fails `Unsupported` (no\n * endpoint). A persisted ref survives a restart (the job is server-backed).\n */\nexport class PerplexityDeepResearch extends Context.Service<\n PerplexityDeepResearch,\n PerplexityDeepResearchService\n>()(\"@betalyra/effect-uai/providers/perplexity/PerplexityDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst WireSearchResult = Schema.Struct({\n title: Schema.String,\n url: Schema.String,\n snippet: Schema.optional(Schema.String),\n})\n\nconst WireChoice = Schema.Struct({\n message: Schema.optional(\n Schema.Struct({ content: Schema.optional(Schema.NullOr(Schema.String)) }),\n ),\n})\n\nconst WireUsage = Schema.Struct({\n prompt_tokens: Schema.optional(Schema.Number),\n completion_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n})\n\nconst WireCompletion = Schema.Struct({\n choices: Schema.Array(WireChoice),\n search_results: Schema.optional(Schema.Array(WireSearchResult)),\n citations: Schema.optional(Schema.Array(Schema.String)),\n usage: Schema.optional(WireUsage),\n})\ntype WireCompletion = typeof WireCompletion.Type\n\nconst WireJob = Schema.Struct({\n id: Schema.String,\n status: Schema.Literals([\"CREATED\", \"IN_PROGRESS\", \"COMPLETED\", \"FAILED\"]),\n error_message: Schema.optional(Schema.NullOr(Schema.String)),\n response: Schema.optional(Schema.NullOr(WireCompletion)),\n})\ntype WireJob = typeof WireJob.Type\n\nconst decodeJob = Schema.decodeUnknownEffect(WireJob)\n\n// Text of a content block; only the two text-bearing kinds contribute.\nconst blockText = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n output_text: (b) => b.text,\n input_image: () => \"\",\n output_image: () => \"\",\n refusal: () => \"\",\n }),\n)\n\nconst historyToMessages = (\n history: ReadonlyArray<HistoryItem>,\n): ReadonlyArray<{ readonly role: string; readonly content: string }> =>\n history\n .filter((i): i is Message => i.type === \"message\")\n .map((m) => ({ role: m.role, content: m.content.map(blockText).join(\"\") }))\n\n// Perplexity links answers to sources with positional `[n]` markers, so each\n// citation carries a 1-based `marker` (and `cited_text` from the snippet), the\n// provider-agnostic representation of that style. Prefer the richer\n// `search_results`; fall back to the legacy `citations` URL list.\nconst citationsOf = (wire: WireCompletion): ReadonlyArray<Annotation> =>\n wire.search_results !== undefined && wire.search_results.length > 0\n ? wire.search_results.map((r, i) => ({\n type: \"url_citation\" as const,\n url: r.url,\n title: r.title,\n marker: i + 1,\n ...(r.snippet !== undefined && r.snippet !== \"\" && { cited_text: r.snippet }),\n }))\n : (wire.citations ?? []).map((url, i) => ({\n type: \"url_citation\" as const,\n url,\n title: url,\n marker: i + 1,\n }))\n\nconst completionToTurn = (wire: WireCompletion): Turn => {\n const text = wire.choices[0]?.message?.content ?? \"\"\n const annotations = citationsOf(wire)\n return {\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text, ...(annotations.length > 0 && { annotations }) }],\n },\n ],\n usage: {\n ...(wire.usage?.prompt_tokens !== undefined && { input_tokens: wire.usage.prompt_tokens }),\n ...(wire.usage?.completion_tokens !== undefined && {\n output_tokens: wire.usage.completion_tokens,\n }),\n ...(wire.usage?.total_tokens !== undefined && { total_tokens: wire.usage.total_tokens }),\n },\n stop_reason: \"stop\",\n }\n}\n\n// Map Perplexity's four job states onto the generic `JobState`, carrying the\n// assembled `Turn` on `Succeeded` and the provider message on `Failed`.\nconst jobStateOf = (wire: WireJob): ResearchState =>\n Match.value(wire.status).pipe(\n Match.when(\"CREATED\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.when(\"IN_PROGRESS\", (): ResearchState => ({ _tag: \"Running\" })),\n Match.when(\"COMPLETED\", (): ResearchState =>\n wire.response != null\n ? { _tag: \"Succeeded\", result: completionToTurn(wire.response) }\n : { _tag: \"Failed\", reason: \"job completed with no response body\", raw: wire },\n ),\n Match.when(\"FAILED\", (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error_message != null && { reason: wire.error_message }),\n raw: wire,\n })),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.perplexity.ai\"\n\nconst authHeader = (cfg: Config) =>\n HttpClientRequest.setHeader(\"authorization\", `Bearer ${Redacted.value(cfg.apiKey)}`)\n\nconst submitBody = (request: PerplexityResearchRequest): Record<string, unknown> => ({\n request: {\n model: request.model ?? \"sonar-deep-research\",\n messages: historyToMessages(request.history),\n ...(request.reasoningEffort !== undefined && { reasoning_effort: request.reasoningEffort }),\n },\n})\n\nconst submitJob = (\n cfg: Config,\n request: PerplexityResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/v1/async/sonar`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe(submitBody(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 const wire = yield* decodeJob(json).pipe(Effect.mapError(transportFailure))\n return Job.jobRef<Turn>(\"perplexity\", wire.id)\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/v1/async/sonar/${ref.id}`).pipe(\n authHeader(cfg),\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 const wire = yield* decodeJob(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\n// Perplexity exposes no cancel endpoint and no event stream on the async job.\nconst unsupported = (capability: string): AiError.Unsupported =>\n new AiError.Unsupported({\n provider: \"perplexity\",\n capability,\n reason: \"Perplexity's async research job is poll-only and cannot be cancelled.\",\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n// The three job ops. Poll-only, so no `streamFrom`: `fromJob` synthesizes one.\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<PerplexityResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: () => Effect.fail(unsupported(\"cancel\")),\n }\n}\n\n/** Build a `PerplexityDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<PerplexityDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => fromJob(jobOps(cfg, client), cfg.job))\n\n/**\n * Layer registering the provider-typed `PerplexityDeepResearch` tag and the\n * generic `DeepResearch` tag. No capability markers: the whole surface (poll,\n * collect, synthesized stream) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<PerplexityDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(PerplexityDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n return fromJob<ResearchRequest>(\n {\n submit: (request) => ops.submit(request as PerplexityResearchRequest),\n poll: ops.poll,\n cancel: ops.cancel,\n },\n cfg.job,\n )\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PerplexityDeepResearch.mjs","names":[],"sources":["../src/PerplexityDeepResearch.ts"],"sourcesContent":["import { Context, Effect, Layer, Match, Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n DeepResearch,\n type DeepResearchService,\n type DeepResearchServiceShape,\n fromJob,\n type ResearchJobOps,\n} from \"@effect-uai/core/DeepResearch\"\nimport type { Annotation, ContentBlock, HistoryItem, Message } from \"@effect-uai/core/Items\"\nimport * as Job from \"@effect-uai/core/Job\"\nimport type { ResearchJobRef, ResearchRequest, ResearchState } from \"@effect-uai/core/Research\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\nimport { httpStatusError, transportFailure } from \"./http.js\"\nimport type { PerplexityReasoningEffort, PerplexityResearchModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Perplexity-typed research request. Narrows `model` to the sonar research\n * models and adds `reasoningEffort` (a Perplexity knob that also steers how\n * many searches `sonar-deep-research` runs). Provider-portable code uses the\n * generic `DeepResearch` tag instead.\n */\nexport type PerplexityResearchRequest = Omit<ResearchRequest, \"model\"> & {\n readonly model?: PerplexityResearchModel\n readonly reasoningEffort?: PerplexityReasoningEffort\n}\n\nexport type PerplexityDeepResearchService = DeepResearchServiceShape<PerplexityResearchRequest>\n\n/**\n * Provider-typed tag. Yield this for the Perplexity model / `reasoningEffort`\n * knobs; yield the generic `DeepResearch` tag for provider-portable code. The\n * async job is poll-only, so it has no live event stream: `researchStream` /\n * `streamFrom` return the synthesized progress {@link fromJob} builds (a leading\n * search event, then the terminal report). `cancel` fails `Unsupported` (no\n * endpoint). A persisted ref survives a restart (the job is server-backed).\n */\nexport class PerplexityDeepResearch extends Context.Service<\n PerplexityDeepResearch,\n PerplexityDeepResearchService\n>()(\"@betalyra/effect-uai/providers/perplexity/PerplexityDeepResearch\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n /** Poll cadence / overall timeout for the derived poll loops. */\n readonly job?: Job.JobConfig\n}\n\n// ---------------------------------------------------------------------------\n// Wire codec\n// ---------------------------------------------------------------------------\n\nconst WireSearchResult = Schema.Struct({\n title: Schema.String,\n url: Schema.String,\n snippet: Schema.optional(Schema.String),\n})\n\nconst WireChoice = Schema.Struct({\n message: Schema.optional(\n Schema.Struct({ content: Schema.optional(Schema.NullOr(Schema.String)) }),\n ),\n})\n\nconst WireUsage = Schema.Struct({\n prompt_tokens: Schema.optional(Schema.Number),\n completion_tokens: Schema.optional(Schema.Number),\n total_tokens: Schema.optional(Schema.Number),\n})\n\nconst WireCompletion = Schema.Struct({\n choices: Schema.Array(WireChoice),\n search_results: Schema.optional(Schema.Array(WireSearchResult)),\n citations: Schema.optional(Schema.Array(Schema.String)),\n usage: Schema.optional(WireUsage),\n})\ntype WireCompletion = typeof WireCompletion.Type\n\nconst WireJob = Schema.Struct({\n id: Schema.String,\n status: Schema.Literals([\"CREATED\", \"IN_PROGRESS\", \"COMPLETED\", \"FAILED\"]),\n error_message: Schema.optional(Schema.NullOr(Schema.String)),\n response: Schema.optional(Schema.NullOr(WireCompletion)),\n})\ntype WireJob = typeof WireJob.Type\n\nconst decodeJob = Schema.decodeUnknownEffect(WireJob)\n\n// Text of a content block; only the two text-bearing kinds contribute.\nconst blockText = Match.type<ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n output_text: (b) => b.text,\n input_image: () => \"\",\n output_image: () => \"\",\n refusal: () => \"\",\n }),\n)\n\nconst historyToMessages = (\n history: ReadonlyArray<HistoryItem>,\n): ReadonlyArray<{ readonly role: string; readonly content: string }> =>\n history\n .filter((i): i is Message => i.type === \"message\")\n .map((m) => ({ role: m.role, content: m.content.map(blockText).join(\"\") }))\n\n// Perplexity links answers to sources with positional `[n]` markers, so each\n// citation carries a 1-based `marker` (and `cited_text` from the snippet), the\n// provider-agnostic representation of that style. Prefer the richer\n// `search_results`; fall back to the legacy `citations` URL list.\nconst citationsOf = (wire: WireCompletion): ReadonlyArray<Annotation> =>\n wire.search_results !== undefined && wire.search_results.length > 0\n ? wire.search_results.map((r, i) => ({\n type: \"url_citation\" as const,\n url: r.url,\n title: r.title,\n marker: i + 1,\n ...(r.snippet !== undefined && r.snippet !== \"\" && { cited_text: r.snippet }),\n }))\n : (wire.citations ?? []).map((url, i) => ({\n type: \"url_citation\" as const,\n url,\n title: url,\n marker: i + 1,\n }))\n\nconst completionToTurn = (wire: WireCompletion): Turn => {\n const text = wire.choices[0]?.message?.content ?? \"\"\n const annotations = citationsOf(wire)\n return {\n items: [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text, ...(annotations.length > 0 && { annotations }) }],\n },\n ],\n usage: {\n ...(wire.usage?.prompt_tokens !== undefined && { input_tokens: wire.usage.prompt_tokens }),\n ...(wire.usage?.completion_tokens !== undefined && {\n output_tokens: wire.usage.completion_tokens,\n }),\n ...(wire.usage?.total_tokens !== undefined && { total_tokens: wire.usage.total_tokens }),\n },\n stop_reason: \"stop\",\n }\n}\n\n// Map Perplexity's four job states onto the generic `JobState`, carrying the\n// assembled `Turn` on `Succeeded` and the provider message on `Failed`.\nconst jobStateOf = (wire: WireJob): ResearchState =>\n Match.value(wire.status).pipe(\n Match.when(\"CREATED\", (): ResearchState => ({ _tag: \"Pending\" })),\n Match.when(\"IN_PROGRESS\", (): ResearchState => ({ _tag: \"Running\" })),\n Match.when(\"COMPLETED\", (): ResearchState =>\n wire.response != null\n ? { _tag: \"Succeeded\", result: completionToTurn(wire.response) }\n : { _tag: \"Failed\", reason: \"job completed with no response body\", raw: wire },\n ),\n Match.when(\"FAILED\", (): ResearchState => ({\n _tag: \"Failed\",\n ...(wire.error_message != null && { reason: wire.error_message }),\n raw: wire,\n })),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// HTTP\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config): string => cfg.baseUrl ?? \"https://api.perplexity.ai\"\n\nconst authHeader = (cfg: Config) =>\n HttpClientRequest.setHeader(\"authorization\", `Bearer ${Redacted.value(cfg.apiKey)}`)\n\nconst submitBody = (request: PerplexityResearchRequest): Record<string, unknown> => ({\n request: {\n model: request.model ?? \"sonar-deep-research\",\n messages: historyToMessages(request.history),\n ...(request.reasoningEffort !== undefined && { reasoning_effort: request.reasoningEffort }),\n },\n})\n\nconst submitJob = (\n cfg: Config,\n request: PerplexityResearchRequest,\n): Effect.Effect<ResearchJobRef, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/v1/async/sonar`).pipe(\n authHeader(cfg),\n HttpClientRequest.bodyJsonUnsafe(submitBody(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 const wire = yield* decodeJob(json).pipe(Effect.mapError(transportFailure))\n return Job.jobRef<Turn>(\"perplexity\", wire.id)\n })\n\nconst pollJob = (\n cfg: Config,\n ref: ResearchJobRef,\n): Effect.Effect<ResearchState, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const httpRequest = HttpClientRequest.get(`${baseUrl(cfg)}/v1/async/sonar/${ref.id}`).pipe(\n authHeader(cfg),\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 const wire = yield* decodeJob(json).pipe(Effect.mapError(transportFailure))\n return jobStateOf(wire)\n })\n\n// Perplexity exposes no cancel endpoint and no event stream on the async job.\nconst unsupported = (capability: string): AiError.Unsupported =>\n new AiError.Unsupported({\n provider: \"perplexity\",\n capability,\n reason: \"Perplexity's async research job is poll-only and cannot be cancelled.\",\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n// The three job ops. Poll-only, so no `streamFrom`: `fromJob` synthesizes one.\nconst jobOps = (\n cfg: Config,\n client: HttpClient.HttpClient,\n): ResearchJobOps<PerplexityResearchRequest> => {\n const withClient = <A>(e: Effect.Effect<A, AiError.AiError, HttpClient.HttpClient>) =>\n Effect.provideService(e, HttpClient.HttpClient, client)\n return {\n submit: (request) => withClient(submitJob(cfg, request)),\n poll: (ref) => withClient(pollJob(cfg, ref)),\n cancel: () => Effect.fail(unsupported(\"cancel\")),\n }\n}\n\n/** Build a `PerplexityDeepResearchService`. For Layer setup, prefer {@link layer}. */\nexport const make = (\n cfg: Config,\n): Effect.Effect<PerplexityDeepResearchService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient, (client) => fromJob(jobOps(cfg, client), cfg.job))\n\n/**\n * Layer registering the provider-typed `PerplexityDeepResearch` tag and the\n * generic `DeepResearch` tag. No capability markers: the whole surface (poll,\n * collect, synthesized stream) is universal.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<PerplexityDeepResearch | DeepResearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(PerplexityDeepResearch, make(cfg))\n const generic = Layer.effect(\n DeepResearch,\n Effect.map(HttpClient.HttpClient, (client): DeepResearchService => {\n const ops = jobOps(cfg, client)\n return fromJob<ResearchRequest>(\n {\n submit: (request) => ops.submit(request as PerplexityResearchRequest),\n poll: ops.poll,\n cancel: ops.cancel,\n },\n cfg.job,\n )\n }),\n )\n return Layer.merge(typed, generic)\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { PerplexitySearchContextSize } from "./models.mjs";
2
2
  import { Context, Effect, Layer, Redacted } from "effect";
3
- import { HttpClient } from "effect/unstable/http";
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 PerplexitySearch_d_exports {
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { Context, DateTime, Effect, Layer, Option, Schema } from "effect";
3
- import { HttpClient, HttpClientRequest } from "effect/unstable/http";
3
+ import { HttpClient, HttpClientRequest } from "effect/http";
4
4
  import * as AiError from "@effect-uai/core/AiError";
5
5
  import { WebSearch } from "@effect-uai/core/WebSearch";
6
6
  //#region src/PerplexitySearch.ts
@@ -1 +1 @@
1
- {"version":3,"file":"PerplexitySearch.mjs","names":[],"sources":["../src/PerplexitySearch.ts"],"sourcesContent":["import { Context, DateTime, Effect, Layer, Option, type Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonSearchRequest,\n type SearchResponse,\n type SearchResult,\n WebSearch,\n type WebSearchService,\n} from \"@effect-uai/core/WebSearch\"\nimport type { PerplexitySearchContextSize } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Perplexity-typed search request. Widens {@link CommonSearchRequest} with\n * the provider's own knobs; the common fields map onto Perplexity's wire\n * filters (see the codec below). There is no `model` - `/search` ranks the\n * web directly.\n */\nexport type PerplexitySearchRequest = CommonSearchRequest & {\n /**\n * Depth / cost trade-off for per-page extraction - see\n * {@link PerplexitySearchContextSize}. Mutually exclusive with\n * `maxTokensPerPage`; when both are set the token cap wins (see the codec).\n */\n readonly searchContextSize?: PerplexitySearchContextSize\n /** Cap on tokens extracted per result page (1..1_000_000). */\n readonly maxTokensPerPage?: number\n}\n\nexport type PerplexitySearchService = {\n readonly search: (\n request: PerplexitySearchRequest,\n ) => Effect.Effect<SearchResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Perplexity-typed search\n * (`searchContextSize`, `maxTokensPerPage`); yield the generic `WebSearch`\n * tag for provider-portable code. Both are registered by {@link layer}.\n */\nexport class PerplexitySearch extends Context.Service<PerplexitySearch, PerplexitySearchService>()(\n \"@betalyra/effect-uai/providers/perplexity/PerplexitySearch\",\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 max_results?: number\n readonly country?: string\n readonly search_language_filter?: ReadonlyArray<string>\n readonly search_domain_filter?: ReadonlyArray<string>\n readonly search_recency_filter?: string\n readonly search_after_date_filter?: string\n readonly search_before_date_filter?: string\n readonly search_context_size?: string\n readonly max_tokens_per_page?: number\n}\n\n/** Perplexity wants dates as `MM/DD/YYYY`, in UTC. */\nconst formatMmDdYyyy = (dt: DateTime.DateTime): string => {\n const d = DateTime.toDateUtc(dt)\n const mm = String(d.getUTCMonth() + 1).padStart(2, \"0\")\n const dd = String(d.getUTCDate()).padStart(2, \"0\")\n return `${mm}/${dd}/${d.getUTCFullYear()}`\n}\n\n/**\n * Perplexity has no separate include / exclude fields - it takes one\n * `search_domain_filter` array where an exclusion is a `-` prefix. Lossless\n * shape transform, so done silently (bucket 3).\n */\nconst domainFilter = (\n include: ReadonlyArray<string> | undefined,\n exclude: ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined => {\n const all = [...(include ?? []), ...(exclude ?? []).map((d) => `-${d}`)]\n return all.length > 0 ? all : undefined\n}\n\nconst buildBody = (request: PerplexitySearchRequest): WireBody => {\n const domains = domainFilter(request.includeDomains, request.excludeDomains)\n return {\n query: request.query,\n ...(request.maxResults !== undefined && { max_results: request.maxResults }),\n ...(request.country !== undefined && { country: request.country }),\n ...(request.language !== undefined && { search_language_filter: [request.language] }),\n ...(domains !== undefined && { search_domain_filter: domains }),\n ...(request.recency !== undefined && { search_recency_filter: request.recency }),\n ...(request.startDate !== undefined && {\n search_after_date_filter: formatMmDdYyyy(request.startDate),\n }),\n ...(request.endDate !== undefined && {\n search_before_date_filter: formatMmDdYyyy(request.endDate),\n }),\n // `/search` rejects `search_context_size` alongside a token cap (both govern\n // per-page extraction) with a 500, so the two are mutually exclusive. The\n // token cap is the finer control, so it wins when both are set.\n ...(request.maxTokensPerPage !== undefined\n ? { max_tokens_per_page: request.maxTokensPerPage }\n : request.searchContextSize !== undefined && {\n search_context_size: request.searchContextSize,\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n title: Schema.String,\n url: Schema.String,\n snippet: Schema.optional(Schema.String),\n date: Schema.optional(Schema.NullOr(Schema.String)),\n last_updated: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireResponse = Schema.Struct({\n results: Schema.Array(WireResult),\n id: Schema.optional(Schema.String),\n})\ntype WireResponse = typeof WireResponse.Type\n\n/** Lenient parse of Perplexity's date strings; unparseable -> omitted. */\nconst parseDate = (s: string | null | undefined): DateTime.DateTime | undefined =>\n s == null ? undefined : Option.getOrUndefined(DateTime.make(s))\n\nconst toResult = (r: WireResult): SearchResult => {\n const publishedDate = parseDate(r.date)\n return {\n url: r.url,\n title: r.title,\n // Perplexity returns no relevance score, so `score` stays undefined.\n ...(r.snippet !== undefined && { snippet: r.snippet }),\n ...(publishedDate !== undefined && { publishedDate }),\n raw: r,\n }\n}\n\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: \"perplexity\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"perplexity\"\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.perplexity.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.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 searchImpl =\n (cfg: Config) =>\n (\n request: PerplexitySearchRequest,\n ): Effect.Effect<SearchResponse, AiError.AiError, HttpClient.HttpClient> =>\n postSearch(cfg, buildBody(request)).pipe(Effect.map(toResponse))\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `PerplexitySearchService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<PerplexitySearchService, 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 `PerplexitySearch` tag and the\n * generic `WebSearch` tag over one implementation. A `CommonSearchRequest`\n * is structurally a `PerplexitySearchRequest` with the provider extras\n * unset, so the generic registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<PerplexitySearch | WebSearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(PerplexitySearch, 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":"PerplexitySearch.mjs","names":[],"sources":["../src/PerplexitySearch.ts"],"sourcesContent":["import { Context, DateTime, Effect, Layer, Option, type Redacted, Schema } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport {\n type CommonSearchRequest,\n type SearchResponse,\n type SearchResult,\n WebSearch,\n type WebSearchService,\n} from \"@effect-uai/core/WebSearch\"\nimport type { PerplexitySearchContextSize } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Perplexity-typed search request. Widens {@link CommonSearchRequest} with\n * the provider's own knobs; the common fields map onto Perplexity's wire\n * filters (see the codec below). There is no `model` - `/search` ranks the\n * web directly.\n */\nexport type PerplexitySearchRequest = CommonSearchRequest & {\n /**\n * Depth / cost trade-off for per-page extraction - see\n * {@link PerplexitySearchContextSize}. Mutually exclusive with\n * `maxTokensPerPage`; when both are set the token cap wins (see the codec).\n */\n readonly searchContextSize?: PerplexitySearchContextSize\n /** Cap on tokens extracted per result page (1..1_000_000). */\n readonly maxTokensPerPage?: number\n}\n\nexport type PerplexitySearchService = {\n readonly search: (\n request: PerplexitySearchRequest,\n ) => Effect.Effect<SearchResponse, AiError.AiError>\n}\n\n/**\n * Provider-typed service tag. Yield this for Perplexity-typed search\n * (`searchContextSize`, `maxTokensPerPage`); yield the generic `WebSearch`\n * tag for provider-portable code. Both are registered by {@link layer}.\n */\nexport class PerplexitySearch extends Context.Service<PerplexitySearch, PerplexitySearchService>()(\n \"@betalyra/effect-uai/providers/perplexity/PerplexitySearch\",\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 max_results?: number\n readonly country?: string\n readonly search_language_filter?: ReadonlyArray<string>\n readonly search_domain_filter?: ReadonlyArray<string>\n readonly search_recency_filter?: string\n readonly search_after_date_filter?: string\n readonly search_before_date_filter?: string\n readonly search_context_size?: string\n readonly max_tokens_per_page?: number\n}\n\n/** Perplexity wants dates as `MM/DD/YYYY`, in UTC. */\nconst formatMmDdYyyy = (dt: DateTime.DateTime): string => {\n const d = DateTime.toDateUtc(dt)\n const mm = String(d.getUTCMonth() + 1).padStart(2, \"0\")\n const dd = String(d.getUTCDate()).padStart(2, \"0\")\n return `${mm}/${dd}/${d.getUTCFullYear()}`\n}\n\n/**\n * Perplexity has no separate include / exclude fields - it takes one\n * `search_domain_filter` array where an exclusion is a `-` prefix. Lossless\n * shape transform, so done silently (bucket 3).\n */\nconst domainFilter = (\n include: ReadonlyArray<string> | undefined,\n exclude: ReadonlyArray<string> | undefined,\n): ReadonlyArray<string> | undefined => {\n const all = [...(include ?? []), ...(exclude ?? []).map((d) => `-${d}`)]\n return all.length > 0 ? all : undefined\n}\n\nconst buildBody = (request: PerplexitySearchRequest): WireBody => {\n const domains = domainFilter(request.includeDomains, request.excludeDomains)\n return {\n query: request.query,\n ...(request.maxResults !== undefined && { max_results: request.maxResults }),\n ...(request.country !== undefined && { country: request.country }),\n ...(request.language !== undefined && { search_language_filter: [request.language] }),\n ...(domains !== undefined && { search_domain_filter: domains }),\n ...(request.recency !== undefined && { search_recency_filter: request.recency }),\n ...(request.startDate !== undefined && {\n search_after_date_filter: formatMmDdYyyy(request.startDate),\n }),\n ...(request.endDate !== undefined && {\n search_before_date_filter: formatMmDdYyyy(request.endDate),\n }),\n // `/search` rejects `search_context_size` alongside a token cap (both govern\n // per-page extraction) with a 500, so the two are mutually exclusive. The\n // token cap is the finer control, so it wins when both are set.\n ...(request.maxTokensPerPage !== undefined\n ? { max_tokens_per_page: request.maxTokensPerPage }\n : request.searchContextSize !== undefined && {\n search_context_size: request.searchContextSize,\n }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Codec - response\n// ---------------------------------------------------------------------------\n\nconst WireResult = Schema.Struct({\n title: Schema.String,\n url: Schema.String,\n snippet: Schema.optional(Schema.String),\n date: Schema.optional(Schema.NullOr(Schema.String)),\n last_updated: Schema.optional(Schema.NullOr(Schema.String)),\n})\ntype WireResult = typeof WireResult.Type\n\nconst WireResponse = Schema.Struct({\n results: Schema.Array(WireResult),\n id: Schema.optional(Schema.String),\n})\ntype WireResponse = typeof WireResponse.Type\n\n/** Lenient parse of Perplexity's date strings; unparseable -> omitted. */\nconst parseDate = (s: string | null | undefined): DateTime.DateTime | undefined =>\n s == null ? undefined : Option.getOrUndefined(DateTime.make(s))\n\nconst toResult = (r: WireResult): SearchResult => {\n const publishedDate = parseDate(r.date)\n return {\n url: r.url,\n title: r.title,\n // Perplexity returns no relevance score, so `score` stays undefined.\n ...(r.snippet !== undefined && { snippet: r.snippet }),\n ...(publishedDate !== undefined && { publishedDate }),\n raw: r,\n }\n}\n\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: \"perplexity\", raw: cause })\n\nconst httpStatusError = (status: number, body: string): AiError.AiError => {\n const provider = \"perplexity\"\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.perplexity.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.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 searchImpl =\n (cfg: Config) =>\n (\n request: PerplexitySearchRequest,\n ): Effect.Effect<SearchResponse, AiError.AiError, HttpClient.HttpClient> =>\n postSearch(cfg, buildBody(request)).pipe(Effect.map(toResponse))\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\n/**\n * Build a `PerplexitySearchService` value. For Layer-based setup, prefer\n * {@link layer}.\n */\nexport const make = (\n cfg: Config,\n): Effect.Effect<PerplexitySearchService, 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 `PerplexitySearch` tag and the\n * generic `WebSearch` tag over one implementation. A `CommonSearchRequest`\n * is structurally a `PerplexitySearchRequest` with the provider extras\n * unset, so the generic registration forwards directly.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<PerplexitySearch | WebSearch, never, HttpClient.HttpClient> => {\n const typed = Layer.effect(PerplexitySearch, 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/perplexity",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Perplexity provider for @effect-uai/core (web search).",
5
5
  "keywords": [
6
6
  "ai",
@@ -47,13 +47,13 @@
47
47
  "access": "public"
48
48
  },
49
49
  "devDependencies": {
50
- "effect": "4.0.0-rc.111",
50
+ "effect": "4.0.1",
51
51
  "typescript": "^7.0.2",
52
- "@effect-uai/core": "0.16.0"
52
+ "@effect-uai/core": "0.18.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@effect-uai/core": ">=0.2.0 <1",
56
- "effect": ">=4.0.0-rc.111 <5.0.0"
56
+ "effect": ">=4.0.1 <5.0.0"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsdown",
@@ -1,5 +1,5 @@
1
1
  import { Context, Effect, Layer, Match, Redacted, Schema } from "effect"
2
- import { HttpClient, HttpClientRequest } from "effect/unstable/http"
2
+ import { HttpClient, HttpClientRequest } from "effect/http"
3
3
  import * as AiError from "@effect-uai/core/AiError"
4
4
  import {
5
5
  DeepResearch,
@@ -1,5 +1,5 @@
1
1
  import { Context, DateTime, Effect, Layer, Option, type Redacted, Schema } from "effect"
2
- import { HttpClient, HttpClientRequest } from "effect/unstable/http"
2
+ import { HttpClient, HttpClientRequest } from "effect/http"
3
3
  import * as AiError from "@effect-uai/core/AiError"
4
4
  import {
5
5
  type CommonSearchRequest,