/** * Jina AI direct HTTP transport. * * Performs direct HTTP requests against Jina Reader (r.jina.ai) and Search (s.jina.ai). */ import type { ProviderQuotaFetchResponse } from "../types.js"; /** * Jina transport response — extends the base provider fetch response with * optional response headers. Production `fetch` returns a `Response` that * always has `headers`; test fakes may omit them (optional), and the * transport harvests headers defensively (skips when absent). * * Defined as a Jina-local type (not added to the shared * {@link ProviderQuotaFetchResponse}) because the base type has a CRITICAL * upstream blast radius (56 symbols across every provider). */ export interface JinaFetchResponse extends ProviderQuotaFetchResponse { readonly headers?: { get(name: string): string | null; }; } export interface JinaTransportDeps { readonly fetch?: (input: string, init: Record) => Promise; readonly setTimeout?: typeof setTimeout; readonly clearTimeout?: typeof clearTimeout; readonly env?: NodeJS.ProcessEnv; } export interface JinaDataItem { readonly title?: string; readonly url?: string; readonly content?: string; /** * Text-mode response field (8J.2). When `X-Return-Format: text` is * requested, Jina places the page content in `data.text` instead of * `data.content`. Declared so the adapter can decode text-mode responses * without normalizing valid text as empty. */ readonly text?: string; readonly description?: string; readonly publishedTime?: string; readonly metadata?: unknown; readonly external?: unknown; /** * Token usage for this result item, as returned in the response body. * Visible in captured fixtures: Reader returns a single top-level * `usage.tokens`, Search returns per-result `usage.tokens`. Declared * so callers can access it; the quota capability uses response headers * (`X-RateLimit-Remaining-*`) rather than body usage for its probe. */ readonly usage?: { readonly tokens?: number; }; } export interface JinaResponse { readonly code?: number; readonly data?: JinaDataItem | readonly JinaDataItem[]; } /** * Normalized Reader options forwarded to Jina's documented headers (8J.2). * Each maps to a Jina Reader request header (see * https://github.com/jina-ai/reader/blob/main/src/dto/crawler-options.ts): * * format -> X-Return-Format ("markdown" | "text") * retainImages -> X-Retain-Images ("true" | "false") * withLinksSummary -> X-With-Links-Summary ("true" | "false") * noGfm -> X-No-Gfm ("true" | "false"; GFM is on by default) * keepImgDataUrl -> X-Keep-Img-Data-Url ("true" | "false") * withImagesSummary -> X-With-Images-Summary ("true" | "false") * timeout -> X-Timeout (seconds; clamped to Jina's 180s ceiling) */ export interface JinaReaderOptions { readonly format?: "markdown" | "text"; readonly retainImages?: boolean; readonly withLinksSummary?: boolean; readonly noGfm?: boolean; readonly keepImgDataUrl?: boolean; readonly withImagesSummary?: boolean; readonly timeout?: number; } export declare function resolveTimeoutMs(env: NodeJS.ProcessEnv): number; export declare function fetchJinaReader(apiKey: string | undefined, targetUrl: string, deps?: JinaTransportDeps, options?: JinaReaderOptions): Promise; export declare function fetchJinaSearch(apiKey: string | undefined, query: string, deps?: JinaTransportDeps, options?: { /** * Domain restriction forwarded as Jina's `X-Site` header (8J.3). * Accepts a bare hostname like "example.com". */ readonly domain?: string; /** * Two-letter ISO country code for result localization (8J.3). * Sent as the `gl` field in a POST JSON body. */ readonly location?: string; }): Promise; export interface JinaDeepSearchUrlCitation { readonly title?: string; readonly url: string; readonly exactQuote?: string; } export interface JinaDeepSearchAnnotation { readonly type?: string; readonly url_citation?: JinaDeepSearchUrlCitation; } export interface JinaDeepSearchResponse { readonly choices?: readonly { readonly message?: { readonly content?: string; readonly annotations?: readonly JinaDeepSearchAnnotation[]; }; }[]; readonly visitedURLs?: readonly string[]; } export declare function resolveDeepSearchTimeoutMs(env: NodeJS.ProcessEnv): number; export declare function fetchJinaDeepSearch(apiKey: string | undefined, query: string, deps?: JinaTransportDeps, externalSignal?: AbortSignal, options?: { /** * Domain restriction forwarded as DeepSearch's `only_hostnames` * array (8J.4). Accepts a bare hostname like "example.com". */ readonly domain?: string; }): Promise; /** * Jina's rate-limit response headers, as documented in the OpenAPI schema * (api.jina.ai/openapi.json): * * "Rate limit headers are included in responses: * `X-RateLimit-Remaining-Requests`, `X-RateLimit-Remaining-Tokens`." * * **Header-name correction (lesson 0.14.8):** finding 8J.5 originally * claimed `x-ratelimit-limit`, `x-ratelimit-remaining`, and `x-usage-tokens`. * The OpenAPI schema contradicts this — the actual headers are * `X-RateLimit-Remaining-Requests` and `X-RateLimit-Remaining-Tokens`. * No `X-RateLimit-Limit` or reset header is exposed. * * The documented rate-limit tiers (per OpenAPI schema): * Free: 500 RPM, 1M TPM, 5 concurrency * Tier 1: 500 RPM, 10M TPM, 50 concurrency * Tier 2: 5,000 RPM, 100M TPM, 500 concurrency */ export interface JinaRateLimitHeaders { /** Remaining requests in the current per-minute window (null if absent). */ readonly remainingRequests: number | null; /** Remaining tokens in the current per-minute window (null if absent). */ readonly remainingTokens: number | null; } /** * Perform a lightweight Search probe against `s.jina.ai` and return the * rate-limit response headers. The probe sends a minimal query (costs * exactly ONE request and ~10k fixed tokens) and drains the body — only * the headers are needed. * * Mirrors Brave's `fetchBraveRateLimit` pattern: one direct probe, read * headers, drain body, normalize outside. Requires `JINA_API_KEY` (Search * is not keyless — 8J.1). */ export declare function fetchJinaRateLimit(apiKey: string, deps?: JinaTransportDeps): Promise; //# sourceMappingURL=client.d.ts.map