refactor: prune unused web seam fields
This commit is contained in:
@@ -18,11 +18,9 @@
|
||||
import { Context, Service } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import type {
|
||||
WebExecContext,
|
||||
WebFetchProvider,
|
||||
WebFetchRequest,
|
||||
WebFetchResult,
|
||||
WebProviderStatus,
|
||||
WebSearchProvider,
|
||||
WebSearchRequest,
|
||||
WebSearchResult,
|
||||
@@ -33,12 +31,10 @@ export {
|
||||
WebError,
|
||||
} from './types.ts'
|
||||
export type {
|
||||
WebExecContext,
|
||||
WebFetchBody,
|
||||
WebFetchProvider,
|
||||
WebFetchRequest,
|
||||
WebFetchResult,
|
||||
WebProviderStatus,
|
||||
WebSearchProvider,
|
||||
WebSearchRequest,
|
||||
WebSearchResult,
|
||||
@@ -76,7 +72,7 @@ export interface WebServiceConfig {
|
||||
* The web access service. Registered as `ctx.web` (one instance per context).
|
||||
*
|
||||
* Selection semantics (resolved at execution time, never order-dependent):
|
||||
* - A configured id that is registered and `status().available` → that provider.
|
||||
* - A configured id that is registered and `available()` → that provider.
|
||||
* - A configured id not registered → `WEB_PROVIDER_CONFIGURED_MISSING`.
|
||||
* - A configured id registered but unavailable →
|
||||
* `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`.
|
||||
@@ -147,15 +143,15 @@ export class WebService extends Service {
|
||||
* capability cannot run. The seam enforces `request.maxResults` on the result:
|
||||
* if the provider over-returns, `sources[]` is truncated and `truncated` set.
|
||||
* @param request - the query plus result-shaping options.
|
||||
* @param exec - the tool-execution context, forwarded to the provider.
|
||||
* @param signal - optional cancellation signal forwarded to the provider.
|
||||
* @returns the provider's results, capped to `request.maxResults`.
|
||||
*/
|
||||
async search(request: WebSearchRequest, exec?: WebExecContext): Promise<WebSearchResult> {
|
||||
async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult> {
|
||||
const provider = resolveProvider({
|
||||
providers: this.searchProviders,
|
||||
...this.searchProviderId !== undefined ? { configuredId: this.searchProviderId } : {},
|
||||
})
|
||||
const result = await provider.search(request, exec)
|
||||
const result = await provider.search(request, signal)
|
||||
return capSources(result, request.maxResults)
|
||||
}
|
||||
|
||||
@@ -164,21 +160,21 @@ export class WebService extends Service {
|
||||
* call time with the selection rules above; throws {@link WebError} when the
|
||||
* capability cannot run. A non-2xx response is a result, not a throw.
|
||||
* @param request - the URL plus retrieval options.
|
||||
* @param exec - the tool-execution context, forwarded to the provider.
|
||||
* @param signal - optional cancellation signal forwarded to the provider.
|
||||
* @returns the retrieval outcome; non-2xx responses resolve descriptively.
|
||||
*/
|
||||
async fetch(request: WebFetchRequest, exec?: WebExecContext): Promise<WebFetchResult> {
|
||||
async fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult> {
|
||||
const provider = resolveProvider({
|
||||
providers: this.fetchProviders,
|
||||
...this.fetchProviderId !== undefined ? { configuredId: this.fetchProviderId } : {},
|
||||
})
|
||||
return provider.fetch(request, exec)
|
||||
return provider.fetch(request, signal)
|
||||
}
|
||||
}
|
||||
|
||||
interface ResolvableProvider {
|
||||
readonly id: string
|
||||
status(): WebProviderStatus
|
||||
available(): boolean
|
||||
}
|
||||
|
||||
/** Resolve the selected provider or throw the matching {@link WebError}. */
|
||||
@@ -189,12 +185,12 @@ function resolveProvider<P extends ResolvableProvider>(selection: Selection<P>):
|
||||
if (!provider) {
|
||||
throw new WebError(`configured web provider "${configuredId}" is not registered`, 'WEB_PROVIDER_CONFIGURED_MISSING')
|
||||
}
|
||||
if (!provider.status().available) {
|
||||
if (!provider.available()) {
|
||||
throw new WebError(`configured web provider "${configuredId}" is registered but unavailable`, 'WEB_PROVIDER_CONFIGURED_UNAVAILABLE')
|
||||
}
|
||||
return provider
|
||||
}
|
||||
const usable = [...providers.values()].filter(provider => provider.status().available)
|
||||
const usable = [...providers.values()].filter(provider => provider.available())
|
||||
const [single] = usable
|
||||
if (single === undefined) {
|
||||
throw new WebError('no usable web provider is registered', 'WEB_PROVIDER_UNAVAILABLE')
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/**
|
||||
* Vocabulary for the web capability seam (`ctx.web`): the search/fetch
|
||||
* request/result shapes providers produce and consumers format, the provider
|
||||
* status discriminant selection reads, the execution-control context, and the
|
||||
* typed error taxonomy.
|
||||
* request/result shapes providers produce and consumers format, provider
|
||||
* availability, direct cancellation control, and the typed error taxonomy.
|
||||
*
|
||||
* These types are shared by every provider backend
|
||||
* (`@deepseek-ai/dsh-web-search-exa`, `@deepseek-ai/dsh-web-search-perplexity`,
|
||||
@@ -19,19 +18,6 @@
|
||||
|
||||
import { HarnessError } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/**
|
||||
* Execution control threaded from the tool layer through the seam into a
|
||||
* provider's network requests, stream readers, and expensive decoding. It is
|
||||
* NOT business input: the first version carries only `signal` so `tool-web` can
|
||||
* propagate turn cancellation, tool timeout, and agent disposal. It deliberately
|
||||
* does NOT carry `ToolExecution`, which would make `dsh-web` depend on
|
||||
* `dsh-tools`.
|
||||
*/
|
||||
export interface WebExecContext {
|
||||
/** Abort signal a provider must honor for its network/decoding work. */
|
||||
readonly signal?: AbortSignal
|
||||
}
|
||||
|
||||
/**
|
||||
* What one search-capable backend can return. The model-facing argument is just
|
||||
* a query; `maxResults` is a `dsh-tool-web`-layer bound passed through unchanged
|
||||
@@ -56,10 +42,6 @@ export interface WebSearchRequest {
|
||||
* when it cut `sources[]` down to `maxResults`.
|
||||
*/
|
||||
export interface WebSearchResult {
|
||||
/** Id of the provider that produced this result. */
|
||||
readonly providerId: string
|
||||
/** Echo of the query the provider answered. */
|
||||
readonly query: string
|
||||
/** Optional provider-generated answer text, search context, or summary. */
|
||||
readonly content?: string
|
||||
/** Citeable sources, already truncated to the request's `maxResults`. */
|
||||
@@ -83,14 +65,13 @@ export interface WebSearchSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* What one fetch-capable backend is asked to retrieve. `timeoutMs` is an
|
||||
* optional positive hint the provider caps. The request deliberately omits
|
||||
* `format`, `prompt`, and extraction controls — those are presentation or
|
||||
* higher-level LLM concerns, not safe-retrieval inputs.
|
||||
* What one fetch-capable backend is asked to retrieve. The request deliberately
|
||||
* omits timeout, format, prompt, and extraction controls: cancellation is a
|
||||
* direct execution argument, while presentation and higher-level LLM concerns
|
||||
* belong outside safe retrieval.
|
||||
*/
|
||||
export interface WebFetchRequest {
|
||||
readonly url: string
|
||||
readonly timeoutMs?: number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,8 +81,6 @@ export interface WebFetchRequest {
|
||||
* represent the resource.
|
||||
*/
|
||||
export interface WebFetchResult {
|
||||
/** Id of the provider that produced this result. */
|
||||
readonly providerId: string
|
||||
/** The final URL after allowed redirects (the request URL is in the request). */
|
||||
readonly url: string
|
||||
/** HTTP status code of the fetched response. */
|
||||
@@ -125,18 +104,6 @@ export type WebFetchBody =
|
||||
| { readonly kind: 'html'; readonly content: string }
|
||||
| { readonly kind: 'text'; readonly content: string }
|
||||
|
||||
/**
|
||||
* Whether one concrete provider implementation is usable, by cheap local checks
|
||||
* only (credential presence, parseable endpoint config). A provider `status()`
|
||||
* must NOT make network calls. It is an input to execution-time selection, not
|
||||
* a health system: `WebService.search()`/`fetch()` read it to pick a usable
|
||||
* provider, and selection failure surfaces as the structured {@link WebError}
|
||||
* codes callers route on.
|
||||
*/
|
||||
export type WebProviderStatus =
|
||||
| { readonly available: true }
|
||||
| { readonly available: false; readonly reason: 'missing-credential' | 'misconfigured' }
|
||||
|
||||
/**
|
||||
* A search-capable backend. Registered with `ctx.web.registerSearchProvider`.
|
||||
* `id` is a stable string, unique within the search capability kind.
|
||||
@@ -144,9 +111,9 @@ export type WebProviderStatus =
|
||||
export interface WebSearchProvider {
|
||||
readonly id: string
|
||||
/** Cheap local usability check; must not make network calls. */
|
||||
status(): WebProviderStatus
|
||||
/** Run one search; honor `exec.signal` for cancellation. */
|
||||
search(request: WebSearchRequest, exec?: WebExecContext): Promise<WebSearchResult>
|
||||
available(): boolean
|
||||
/** Run one search; honor `signal` for cancellation. */
|
||||
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,9 +123,9 @@ export interface WebSearchProvider {
|
||||
export interface WebFetchProvider {
|
||||
readonly id: string
|
||||
/** Cheap local usability check; must not make network calls. */
|
||||
status(): WebProviderStatus
|
||||
/** Retrieve one URL; honor `exec.signal` for cancellation. */
|
||||
fetch(request: WebFetchRequest, exec?: WebExecContext): Promise<WebFetchResult>
|
||||
available(): boolean
|
||||
/** Retrieve one URL; honor `signal` for cancellation. */
|
||||
fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,12 +145,12 @@ export interface WebFetchProvider {
|
||||
* - `WEB_PROVIDER_UNAVAILABLE`: no provider configured and none usable.
|
||||
* - `WEB_PROVIDER_CONFIGURED_MISSING`: a configured id is not registered.
|
||||
* - `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`: a configured id is registered but its
|
||||
* `status()` reports unavailable.
|
||||
* `available()` returns false.
|
||||
* - `WEB_PROVIDER_AMBIGUOUS`: no id configured and multiple usable providers
|
||||
* exist (selection refuses to pick by registration order).
|
||||
* - `WEB_DUPLICATE_PROVIDER`: a registration-time programming error — an id is
|
||||
* already registered for that capability kind.
|
||||
* - `WEB_ABORTED`: the operation was aborted via `WebExecContext.signal`.
|
||||
* - `WEB_ABORTED`: the operation was aborted via its optional signal.
|
||||
* - `WEB_PROVIDER_ERROR`: catch-all for a provider's own failure surfaced
|
||||
* through the seam, including network/transport failure (DNS, connection
|
||||
* refused, TLS).
|
||||
|
||||
Reference in New Issue
Block a user