@maci0/dsh-google-vertex 0.0.0-stage → 0.12.4

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.
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Wire translation for Google's own Gemini models on Vertex AI: the request
3
+ * body `publishers/google` accepts, the SSE response shape, and (the part that
4
+ * makes tool calling work at all on Gemini 3) the thought signature that has to
5
+ * travel back with every replayed function call.
6
+ *
7
+ * Everything here is pure: the adapter feeds it bytes and yields the chunks it
8
+ * returns, so the protocol is testable without a harness or a network.
9
+ *
10
+ * @module dsh-google-vertex/gemini
11
+ */
12
+ import type { FinishReason, GenerateOptions, Message, StreamChunk, TokenUsage } from './host.ts';
13
+ import type { VertexWireConfig } from './wire.ts';
14
+ import type { VertexModel } from './adapter.ts';
15
+ /** Harness code reported when the provider refused on safety grounds. */
16
+ export declare const SAFETY_BLOCKED_CODE = "SAFETY";
17
+ /**
18
+ * Gemini context capacity: every current Gemini model serves roughly a million
19
+ * tokens, and the catalog below narrows nothing.
20
+ */
21
+ export declare const DEFAULT_GEMINI_CONTEXT_WINDOW = 1048576;
22
+ /**
23
+ * Output cap applied when a caller omits one.
24
+ *
25
+ * Vertex's ceiling here is EXCLUSIVE: `maxOutputTokens: 65536` is refused with
26
+ * "supported range is from 1 (inclusive) to 65536 (exclusive)", so the highest
27
+ * accepted value is one less.
28
+ */
29
+ export declare const DEFAULT_GEMINI_MAX_TOKENS = 65535;
30
+ /**
31
+ * Gemini models the global endpoint serves, in picker order. Ids are the
32
+ * provider's own aliases, so a promoted release needs no edit here.
33
+ */
34
+ export declare const DEFAULT_GEMINI_MODELS: readonly VertexModel[];
35
+ /**
36
+ * The streaming publisher path for one Gemini model.
37
+ * @param project - Google Cloud project id.
38
+ * @param location - region, or `global`.
39
+ * @param model - publisher model id, e.g. `gemini-3.5-flash`.
40
+ * @returns the absolute request URL, with the SSE response encoding.
41
+ */
42
+ export declare function geminiEndpointFor(project: string, location: string, model: string): string;
43
+ /** One function call the model requested. */
44
+ interface GeminiFunctionCall {
45
+ name: string;
46
+ args?: Record<string, unknown>;
47
+ id?: string;
48
+ }
49
+ /** One function result being sent back. */
50
+ interface GeminiFunctionResponse {
51
+ name: string;
52
+ id?: string;
53
+ response: Record<string, unknown>;
54
+ }
55
+ /** One content part on the wire. */
56
+ interface GeminiPart {
57
+ text?: string;
58
+ thought?: boolean;
59
+ thoughtSignature?: string;
60
+ functionCall?: GeminiFunctionCall;
61
+ functionResponse?: GeminiFunctionResponse;
62
+ }
63
+ /** One wire content turn. */
64
+ export interface GeminiContent {
65
+ role: 'user' | 'model';
66
+ parts: GeminiPart[];
67
+ }
68
+ /** One wire tool declaration. */
69
+ interface GeminiToolDeclaration {
70
+ name: string;
71
+ description: string;
72
+ parameters: Record<string, unknown>;
73
+ }
74
+ /** The request body `:streamGenerateContent` accepts. */
75
+ export interface GeminiRequestBody {
76
+ contents: GeminiContent[];
77
+ systemInstruction?: {
78
+ parts: {
79
+ text: string;
80
+ }[];
81
+ };
82
+ tools?: {
83
+ functionDeclarations: GeminiToolDeclaration[];
84
+ }[];
85
+ generationConfig?: {
86
+ temperature?: number;
87
+ maxOutputTokens?: number;
88
+ stopSequences?: string[];
89
+ };
90
+ }
91
+ /**
92
+ * Index-aligned provider metadata for one emitted block.
93
+ *
94
+ * Vertex requires the thought signature of a function call to be echoed when the
95
+ * call is replayed with its result; dropping it is a 400
96
+ * ("Function call is missing a thought_signature"). Text parts can carry one
97
+ * too, so the entry is kept for both block kinds.
98
+ */
99
+ interface GeminiReplayBlock {
100
+ readonly type: 'text' | 'tool-call';
101
+ readonly thoughtSignature?: string;
102
+ }
103
+ /** The envelope the harness stores on an assistant message and hands back. */
104
+ interface GeminiReplayEnvelope {
105
+ readonly response: {
106
+ readonly kind: 'google-vertex-gemini';
107
+ readonly version: 1;
108
+ readonly model: string;
109
+ };
110
+ readonly blocks: readonly GeminiReplayBlock[];
111
+ }
112
+ /**
113
+ * Build the replay envelope for one finished response.
114
+ * @param model - the model that produced the response.
115
+ * @param blocks - per-block metadata in emitted order.
116
+ * @returns the envelope for the terminal finish chunk.
117
+ */
118
+ export declare function geminiReplayState(model: string, blocks: readonly GeminiReplayBlock[]): GeminiReplayEnvelope;
119
+ /**
120
+ * Read back the replay metadata for one assistant message.
121
+ *
122
+ * Anything unexpected (a foreign envelope, another model, a block count that no
123
+ * longer lines up with the content) yields undefined, which degrades to sending
124
+ * the call without its signature rather than throwing: the provider then decides,
125
+ * and a cross-provider history stays replayable.
126
+ * @param message - the assistant message from history.
127
+ * @param model - the model about to be called; signatures do not cross models.
128
+ * @returns index-aligned metadata, or undefined when unusable.
129
+ */
130
+ export declare function readGeminiReplay(message: Message, model: string): readonly GeminiReplayBlock[] | undefined;
131
+ /**
132
+ * Build the request body for one model call.
133
+ *
134
+ * History is projected part by part (tool results become `functionResponse`
135
+ * parts, replayed tool calls carry their thought signature), and consecutive
136
+ * same-role turns are merged, which is the one turn shape Gemini accepts.
137
+ *
138
+ * No `thinkingConfig` is ever sent: Gemini's own default (dynamic thinking) is
139
+ * what keeps 2.5 Pro working, since that model refuses a zero thinking budget.
140
+ * @param options - the harness request.
141
+ * @param config - project, location, and default output cap.
142
+ * @returns the wire body.
143
+ */
144
+ export declare function buildGeminiRequest(options: GenerateOptions, config: VertexWireConfig): GeminiRequestBody;
145
+ /** Raw usage counters as Vertex reports them. */
146
+ interface GeminiUsageMetadata {
147
+ promptTokenCount?: number;
148
+ candidatesTokenCount?: number;
149
+ cachedContentTokenCount?: number;
150
+ thoughtsTokenCount?: number;
151
+ totalTokenCount?: number;
152
+ }
153
+ /**
154
+ * Map Gemini's counters onto harness accounting.
155
+ *
156
+ * Gemini folds cached input into `promptTokenCount` and reports it separately as
157
+ * well, so the harness's disjoint rule means subtracting it out; thinking tokens
158
+ * are billed as output, exactly as the harness's own pi-ai mapping treats them.
159
+ * @param usage - the latest cumulative counters seen on the stream.
160
+ * @returns disjoint harness counts.
161
+ */
162
+ export declare function mapGeminiUsage(usage: GeminiUsageMetadata): TokenUsage;
163
+ /**
164
+ * Map Gemini's finish reason onto the harness vocabulary.
165
+ *
166
+ * `STOP`, `OTHER`, and an absent reason all mean the same thing here, and so
167
+ * does any reason a provider release adds: the turn ended, and only a tool call
168
+ * in it changes what that is called.
169
+ * @param reason - the `finishReason` Vertex reported.
170
+ * @param sawToolCall - whether the response contained a function call, which
171
+ * Gemini reports as an ordinary `STOP`.
172
+ * @returns the harness finish reason.
173
+ */
174
+ export declare function mapGeminiFinishReason(reason: string | undefined, sawToolCall: boolean): FinishReason;
175
+ /**
176
+ * Translate Gemini's `streamGenerateContent` chunks into harness chunks.
177
+ *
178
+ * Gemini streams whole parts rather than deltas, so the shape of this translator
179
+ * is: text parts append to one open text block, a function call closes that block
180
+ * and is itself closed immediately (arguments arrive complete), and the terminal
181
+ * chunk carries usage plus the stop reason.
182
+ */
183
+ export declare class GeminiStreamTranslator {
184
+ #private;
185
+ /**
186
+ * @param model - the model being called, recorded in the replay envelope.
187
+ */
188
+ constructor(model: string);
189
+ /**
190
+ * Feed one decoded SSE payload.
191
+ * @param event - the parsed chunk.
192
+ * @returns the chunks this payload completes, in order.
193
+ */
194
+ handle(event: Record<string, unknown>): StreamChunk[];
195
+ /** True once the provider reported a finish reason. */
196
+ get sawFinish(): boolean;
197
+ /** True once an in-band error ended the stream, which `handle` already reported. */
198
+ get failed(): boolean;
199
+ /** {@inheritDoc StreamTranslatorLike.terminal} */
200
+ get terminal(): boolean;
201
+ /**
202
+ * Terminal chunks: the closed tail, usage, and the finish reason.
203
+ *
204
+ * Called once, after the body ends. A response that produced nothing at all is
205
+ * reported as an empty response rather than as a silent success.
206
+ * @returns the trailing chunks in emission order.
207
+ */
208
+ finish(): StreamChunk[];
209
+ }
210
+ export {};
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Provider adapter for Google's own Gemini models on Vertex AI.
3
+ *
4
+ * Same credential as the Claude route beside it (one service-account file, one
5
+ * bearer token per request) over the `publishers/google` endpoint instead of
6
+ * `publishers/anthropic`. Both live in one plugin row so project, region, and
7
+ * credentials are configured once.
8
+ *
9
+ * The route is text-only: `inputModalities: ['text']` makes `LlmRuntime` project
10
+ * images and files to placeholder text before dispatch. Tool calling is fully
11
+ * supported, including Vertex's required thought-signature replay.
12
+ *
13
+ * @module dsh-google-vertex/gemini-adapter
14
+ */
15
+ import { VertexPublisherAdapter } from './adapter.ts';
16
+ import type { TokenProvider, VertexModel } from './adapter.ts';
17
+ import type { FetchLike, ServiceAccount } from './auth.ts';
18
+ import type { VertexWireConfig } from './wire.ts';
19
+ import type { GenerateOptions, StreamChunk } from './host.ts';
20
+ /** Resolved adapter configuration for the Gemini route. */
21
+ export interface GeminiAdapterConfig extends VertexWireConfig {
22
+ readonly serviceAccount: ServiceAccount;
23
+ readonly models: readonly VertexModel[];
24
+ /** Bound on the interval between two stream reads, in milliseconds. */
25
+ readonly streamIdleTimeoutMs: number;
26
+ }
27
+ /**
28
+ * Duck-typed adapter over Vertex's Gemini publisher endpoint.
29
+ *
30
+ * The metadata face and the streaming pipeline are the shared ones; this class
31
+ * supplies the Gemini catalog, wording, endpoint, body, and translator.
32
+ */
33
+ export declare class GoogleVertexGeminiAdapter extends VertexPublisherAdapter<GeminiAdapterConfig> {
34
+ /**
35
+ * @param config - the resolved configuration this adapter serves.
36
+ * @param options - transport, token-source, and discovery overrides for tests.
37
+ */
38
+ constructor(config: GeminiAdapterConfig, options?: {
39
+ fetch?: FetchLike;
40
+ tokens?: TokenProvider;
41
+ discover?: () => Promise<readonly VertexModel[]>;
42
+ });
43
+ /**
44
+ * Stream one completion through `:streamGenerateContent?alt=sse`.
45
+ *
46
+ * Gemini has no terminal event: the body simply ends, and the finish reason
47
+ * rides the last content chunk. A body that ends without one is therefore a
48
+ * truncated response, which is what {@link GeminiStreamTranslator.sawFinish}
49
+ * distinguishes, unless an in-band error or the idle watchdog already ended
50
+ * the turn. The shared pump owns the watchdog, the token mint, and the SSE
51
+ * loop; this route's finish is built at the end of the body.
52
+ */
53
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
54
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * The slice of the DeepSeek Harness host surface this plugin uses, declared
3
+ * structurally.
4
+ *
5
+ * Like the other plugins under `~/dsh-plugins`, this package is installed
6
+ * outside the harness checkout and cannot resolve `@deepseek-ai/*` from its own
7
+ * directory, so it carries no runtime dependency on them. `LlmRuntime` reaches
8
+ * adapters through plain method calls (there is no `instanceof LlmAdapter`
9
+ * check anywhere in it), so a duck-typed adapter is a supported shape.
10
+ *
11
+ * Each declaration is narrowed to what this adapter reads or emits. Fields the
12
+ * adapter never touches are deliberately absent: a mirrored field that nothing
13
+ * reads is a field whose absence goes unnoticed. Widen a declaration when the
14
+ * adapter starts using it, not before.
15
+ *
16
+ * @module dsh-google-vertex/host
17
+ */
18
+ /** Disposer returned by every host registration. */
19
+ type Disposable = () => void;
20
+ /** Content block the harness may hand us in request history. */
21
+ export type ContentBlock = {
22
+ readonly type: 'text';
23
+ readonly text: string;
24
+ } | {
25
+ readonly type: 'reasoning';
26
+ readonly text: string;
27
+ } | {
28
+ readonly type: 'tool-call';
29
+ readonly id: string;
30
+ readonly name: string;
31
+ readonly arguments: string;
32
+ } | {
33
+ readonly type: string;
34
+ readonly [key: string]: unknown;
35
+ };
36
+ /** One message in a fully-assembled request. */
37
+ export interface Message {
38
+ readonly id: string;
39
+ /**
40
+ * The harness carries a tool result as its own `tool`-role message, with the
41
+ * call identity and error flag on the message rather than in its content.
42
+ */
43
+ readonly role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
44
+ readonly content: readonly ContentBlock[];
45
+ /** Tool-role only: the provider call id this message answers. */
46
+ readonly toolCallId?: string;
47
+ /** Tool-role only: whether the tool invocation failed. */
48
+ readonly isError?: boolean;
49
+ /**
50
+ * Producer attribution. Only `replayState` is read: an assistant message this
51
+ * adapter produced carries the provider's per-block metadata (Vertex's
52
+ * thought signatures), which the next request must echo back.
53
+ */
54
+ readonly source?: {
55
+ readonly kind?: string;
56
+ readonly replayState?: unknown;
57
+ };
58
+ }
59
+ /** JSON-schema description of one tool, as the harness sends it. */
60
+ export interface ToolSchema {
61
+ readonly name: string;
62
+ readonly description: string;
63
+ readonly parameters: Record<string, unknown>;
64
+ }
65
+ /** A single model request, narrowed to the fields this adapter reads. */
66
+ export interface GenerateOptions {
67
+ readonly provider?: string;
68
+ readonly model: string;
69
+ readonly messages: readonly Message[];
70
+ readonly system?: string;
71
+ readonly tools?: readonly ToolSchema[];
72
+ readonly temperature?: number;
73
+ readonly maxTokens?: number;
74
+ readonly stop?: readonly string[];
75
+ readonly signal?: AbortSignal;
76
+ }
77
+ /** Token accounting for one model call. */
78
+ export interface TokenUsage {
79
+ inputTokens: number;
80
+ outputTokens: number;
81
+ totalTokens?: number;
82
+ cacheReadTokens?: number;
83
+ cacheWriteTokens?: number;
84
+ reasoningTokens?: number;
85
+ }
86
+ /** Stable provider-neutral failure shape. */
87
+ export interface LlmFailure {
88
+ readonly message: string;
89
+ readonly code: string;
90
+ readonly status?: number;
91
+ /** Positive provider-requested retry delay in milliseconds. */
92
+ readonly providerRetryAfterMs?: number;
93
+ }
94
+ /** Why a model response stopped. */
95
+ export type FinishReason = {
96
+ readonly kind: 'stop';
97
+ } | {
98
+ readonly kind: 'tool-calls';
99
+ } | {
100
+ readonly kind: 'max-tokens';
101
+ } | {
102
+ readonly kind: 'aborted';
103
+ readonly failure: LlmFailure;
104
+ } | {
105
+ readonly kind: 'error';
106
+ readonly failure: LlmFailure;
107
+ };
108
+ /** Adapter-private lossless-JSON state carried by a terminal finish chunk. */
109
+ interface ReplayEnvelope {
110
+ /** Response-level metadata (ids, native stop reason). */
111
+ readonly response: unknown;
112
+ /** Per-block metadata, one entry per emitted block in stream order. */
113
+ readonly blocks?: readonly unknown[];
114
+ }
115
+ /** The chunk shapes this adapter emits. */
116
+ export type StreamChunk = {
117
+ readonly type: 'block-start';
118
+ readonly index: number;
119
+ readonly blockType: string;
120
+ } | {
121
+ readonly type: 'text-delta';
122
+ readonly index: number;
123
+ readonly text: string;
124
+ } | {
125
+ readonly type: 'tool-call-delta';
126
+ readonly index: number;
127
+ readonly id: string;
128
+ readonly name?: string;
129
+ readonly argumentsDelta: string;
130
+ } | {
131
+ readonly type: 'block-end';
132
+ readonly index: number;
133
+ readonly block: ContentBlock;
134
+ } | {
135
+ readonly type: 'usage';
136
+ readonly usage: TokenUsage;
137
+ } | {
138
+ readonly type: 'finish';
139
+ readonly reason: FinishReason;
140
+ readonly replayState?: ReplayEnvelope;
141
+ };
142
+ /** Display metadata for one adapter-owned provider route. */
143
+ export interface LlmProviderInfo {
144
+ readonly id: string;
145
+ readonly name: string;
146
+ }
147
+ /** One adapter-advertised model. */
148
+ export interface LlmModelInfo {
149
+ readonly provider: string;
150
+ readonly id: string;
151
+ readonly name: string;
152
+ readonly description?: string;
153
+ readonly inputModalities?: readonly string[];
154
+ }
155
+ /** Exact-route model metadata resolved by its owning adapter. */
156
+ export interface LlmResolvedModelInfo extends LlmModelInfo {
157
+ readonly context?: {
158
+ readonly contextWindow: number;
159
+ };
160
+ readonly defaultMaxTokens?: number;
161
+ }
162
+ /** What `prepareCall` binds: exact model metadata plus one-generation dispatch. */
163
+ export interface PreparedAdapterCall {
164
+ readonly model: LlmResolvedModelInfo;
165
+ readonly stream: (options: GenerateOptions) => AsyncIterable<StreamChunk>;
166
+ }
167
+ /**
168
+ * The adapter face `ctx.llm.registerAdapter()` consumes.
169
+ *
170
+ * The list is the full public surface of the harness `LlmAdapter` base class,
171
+ * including the members that only have defaults there. `LlmRuntime` calls
172
+ * `providerRetryPolicy` and `imageRequestPricing` on every dispatch, so a
173
+ * duck-typed adapter that omits them throws on the first registration rather
174
+ * than falling back to the base-class default.
175
+ */
176
+ export interface LlmAdapterLike {
177
+ providerInfo(provider: string): LlmProviderInfo;
178
+ providerRetryPolicy(provider: string): unknown;
179
+ imageRequestPricing(provider: string, model: string): unknown;
180
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
181
+ resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
182
+ prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
183
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
184
+ }
185
+ /** The `ctx.llm` seam, narrowed to the one call this plugin makes. */
186
+ interface LlmServiceLike {
187
+ registerAdapter(providers: string[], adapter: LlmAdapterLike): Disposable;
188
+ }
189
+ /** The host context slice this plugin touches. */
190
+ export interface HostContext {
191
+ readonly llm: LlmServiceLike;
192
+ readonly logger: {
193
+ info(message: unknown): void;
194
+ warn(message: unknown): void;
195
+ };
196
+ /**
197
+ * Subscribe to a host event. Optional: a bare test host may omit it, in which
198
+ * case no cached catalog is dropped.
199
+ */
200
+ on?(event: 'loader/volatile-update', listener: () => void): unknown;
201
+ }
202
+ export {};
@@ -0,0 +1,161 @@
1
+ /**
2
+ * dsh-google-vertex: use Google-hosted models from Vertex AI inside DeepSeek
3
+ * Harness, authenticated with a service-account file.
4
+ *
5
+ * Two capabilities, one configuration row: an `ctx.llm` provider adapter for
6
+ * Vertex's Anthropic publisher endpoint (Claude) and one for its Google
7
+ * publisher endpoint (Gemini). Registering them makes both routes selectable in
8
+ * the Web client's model picker, because `buildModelCatalog()` enumerates
9
+ * `ctx.llm.listProviders()` and asks each adapter for `listModels()` /
10
+ * `resolveModel()`.
11
+ *
12
+ * A third: the `google-vertex` settings namespace. The Gemini adapter discovers
13
+ * its catalog at runtime behind a five-minute cache, and a browser half has no
14
+ * other way to reach this process, so the namespace's one write (the Refresh
15
+ * control on this plugin's row page under Plugins) is what drops that cache
16
+ * on demand.
17
+ *
18
+ * The credential is the service-account JSON itself: a path in configuration,
19
+ * or `GOOGLE_APPLICATION_CREDENTIALS` in the launch environment. Nothing is
20
+ * copied into the harness credential store, and the file is read once at mount
21
+ * so a typo fails loudly instead of on the first message.
22
+ *
23
+ * @module dsh-google-vertex
24
+ */
25
+ import type { Volatile } from '@deepseek-ai/cordis';
26
+ import Schema from '@deepseek-ai/schemastery';
27
+ import type { VertexAnthropicConfig, VertexModel } from './adapter.ts';
28
+ import type { GeminiAdapterConfig } from './gemini_adapter.ts';
29
+ import type { HostContext } from './host.ts';
30
+ /** Plugin name as it appears in the loader. */
31
+ export declare const name = "google-vertex";
32
+ /** The `ctx.llm` route serving Google-hosted Anthropic (Claude) models. */
33
+ export declare const PROVIDER = "google-vertex-anthropic";
34
+ /** The `ctx.llm` route serving Gemini models. */
35
+ export declare const GEMINI_PROVIDER = "google-vertex-gemini";
36
+ /**
37
+ * Settings namespace the browser half's card edits: the join key between the
38
+ * two halves. The card registers into `plugins.row.config` under this namespace,
39
+ * and the settings tab pairs the two without knowing what the namespace means.
40
+ */
41
+ export declare const GOOGLE_VERTEX_SETTINGS_NAMESPACE = "google-vertex";
42
+ /** The one service this plugin needs mounted. */
43
+ export declare const inject: string[];
44
+ /**
45
+ * Claude models Vertex serves, in picker order. Ids are the provider's own
46
+ * aliases, which track the newest dated release of each family and need no
47
+ * edit when Vertex promotes one; the catalog is overridable from configuration
48
+ * for a deployment that pins dated versions or uses a different listing.
49
+ */
50
+ export declare const DEFAULT_MODELS: readonly VertexModel[];
51
+ /** Context capacity reported for every Claude model; every listed family serves 200k. */
52
+ export declare const DEFAULT_CONTEXT_WINDOW = 200000;
53
+ /**
54
+ * Output cap used when a caller omits one. Well under the models' 64k ceiling:
55
+ * an agent turn rarely needs more, and the harness enforces its own budget.
56
+ */
57
+ export declare const DEFAULT_MAX_TOKENS = 32000;
58
+ /**
59
+ * Configuration received by the plugin: the row as the exported schema emits
60
+ * it, so the volatile stamp arrives as a live reference rather than a string.
61
+ *
62
+ * A profile patch supplies `Options`; `resolveConfig` normalizes one into the
63
+ * adapter configuration.
64
+ */
65
+ export interface Config {
66
+ /** Service-account JSON path, `~` allowed; defaults to `GOOGLE_APPLICATION_CREDENTIALS`. */
67
+ readonly serviceAccountFile?: string;
68
+ /**
69
+ * Project id; defaults to `GOOGLE_CLOUD_PROJECT`, then `GCLOUD_PROJECT`, then
70
+ * the credentials file's own `project_id`.
71
+ */
72
+ readonly project?: string;
73
+ /** Region, or `global` (the default); defaults to `GOOGLE_CLOUD_LOCATION`. */
74
+ readonly location: string;
75
+ /** Claude model ids to advertise, replacing the built-in catalog. */
76
+ readonly models: string[];
77
+ /** Gemini model ids to advertise, replacing the built-in catalog. */
78
+ readonly geminiModels: string[];
79
+ /**
80
+ * Claude context window reported for every model; defaults to 200000. The
81
+ * Gemini route reports {@link DEFAULT_GEMINI_CONTEXT_WINDOW} for every model
82
+ * it serves, which is why this key has no Gemini counterpart.
83
+ */
84
+ readonly contextWindow: number;
85
+ /** Claude output cap applied when a caller omits one; defaults to 32000. */
86
+ readonly maxTokens: number;
87
+ /**
88
+ * Bound on the interval between two stream reads on either route; defaults to
89
+ * 300000. A provider that stops sending is reported as `TIMEOUT` instead of
90
+ * holding the turn open forever.
91
+ */
92
+ readonly streamIdleTimeoutMs: number;
93
+ /**
94
+ * Stamp written by the Refresh control; absent until the first manual refresh.
95
+ * Volatile in the schema, so the settings document (which accepts only
96
+ * volatile fields) commits the write into this running reference and its
97
+ * `loader/volatile-update` drops both cached catalogs. The host never reads
98
+ * the value; the write itself is the signal.
99
+ */
100
+ readonly revalidatedAt: Volatile<string | undefined>;
101
+ }
102
+ /** Raw row values accepted from a profile patch, with live references unwrapped. */
103
+ export type Options = {
104
+ [K in keyof Config]?: Config[K] extends Volatile<infer T> ? T : Config[K];
105
+ };
106
+ /**
107
+ * Row schema: defaults live here, so a deployment only states what it changes.
108
+ *
109
+ * `serviceAccountFile`, `project`, `models`, and `geminiModels` carry no
110
+ * `.default()`: the first two fall back to the environment, and an omitted
111
+ * catalog materializes empty, which `resolveConfig` treats exactly like an
112
+ * absent one and replaces with the built-in list.
113
+ *
114
+ * `revalidatedAt` is volatile (the only kind of field the settings document
115
+ * accepts) and carries no default: absence means "never refreshed manually".
116
+ */
117
+ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
118
+ serviceAccountFile: Schema<string, string, "plain">;
119
+ project: Schema<string, string, "plain">;
120
+ location: Schema<string, string, "defined">;
121
+ models: Schema<string[], string[], "plain">;
122
+ geminiModels: Schema<string[], string[], "plain">;
123
+ contextWindow: Schema<number, number, "defined">;
124
+ maxTokens: Schema<number, number, "defined">;
125
+ streamIdleTimeoutMs: Schema<number, number, "defined">;
126
+ revalidatedAt: Schema<string, string, "volatile">;
127
+ }>>, Schemastery.ObjectT<NoInfer<{
128
+ serviceAccountFile: Schema<string, string, "plain">;
129
+ project: Schema<string, string, "plain">;
130
+ location: Schema<string, string, "defined">;
131
+ models: Schema<string[], string[], "plain">;
132
+ geminiModels: Schema<string[], string[], "plain">;
133
+ contextWindow: Schema<number, number, "defined">;
134
+ maxTokens: Schema<number, number, "defined">;
135
+ streamIdleTimeoutMs: Schema<number, number, "defined">;
136
+ revalidatedAt: Schema<string, string, "volatile">;
137
+ }>>, "plain">;
138
+ /** Validated configuration plus the file it was read from. */
139
+ interface ResolvedConfig {
140
+ readonly anthropic: VertexAnthropicConfig;
141
+ readonly gemini: GeminiAdapterConfig;
142
+ /** Absolute path of the service-account file, for the mount log line. */
143
+ readonly serviceAccountFile: string;
144
+ }
145
+ /**
146
+ * Validate and normalize one configuration row.
147
+ *
148
+ * Invalid values throw rather than being silently defaulted: a typo'd path or
149
+ * project would otherwise present as an opaque provider error mid-turn.
150
+ * @param config - raw row values, as a profile patch or an unwrapped row.
151
+ * @param env - environment consulted for the credential and region defaults.
152
+ * @returns the resolved adapter configuration.
153
+ */
154
+ export declare function resolveConfig(config?: Options, env?: NodeJS.ProcessEnv): ResolvedConfig;
155
+ /**
156
+ * Mount both adapters.
157
+ * @param ctx - host context; `ctx.llm` must be mounted (`inject` guarantees it).
158
+ * @param config - this plugin's row, as the schema emits it.
159
+ */
160
+ export declare function apply(ctx: HostContext, config: Config): void;
161
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Helpers the two publisher wire translators (Anthropic and Gemini) share:
3
+ * tool-argument parsing, system-text collection, and usage-counter merging.
4
+ *
5
+ * @module dsh-google-vertex/wire-shared
6
+ */
7
+ import type { GenerateOptions } from './host.ts';
8
+ /** Parse a model-produced arguments string into the object the provider requires. */
9
+ export declare function toolInput(argumentsJson: string): Record<string, unknown>;
10
+ /** System-role text the request carries, in assembly order. */
11
+ export declare function systemParts(options: GenerateOptions): string[];
12
+ /**
13
+ * Read the named counters out of one usage payload, keeping only the ones the
14
+ * provider actually sent.
15
+ * @param raw - the payload's usage member.
16
+ * @param names - the counter names this route reads.
17
+ * @returns only the present, well-formed counters.
18
+ */
19
+ export declare function takeCounters(raw: unknown, names: readonly string[]): Record<string, number>;