@kindgi/adapter-model-gemini 0.1.4-rc.5 → 0.1.5-rc.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.
- package/README.md +7 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +11 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +107 -22
- package/dist/provider.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/provider.ts +126 -35
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ One non-streaming `models.generateContent` call per `invoke`:
|
|
|
23
23
|
- **Thinking parts** (`thought: true`) are left out of the message text. Thinking tokens count as completion tokens, since they bill as output.
|
|
24
24
|
- **Structured output.** `structuredOutput` becomes `responseMimeType: 'application/json'` plus `responseJsonSchema`.
|
|
25
25
|
- `temperature`, `maxOutputTokens` (the call's, else the model's `ModelInfo.maxOutputTokens`) and `abortSignal` are passed through.
|
|
26
|
+
- **Retries.** A call that fails with a transient status (408, 429, 500, 502, 503, 504) is retried: three attempts in all, backing off from a second, with jitter. The `@google/genai` client retries only when it's told to, and the adapter tells it. A failed connection isn't retried. The result's `attempts` counts every attempt; a call that still fails throws the last error, and `attemptsOf(error)` (`@kindgi/capabilities/attempts`) counts them. An injected `client` keeps its own settings.
|
|
26
27
|
- **`finishReason`:**
|
|
27
28
|
- a response with function calls → `tool-use`;
|
|
28
29
|
- `STOP` or unspecified → `stop`;
|
|
@@ -37,7 +38,7 @@ One non-streaming `models.generateContent` call per `invoke`:
|
|
|
37
38
|
- uncached prompt tokens at `promptUsdPer1kTokens`;
|
|
38
39
|
- cached prompt tokens at `promptUsdPer1kTokens × cachedPromptMultiplier` (default `0.25`, Gemini 2.5's implicit-cache discount);
|
|
39
40
|
- completion tokens, thinking included, at `completionUsdPer1kTokens`;
|
|
40
|
-
- `longContext: { thresholdTokens, promptUsdPer1kTokens, completionUsdPer1kTokens }`: a call whose prompt exceeds the threshold bills entirely at those rates
|
|
41
|
+
- `longContext: { thresholdTokens, promptUsdPer1kTokens, completionUsdPer1kTokens }`: a call whose prompt exceeds the threshold bills entirely at those rates, for a model whose price rises past a prompt size.
|
|
41
42
|
|
|
42
43
|
`usage.promptTokens` includes cached tokens and built-in tool prompts; `usage.cachedTokens` reports the cached ones when there are any.
|
|
43
44
|
|
|
@@ -54,6 +55,7 @@ One non-streaming `models.generateContent` call per `invoke`:
|
|
|
54
55
|
- `metadata.region` as the location (`unspecified` means `global`);
|
|
55
56
|
- an optional `secret_ref` holding a service-account key.
|
|
56
57
|
- **`vertexTarget(input)`**: the project and location a registration names, as the factory reads them.
|
|
58
|
+
- **`geminiAdapterEntry`**: the `AdapterFactoryEntry` a runtime registers, `geminiAdapterFactory` plus **`geminiCheckConfig(input)`**. The check reports what the factory would throw on (`adapter_config.api`, `secret_ref` on the Developer API, `adapter_config.project`, `metadata.region`) with the factory's own messages, without building anything.
|
|
57
59
|
- **Cost helpers**: `computeCostUsd(usage, rates)`, `toFrameworkUsage(usageMetadata)`, `GeminiCostRates`, `GeminiModelInfo`, `DEFAULT_CACHED_PROMPT_MULTIPLIER`.
|
|
58
60
|
- **Translation helpers**: `toGeminiRequest(messages)`, `toGeminiFunctions(tools)`, `fromGeminiResponse(response)`, `mapFinishReason(reason)`, `checkFunctionName(name)`.
|
|
59
61
|
|
|
@@ -69,27 +71,19 @@ const gemini = createGeminiProvider({
|
|
|
69
71
|
region: 'global',
|
|
70
72
|
models: [
|
|
71
73
|
{
|
|
72
|
-
name: 'gemini-
|
|
74
|
+
name: 'gemini-3.8-flash',
|
|
73
75
|
contextWindow: 1_048_576,
|
|
74
|
-
features: ['tool-use'],
|
|
76
|
+
features: ['tool-use', 'structured-output', 'long-context'],
|
|
75
77
|
maxOutputTokens: 8_192,
|
|
76
78
|
// Rates from Google's published pricing.
|
|
77
|
-
cost: {
|
|
78
|
-
promptUsdPer1kTokens: 0.00125,
|
|
79
|
-
completionUsdPer1kTokens: 0.01,
|
|
80
|
-
longContext: {
|
|
81
|
-
thresholdTokens: 200_000,
|
|
82
|
-
promptUsdPer1kTokens: 0.0025,
|
|
83
|
-
completionUsdPer1kTokens: 0.015,
|
|
84
|
-
},
|
|
85
|
-
},
|
|
79
|
+
cost: { promptUsdPer1kTokens: 0.00075, completionUsdPer1kTokens: 0.00375 },
|
|
86
80
|
},
|
|
87
81
|
],
|
|
88
82
|
},
|
|
89
83
|
});
|
|
90
84
|
|
|
91
85
|
const result = await gemini.invoke({
|
|
92
|
-
model: 'gemini-
|
|
86
|
+
model: 'gemini-3.8-flash',
|
|
93
87
|
messages: [{ role: 'user', content: 'Summarise the grievance in one sentence.' }],
|
|
94
88
|
});
|
|
95
89
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { GEMINI_ADAPTER_ID, createGeminiProvider, geminiAdapterFactory, vertexTarget, } from './provider.js';
|
|
1
|
+
export { GEMINI_ADAPTER_ID, createGeminiProvider, geminiAdapterEntry, geminiAdapterFactory, geminiCheckConfig, vertexTarget, } from './provider.js';
|
|
2
2
|
export type { GeminiClient, GeminiProviderOptions } from './provider.js';
|
|
3
3
|
export { DEFAULT_CACHED_PROMPT_MULTIPLIER, computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
4
4
|
export type { GeminiCostRates, GeminiModelInfo } from './cost.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,gCAAgC,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC/F,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,gCAAgC,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC/F,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
-
export { GEMINI_ADAPTER_ID, createGeminiProvider, geminiAdapterFactory, vertexTarget, } from './provider.js';
|
|
3
|
+
export { GEMINI_ADAPTER_ID, createGeminiProvider, geminiAdapterEntry, geminiAdapterFactory, geminiCheckConfig, vertexTarget, } from './provider.js';
|
|
4
4
|
export { DEFAULT_CACHED_PROMPT_MULTIPLIER, computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
5
5
|
export { checkFunctionName, fromGeminiResponse, mapFinishReason, toGeminiFunctions, toGeminiRequest, } from './translate.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,gCAAgC,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE/F,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,GACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,gCAAgC,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE/F,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type GenerateContentParameters, type GenerateContentResponse } from '@google/genai';
|
|
2
|
-
import type { AdapterFactory, ModelProvider, ProviderMetadata } from '@kindgi/capabilities';
|
|
2
|
+
import type { AdapterConfigCheckInput, AdapterConfigProblem, AdapterFactory, AdapterFactoryEntry, ModelProvider, ProviderMetadata } from '@kindgi/capabilities';
|
|
3
3
|
import { type GeminiModelInfo } from './cost.js';
|
|
4
4
|
/** The adapter id servers register `geminiAdapterFactory` under. */
|
|
5
5
|
export declare const GEMINI_ADAPTER_ID = "@kindgi/adapter-model-gemini";
|
|
@@ -73,6 +73,16 @@ export declare function parseServiceAccountKey(key: string): Record<string, stri
|
|
|
73
73
|
* - `secret_ref` (required) — a Gemini API key from Google AI Studio.
|
|
74
74
|
*/
|
|
75
75
|
export declare const geminiAdapterFactory: AdapterFactory;
|
|
76
|
+
/**
|
|
77
|
+
* What's wrong with a registration for this adapter, read without building
|
|
78
|
+
* it (`AdapterFactoryEntry.checkConfig`): its API, its Gemini API key on
|
|
79
|
+
* the Developer API, its Vertex project and location. Each problem's
|
|
80
|
+
* message is the error the factory throws for it: both read the
|
|
81
|
+
* registration through the same functions.
|
|
82
|
+
*/
|
|
83
|
+
export declare function geminiCheckConfig(input: AdapterConfigCheckInput): readonly AdapterConfigProblem[];
|
|
84
|
+
/** The entry a runtime registers: the factory, and its static check. */
|
|
85
|
+
export declare const geminiAdapterEntry: AdapterFactoryEntry;
|
|
76
86
|
/** The Vertex project and location a registration names (see `geminiAdapterFactory`). */
|
|
77
87
|
export declare function vertexTarget(input: Parameters<AdapterFactory>[0]): NonNullable<GeminiProviderOptions['vertex']>;
|
|
78
88
|
//# sourceMappingURL=provider.d.ts.map
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAG7B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EAInB,aAAa,EACb,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,KAAK,eAAe,EAAoC,MAAM,WAAW,CAAC;AAGnF,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAEhE,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE;QACf,eAAe,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;KACtF,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG;QACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;KAC7C,CAAC;IACF;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,oGAAoG;IACpG,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAChC;AAaD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa,CAmHlF;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiC1E;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAYlC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,oBAAoB,EAAE,CAQjG;AAED,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,EAAE,mBAKhC,CAAC;AAkCF,yFAAyF;AACzF,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GACnC,WAAW,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAM9C"}
|
package/dist/provider.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
3
|
import { GoogleGenAI, } from '@google/genai';
|
|
4
|
+
import { adapterConfigError, samplingFor } from '@kindgi/capabilities';
|
|
4
5
|
import { createAttemptCounter } from '@kindgi/capabilities/attempts';
|
|
5
6
|
import { computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
6
7
|
import { fromGeminiResponse, toGeminiFunctions, toGeminiRequest } from './translate.js';
|
|
7
8
|
/** The adapter id servers register `geminiAdapterFactory` under. */
|
|
8
9
|
export const GEMINI_ADAPTER_ID = '@kindgi/adapter-model-gemini';
|
|
9
10
|
const CLOUD_PLATFORM_SCOPE = 'https://www.googleapis.com/auth/cloud-platform';
|
|
11
|
+
/**
|
|
12
|
+
* The SDK retries a call only when it's given `retryOptions`: three
|
|
13
|
+
* attempts on a transient status (408, 429, 500, 502, 503, 504), backing
|
|
14
|
+
* off from a second, with jitter, the OpenAI and Anthropic SDKs' default
|
|
15
|
+
* count. Unlike theirs, it doesn't retry a failed connection (Node's
|
|
16
|
+
* `fetch failed`).
|
|
17
|
+
*/
|
|
18
|
+
const RETRY_ATTEMPTS = 3;
|
|
10
19
|
/**
|
|
11
20
|
* A Gemini `ModelProvider`, on Vertex AI (`vertex`) or the Gemini
|
|
12
21
|
* Developer API (`apiKey`). Non-streaming, tool-use enabled: one
|
|
@@ -32,7 +41,10 @@ export function createGeminiProvider(options) {
|
|
|
32
41
|
}
|
|
33
42
|
if (cached?.key === key)
|
|
34
43
|
return cached.client;
|
|
35
|
-
const client = new GoogleGenAI({
|
|
44
|
+
const client = new GoogleGenAI({
|
|
45
|
+
apiKey: key,
|
|
46
|
+
httpOptions: { fetch: attempts.fetch, retryOptions: { attempts: RETRY_ATTEMPTS } },
|
|
47
|
+
});
|
|
36
48
|
cached = { key, client };
|
|
37
49
|
return client;
|
|
38
50
|
}
|
|
@@ -44,7 +56,7 @@ export function createGeminiProvider(options) {
|
|
|
44
56
|
vertexai: true,
|
|
45
57
|
project: vertex.project,
|
|
46
58
|
location: vertex.location,
|
|
47
|
-
httpOptions: { fetch: attempts.fetch },
|
|
59
|
+
httpOptions: { fetch: attempts.fetch, retryOptions: { attempts: RETRY_ATTEMPTS } },
|
|
48
60
|
...(key !== '' && {
|
|
49
61
|
googleAuthOptions: {
|
|
50
62
|
credentials: parseServiceAccountKey(key),
|
|
@@ -66,6 +78,7 @@ export function createGeminiProvider(options) {
|
|
|
66
78
|
const startedAt = Date.now();
|
|
67
79
|
const { systemInstruction, contents } = toGeminiRequest(input.messages);
|
|
68
80
|
const maxOutputTokens = input.maxOutputTokens ?? model.maxOutputTokens;
|
|
81
|
+
const sampling = samplingFor(model, input);
|
|
69
82
|
const config = {
|
|
70
83
|
...(systemInstruction !== undefined && { systemInstruction }),
|
|
71
84
|
...(input.tools !== undefined &&
|
|
@@ -76,9 +89,19 @@ export function createGeminiProvider(options) {
|
|
|
76
89
|
responseMimeType: 'application/json',
|
|
77
90
|
responseJsonSchema: input.structuredOutput.schema,
|
|
78
91
|
}),
|
|
79
|
-
...(
|
|
92
|
+
...(sampling.temperature !== undefined && { temperature: sampling.temperature }),
|
|
93
|
+
...(input.thinking === 'lowest' &&
|
|
94
|
+
model.thinking !== undefined && {
|
|
95
|
+
// Gemini's thinking levels: `low`, `minimal` (3.8 Flash refuses `minimal`).
|
|
96
|
+
thinkingConfig: { thinkingLevel: model.thinking.lowest.toUpperCase() },
|
|
97
|
+
}),
|
|
80
98
|
...(maxOutputTokens !== undefined && { maxOutputTokens }),
|
|
81
99
|
...(input.abortSignal !== undefined && { abortSignal: input.abortSignal }),
|
|
100
|
+
// A header on this request only, when the caller sets it (the
|
|
101
|
+
// provider's registration opted in); merged into the client's.
|
|
102
|
+
...(input.traceparent !== undefined && {
|
|
103
|
+
httpOptions: { headers: { traceparent: input.traceparent } },
|
|
104
|
+
}),
|
|
82
105
|
};
|
|
83
106
|
const client = await resolveClient();
|
|
84
107
|
const counted = await attempts.count(() => client.models.generateContent({
|
|
@@ -101,6 +124,7 @@ export function createGeminiProvider(options) {
|
|
|
101
124
|
// An injected client sends with its own fetch: nothing was counted.
|
|
102
125
|
...(counted.attempts > 0 && { attempts: counted.attempts }),
|
|
103
126
|
...(response.usageMetadata !== undefined && { rawUsage: { ...response.usageMetadata } }),
|
|
127
|
+
...(sampling.warnings.length > 0 && { warnings: sampling.warnings }),
|
|
104
128
|
};
|
|
105
129
|
},
|
|
106
130
|
};
|
|
@@ -160,34 +184,87 @@ export function parseServiceAccountKey(key) {
|
|
|
160
184
|
*/
|
|
161
185
|
export const geminiAdapterFactory = (input) => {
|
|
162
186
|
const metadata = input.metadata;
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
throw new Error(`${GEMINI_ADAPTER_ID}: provider "${input.metadata.id}" uses the Gemini Developer API (adapter_config.api "developer") and needs secret_ref: its Gemini API key.`);
|
|
167
|
-
}
|
|
187
|
+
const hasSecretRef = input.resolveApiKey !== undefined;
|
|
188
|
+
throwIf(input.metadata.id, apiProblem(input) ?? developerKeyProblem(input, hasSecretRef));
|
|
189
|
+
if (apiOf(input) === 'developer') {
|
|
168
190
|
return createGeminiProvider({ metadata, apiKey: input.resolveApiKey });
|
|
169
191
|
}
|
|
170
|
-
if (api !== 'vertex') {
|
|
171
|
-
throw new Error(`${GEMINI_ADAPTER_ID}: provider "${input.metadata.id}" adapter_config.api must be "vertex" or "developer", got ${JSON.stringify(api)}.`);
|
|
172
|
-
}
|
|
173
192
|
return createGeminiProvider({
|
|
174
193
|
metadata,
|
|
175
194
|
vertex: vertexTarget(input),
|
|
176
195
|
...(input.resolveApiKey !== undefined && { credentials: input.resolveApiKey }),
|
|
177
196
|
});
|
|
178
197
|
};
|
|
198
|
+
/**
|
|
199
|
+
* What's wrong with a registration for this adapter, read without building
|
|
200
|
+
* it (`AdapterFactoryEntry.checkConfig`): its API, its Gemini API key on
|
|
201
|
+
* the Developer API, its Vertex project and location. Each problem's
|
|
202
|
+
* message is the error the factory throws for it: both read the
|
|
203
|
+
* registration through the same functions.
|
|
204
|
+
*/
|
|
205
|
+
export function geminiCheckConfig(input) {
|
|
206
|
+
const api = apiProblem(input);
|
|
207
|
+
if (api !== undefined)
|
|
208
|
+
return [api];
|
|
209
|
+
if (apiOf(input) === 'developer') {
|
|
210
|
+
const key = developerKeyProblem(input, input.hasSecretRef);
|
|
211
|
+
return key !== undefined ? [key] : [];
|
|
212
|
+
}
|
|
213
|
+
return vertexTargetProblems(input);
|
|
214
|
+
}
|
|
215
|
+
/** The entry a runtime registers: the factory, and its static check. */
|
|
216
|
+
export const geminiAdapterEntry = {
|
|
217
|
+
adapterId: GEMINI_ADAPTER_ID,
|
|
218
|
+
capabilityKind: 'llm-inference',
|
|
219
|
+
factory: geminiAdapterFactory,
|
|
220
|
+
checkConfig: geminiCheckConfig,
|
|
221
|
+
};
|
|
222
|
+
function throwIf(providerId, problem) {
|
|
223
|
+
if (problem !== undefined)
|
|
224
|
+
throw adapterConfigError(GEMINI_ADAPTER_ID, providerId, problem);
|
|
225
|
+
}
|
|
226
|
+
function apiOf(input) {
|
|
227
|
+
return input.config?.api ?? 'vertex';
|
|
228
|
+
}
|
|
229
|
+
function apiProblem(input) {
|
|
230
|
+
const api = apiOf(input);
|
|
231
|
+
if (api === 'vertex' || api === 'developer')
|
|
232
|
+
return undefined;
|
|
233
|
+
return {
|
|
234
|
+
path: '/adapter_config/api',
|
|
235
|
+
message: `adapter_config.api must be "vertex" or "developer", got ${JSON.stringify(api)}.`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function developerKeyProblem(input, hasSecretRef) {
|
|
239
|
+
if (apiOf(input) !== 'developer' || hasSecretRef)
|
|
240
|
+
return undefined;
|
|
241
|
+
return {
|
|
242
|
+
path: '/secret_ref',
|
|
243
|
+
message: 'uses the Gemini Developer API (adapter_config.api "developer") and needs secret_ref: its Gemini API key.',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
179
246
|
/** The Vertex project and location a registration names (see `geminiAdapterFactory`). */
|
|
180
247
|
export function vertexTarget(input) {
|
|
248
|
+
throwIf(input.metadata.id, vertexTargetProblems(input)[0]);
|
|
249
|
+
return {
|
|
250
|
+
project: input.config?.project,
|
|
251
|
+
location: locationOf(input),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function locationOf(input) {
|
|
255
|
+
return input.metadata.region === 'unspecified' ? 'global' : input.metadata.region;
|
|
256
|
+
}
|
|
257
|
+
function vertexTargetProblems(input) {
|
|
181
258
|
const project = input.config?.project;
|
|
182
259
|
if (typeof project !== 'string' || project.length === 0) {
|
|
183
|
-
|
|
260
|
+
return [
|
|
261
|
+
{
|
|
262
|
+
path: '/adapter_config/project',
|
|
263
|
+
message: 'needs adapter_config.project (the Vertex AI project).',
|
|
264
|
+
},
|
|
265
|
+
];
|
|
184
266
|
}
|
|
185
|
-
|
|
186
|
-
project,
|
|
187
|
-
location: input.metadata.region === 'unspecified' ? 'global' : input.metadata.region,
|
|
188
|
-
};
|
|
189
|
-
checkVertexTarget(input.metadata.id, target);
|
|
190
|
-
return target;
|
|
267
|
+
return vertexTargetProblemsOf({ project, location: locationOf(input) });
|
|
191
268
|
}
|
|
192
269
|
/** A provider names exactly one target: Vertex AI, or a Gemini API key. */
|
|
193
270
|
function checkTarget(providerId, options) {
|
|
@@ -195,7 +272,7 @@ function checkTarget(providerId, options) {
|
|
|
195
272
|
throw new Error(`${GEMINI_ADAPTER_ID}: provider "${providerId}" needs exactly one of vertex (a Vertex AI project) or apiKey (a Gemini Developer API key).`);
|
|
196
273
|
}
|
|
197
274
|
if (options.vertex !== undefined)
|
|
198
|
-
|
|
275
|
+
throwIf(providerId, vertexTargetProblemsOf(options.vertex)[0]);
|
|
199
276
|
}
|
|
200
277
|
/**
|
|
201
278
|
* The SDK puts the project in the request path and builds the hostname
|
|
@@ -203,13 +280,21 @@ function checkTarget(providerId, options) {
|
|
|
203
280
|
* anything but a project id and one DNS label could move the request,
|
|
204
281
|
* with the server's credentials, to another host.
|
|
205
282
|
*/
|
|
206
|
-
function
|
|
283
|
+
function vertexTargetProblemsOf(vertex) {
|
|
284
|
+
const problems = [];
|
|
207
285
|
if (!VERTEX_PROJECT.test(vertex.project)) {
|
|
208
|
-
|
|
286
|
+
problems.push({
|
|
287
|
+
path: '/adapter_config/project',
|
|
288
|
+
message: 'adapter_config.project must be a Google Cloud project id or number.',
|
|
289
|
+
});
|
|
209
290
|
}
|
|
210
291
|
if (!VERTEX_LOCATION.test(vertex.location)) {
|
|
211
|
-
|
|
292
|
+
problems.push({
|
|
293
|
+
path: '/metadata/region',
|
|
294
|
+
message: 'metadata.region must be a Vertex AI location (one DNS label, e.g. us-central1).',
|
|
295
|
+
});
|
|
212
296
|
}
|
|
297
|
+
return problems;
|
|
213
298
|
}
|
|
214
299
|
/** A Google Cloud project id (6–30: lowercase, digits, hyphens) or project number. */
|
|
215
300
|
const VERTEX_PROJECT = /^(?:[a-z][a-z0-9-]{4,28}[a-z0-9]|[0-9]{1,20})$/;
|
package/dist/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,EAIL,WAAW,
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/adapter-model-gemini",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5-rc.0",
|
|
4
4
|
"description": "Gemini ModelProvider for @kindgi/capabilities, on Vertex AI. Wraps @google/genai with the framework's provider-neutral interface: message-trail ↔ generateContent translation, function calling with thought signatures, token and cost accounting including thinking tokens, and Google Application Default Credentials (or a service-account key from the secret store). Non-streaming, tool-use enabled.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@kindgi/capabilities": "0.1.
|
|
31
|
+
"@kindgi/capabilities": "0.1.5-rc.0",
|
|
32
32
|
"@google/genai": "^2.24.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
package/src/index.ts
CHANGED
package/src/provider.ts
CHANGED
|
@@ -6,14 +6,20 @@ import {
|
|
|
6
6
|
type GenerateContentParameters,
|
|
7
7
|
type GenerateContentResponse,
|
|
8
8
|
GoogleGenAI,
|
|
9
|
+
type ThinkingLevel,
|
|
9
10
|
} from '@google/genai';
|
|
10
11
|
import type {
|
|
12
|
+
AdapterConfigCheckInput,
|
|
13
|
+
AdapterConfigProblem,
|
|
11
14
|
AdapterFactory,
|
|
15
|
+
AdapterFactoryEntry,
|
|
16
|
+
AdapterFactoryInput,
|
|
12
17
|
ModelCallInput,
|
|
13
18
|
ModelCallResult,
|
|
14
19
|
ModelProvider,
|
|
15
20
|
ProviderMetadata,
|
|
16
21
|
} from '@kindgi/capabilities';
|
|
22
|
+
import { adapterConfigError, samplingFor } from '@kindgi/capabilities';
|
|
17
23
|
import { createAttemptCounter } from '@kindgi/capabilities/attempts';
|
|
18
24
|
|
|
19
25
|
import { type GeminiModelInfo, computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
@@ -62,6 +68,15 @@ export interface GeminiProviderOptions {
|
|
|
62
68
|
|
|
63
69
|
const CLOUD_PLATFORM_SCOPE = 'https://www.googleapis.com/auth/cloud-platform';
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* The SDK retries a call only when it's given `retryOptions`: three
|
|
73
|
+
* attempts on a transient status (408, 429, 500, 502, 503, 504), backing
|
|
74
|
+
* off from a second, with jitter, the OpenAI and Anthropic SDKs' default
|
|
75
|
+
* count. Unlike theirs, it doesn't retry a failed connection (Node's
|
|
76
|
+
* `fetch failed`).
|
|
77
|
+
*/
|
|
78
|
+
const RETRY_ATTEMPTS = 3;
|
|
79
|
+
|
|
65
80
|
/**
|
|
66
81
|
* A Gemini `ModelProvider`, on Vertex AI (`vertex`) or the Gemini
|
|
67
82
|
* Developer API (`apiKey`). Non-streaming, tool-use enabled: one
|
|
@@ -89,7 +104,10 @@ export function createGeminiProvider(options: GeminiProviderOptions): ModelProvi
|
|
|
89
104
|
);
|
|
90
105
|
}
|
|
91
106
|
if (cached?.key === key) return cached.client;
|
|
92
|
-
const client = new GoogleGenAI({
|
|
107
|
+
const client = new GoogleGenAI({
|
|
108
|
+
apiKey: key,
|
|
109
|
+
httpOptions: { fetch: attempts.fetch, retryOptions: { attempts: RETRY_ATTEMPTS } },
|
|
110
|
+
});
|
|
93
111
|
cached = { key, client };
|
|
94
112
|
return client;
|
|
95
113
|
}
|
|
@@ -100,7 +118,7 @@ export function createGeminiProvider(options: GeminiProviderOptions): ModelProvi
|
|
|
100
118
|
vertexai: true,
|
|
101
119
|
project: vertex.project,
|
|
102
120
|
location: vertex.location,
|
|
103
|
-
httpOptions: { fetch: attempts.fetch },
|
|
121
|
+
httpOptions: { fetch: attempts.fetch, retryOptions: { attempts: RETRY_ATTEMPTS } },
|
|
104
122
|
...(key !== '' && {
|
|
105
123
|
googleAuthOptions: {
|
|
106
124
|
credentials: parseServiceAccountKey(key),
|
|
@@ -125,6 +143,7 @@ export function createGeminiProvider(options: GeminiProviderOptions): ModelProvi
|
|
|
125
143
|
const startedAt = Date.now();
|
|
126
144
|
const { systemInstruction, contents } = toGeminiRequest(input.messages);
|
|
127
145
|
const maxOutputTokens = input.maxOutputTokens ?? model.maxOutputTokens;
|
|
146
|
+
const sampling = samplingFor(model, input);
|
|
128
147
|
const config: GenerateContentConfig = {
|
|
129
148
|
...(systemInstruction !== undefined && { systemInstruction }),
|
|
130
149
|
...(input.tools !== undefined &&
|
|
@@ -135,9 +154,19 @@ export function createGeminiProvider(options: GeminiProviderOptions): ModelProvi
|
|
|
135
154
|
responseMimeType: 'application/json',
|
|
136
155
|
responseJsonSchema: input.structuredOutput.schema,
|
|
137
156
|
}),
|
|
138
|
-
...(
|
|
157
|
+
...(sampling.temperature !== undefined && { temperature: sampling.temperature }),
|
|
158
|
+
...(input.thinking === 'lowest' &&
|
|
159
|
+
model.thinking !== undefined && {
|
|
160
|
+
// Gemini's thinking levels: `low`, `minimal` (3.8 Flash refuses `minimal`).
|
|
161
|
+
thinkingConfig: { thinkingLevel: model.thinking.lowest.toUpperCase() as ThinkingLevel },
|
|
162
|
+
}),
|
|
139
163
|
...(maxOutputTokens !== undefined && { maxOutputTokens }),
|
|
140
164
|
...(input.abortSignal !== undefined && { abortSignal: input.abortSignal }),
|
|
165
|
+
// A header on this request only, when the caller sets it (the
|
|
166
|
+
// provider's registration opted in); merged into the client's.
|
|
167
|
+
...(input.traceparent !== undefined && {
|
|
168
|
+
httpOptions: { headers: { traceparent: input.traceparent } },
|
|
169
|
+
}),
|
|
141
170
|
};
|
|
142
171
|
|
|
143
172
|
const client = await resolveClient();
|
|
@@ -164,6 +193,7 @@ export function createGeminiProvider(options: GeminiProviderOptions): ModelProvi
|
|
|
164
193
|
// An injected client sends with its own fetch: nothing was counted.
|
|
165
194
|
...(counted.attempts > 0 && { attempts: counted.attempts }),
|
|
166
195
|
...(response.usageMetadata !== undefined && { rawUsage: { ...response.usageMetadata } }),
|
|
196
|
+
...(sampling.warnings.length > 0 && { warnings: sampling.warnings }),
|
|
167
197
|
};
|
|
168
198
|
},
|
|
169
199
|
};
|
|
@@ -229,19 +259,10 @@ export function parseServiceAccountKey(key: string): Record<string, string> {
|
|
|
229
259
|
*/
|
|
230
260
|
export const geminiAdapterFactory: AdapterFactory = (input) => {
|
|
231
261
|
const metadata = input.metadata as GeminiProviderOptions['metadata'];
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
`${GEMINI_ADAPTER_ID}: provider "${input.metadata.id}" uses the Gemini Developer API (adapter_config.api "developer") and needs secret_ref: its Gemini API key.`,
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
return createGeminiProvider({ metadata, apiKey: input.resolveApiKey });
|
|
240
|
-
}
|
|
241
|
-
if (api !== 'vertex') {
|
|
242
|
-
throw new Error(
|
|
243
|
-
`${GEMINI_ADAPTER_ID}: provider "${input.metadata.id}" adapter_config.api must be "vertex" or "developer", got ${JSON.stringify(api)}.`,
|
|
244
|
-
);
|
|
262
|
+
const hasSecretRef = input.resolveApiKey !== undefined;
|
|
263
|
+
throwIf(input.metadata.id, apiProblem(input) ?? developerKeyProblem(input, hasSecretRef));
|
|
264
|
+
if (apiOf(input) === 'developer') {
|
|
265
|
+
return createGeminiProvider({ metadata, apiKey: input.resolveApiKey as () => Promise<string> });
|
|
245
266
|
}
|
|
246
267
|
return createGeminiProvider({
|
|
247
268
|
metadata,
|
|
@@ -250,22 +271,89 @@ export const geminiAdapterFactory: AdapterFactory = (input) => {
|
|
|
250
271
|
});
|
|
251
272
|
};
|
|
252
273
|
|
|
274
|
+
/**
|
|
275
|
+
* What's wrong with a registration for this adapter, read without building
|
|
276
|
+
* it (`AdapterFactoryEntry.checkConfig`): its API, its Gemini API key on
|
|
277
|
+
* the Developer API, its Vertex project and location. Each problem's
|
|
278
|
+
* message is the error the factory throws for it: both read the
|
|
279
|
+
* registration through the same functions.
|
|
280
|
+
*/
|
|
281
|
+
export function geminiCheckConfig(input: AdapterConfigCheckInput): readonly AdapterConfigProblem[] {
|
|
282
|
+
const api = apiProblem(input);
|
|
283
|
+
if (api !== undefined) return [api];
|
|
284
|
+
if (apiOf(input) === 'developer') {
|
|
285
|
+
const key = developerKeyProblem(input, input.hasSecretRef);
|
|
286
|
+
return key !== undefined ? [key] : [];
|
|
287
|
+
}
|
|
288
|
+
return vertexTargetProblems(input);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** The entry a runtime registers: the factory, and its static check. */
|
|
292
|
+
export const geminiAdapterEntry: AdapterFactoryEntry = {
|
|
293
|
+
adapterId: GEMINI_ADAPTER_ID,
|
|
294
|
+
capabilityKind: 'llm-inference',
|
|
295
|
+
factory: geminiAdapterFactory,
|
|
296
|
+
checkConfig: geminiCheckConfig,
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/** What the checks read of a registration. */
|
|
300
|
+
type Registration = Pick<AdapterFactoryInput, 'metadata' | 'config'>;
|
|
301
|
+
|
|
302
|
+
function throwIf(providerId: string, problem: AdapterConfigProblem | undefined): void {
|
|
303
|
+
if (problem !== undefined) throw adapterConfigError(GEMINI_ADAPTER_ID, providerId, problem);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function apiOf(input: Registration): unknown {
|
|
307
|
+
return input.config?.api ?? 'vertex';
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function apiProblem(input: Registration): AdapterConfigProblem | undefined {
|
|
311
|
+
const api = apiOf(input);
|
|
312
|
+
if (api === 'vertex' || api === 'developer') return undefined;
|
|
313
|
+
return {
|
|
314
|
+
path: '/adapter_config/api',
|
|
315
|
+
message: `adapter_config.api must be "vertex" or "developer", got ${JSON.stringify(api)}.`,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function developerKeyProblem(
|
|
320
|
+
input: Registration,
|
|
321
|
+
hasSecretRef: boolean,
|
|
322
|
+
): AdapterConfigProblem | undefined {
|
|
323
|
+
if (apiOf(input) !== 'developer' || hasSecretRef) return undefined;
|
|
324
|
+
return {
|
|
325
|
+
path: '/secret_ref',
|
|
326
|
+
message:
|
|
327
|
+
'uses the Gemini Developer API (adapter_config.api "developer") and needs secret_ref: its Gemini API key.',
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
253
331
|
/** The Vertex project and location a registration names (see `geminiAdapterFactory`). */
|
|
254
332
|
export function vertexTarget(
|
|
255
333
|
input: Parameters<AdapterFactory>[0],
|
|
256
334
|
): NonNullable<GeminiProviderOptions['vertex']> {
|
|
335
|
+
throwIf(input.metadata.id, vertexTargetProblems(input)[0]);
|
|
336
|
+
return {
|
|
337
|
+
project: input.config?.project as string,
|
|
338
|
+
location: locationOf(input),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function locationOf(input: Registration): string {
|
|
343
|
+
return input.metadata.region === 'unspecified' ? 'global' : input.metadata.region;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function vertexTargetProblems(input: Registration): AdapterConfigProblem[] {
|
|
257
347
|
const project = input.config?.project;
|
|
258
348
|
if (typeof project !== 'string' || project.length === 0) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
349
|
+
return [
|
|
350
|
+
{
|
|
351
|
+
path: '/adapter_config/project',
|
|
352
|
+
message: 'needs adapter_config.project (the Vertex AI project).',
|
|
353
|
+
},
|
|
354
|
+
];
|
|
262
355
|
}
|
|
263
|
-
|
|
264
|
-
project,
|
|
265
|
-
location: input.metadata.region === 'unspecified' ? 'global' : input.metadata.region,
|
|
266
|
-
};
|
|
267
|
-
checkVertexTarget(input.metadata.id, target);
|
|
268
|
-
return target;
|
|
356
|
+
return vertexTargetProblemsOf({ project, location: locationOf(input) });
|
|
269
357
|
}
|
|
270
358
|
|
|
271
359
|
/** A provider names exactly one target: Vertex AI, or a Gemini API key. */
|
|
@@ -275,7 +363,7 @@ function checkTarget(providerId: string, options: GeminiProviderOptions): void {
|
|
|
275
363
|
`${GEMINI_ADAPTER_ID}: provider "${providerId}" needs exactly one of vertex (a Vertex AI project) or apiKey (a Gemini Developer API key).`,
|
|
276
364
|
);
|
|
277
365
|
}
|
|
278
|
-
if (options.vertex !== undefined)
|
|
366
|
+
if (options.vertex !== undefined) throwIf(providerId, vertexTargetProblemsOf(options.vertex)[0]);
|
|
279
367
|
}
|
|
280
368
|
|
|
281
369
|
/**
|
|
@@ -284,20 +372,23 @@ function checkTarget(providerId: string, options: GeminiProviderOptions): void {
|
|
|
284
372
|
* anything but a project id and one DNS label could move the request,
|
|
285
373
|
* with the server's credentials, to another host.
|
|
286
374
|
*/
|
|
287
|
-
function
|
|
288
|
-
providerId: string,
|
|
375
|
+
function vertexTargetProblemsOf(
|
|
289
376
|
vertex: NonNullable<GeminiProviderOptions['vertex']>,
|
|
290
|
-
):
|
|
377
|
+
): AdapterConfigProblem[] {
|
|
378
|
+
const problems: AdapterConfigProblem[] = [];
|
|
291
379
|
if (!VERTEX_PROJECT.test(vertex.project)) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
380
|
+
problems.push({
|
|
381
|
+
path: '/adapter_config/project',
|
|
382
|
+
message: 'adapter_config.project must be a Google Cloud project id or number.',
|
|
383
|
+
});
|
|
295
384
|
}
|
|
296
385
|
if (!VERTEX_LOCATION.test(vertex.location)) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
386
|
+
problems.push({
|
|
387
|
+
path: '/metadata/region',
|
|
388
|
+
message: 'metadata.region must be a Vertex AI location (one DNS label, e.g. us-central1).',
|
|
389
|
+
});
|
|
300
390
|
}
|
|
391
|
+
return problems;
|
|
301
392
|
}
|
|
302
393
|
|
|
303
394
|
/** A Google Cloud project id (6–30: lowercase, digits, hyphens) or project number. */
|