/** * DeepSeek Fetch API provider: page Markdown through one direct request, without * a model request. * @module @deepseek-ai/dsh-web-deepseek-api/fetch */ import type { WebFetchProvider, WebFetchRequest, WebFetchResult } from '@deepseek-ai/dsh-web' import { DEEPSEEK_WEB_API_GUIDANCE } from './search.ts' import { dispatchDeepSeekWeb, hasDeepSeekWebCredential, invalidResponse, isRecord, type DeepSeekWebAuth, } from './transport.ts' /** Stable id the Fetch API provider registers under in the fetch registry. */ export const DEEPSEEK_FETCH_API_PROVIDER_ID = 'deepseek-fetch-api' /** Authentication and endpoint settings snapshotted for one fetch. */ export interface DeepSeekFetchApiOptions extends DeepSeekWebAuth { /** API base including its path prefix; `/fetch` is appended. */ baseURL: string } /** * Fetch page Markdown through the DeepSeek Fetch API. The API reports neither the * target's status nor a final URL, and it fails when the target yields no usable * content, so a success is the requested page retrieved with HTTP 200. */ export class DeepSeekFetchApiProvider implements WebFetchProvider { readonly id = DEEPSEEK_FETCH_API_PROVIDER_ID /** @param resolveOptions - the options for the NEXT fetch, read once at each fetch's entry. */ constructor(private readonly resolveOptions: () => DeepSeekFetchApiOptions) {} available(): boolean { const options = this.resolveOptions() return hasDeepSeekWebCredential(options) && URL.canParse(options.baseURL) } fetch(request: WebFetchRequest, signal?: AbortSignal): Promise { const options = this.resolveOptions() return dispatchDeepSeekWeb(options, { endpoint: `${options.baseURL.replace(/\/+$/u, '')}/fetch`, body: { url: request.url }, decode: (payload): WebFetchResult => { if (!isRecord(payload) || typeof payload.content !== 'string') throw invalidResponse() return { url: request.url, statusCode: 200, body: { kind: 'text', content: payload.content }, truncated: false } }, guidance: { subject: 'Fetch', ...DEEPSEEK_WEB_API_GUIDANCE }, }, signal) } }