@gullabs/any-llm 0.7.0 → 0.8.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 +17 -15
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/skills/any-llm/SKILL.md +216 -27
package/README.md
CHANGED
|
@@ -20,15 +20,14 @@ This installs the core engine, Gemini adapter, and `@google/genai`.
|
|
|
20
20
|
```ts
|
|
21
21
|
import {
|
|
22
22
|
createClient,
|
|
23
|
+
composeProviders,
|
|
23
24
|
defaultGeminiRegistry,
|
|
24
25
|
defineCallSite,
|
|
25
|
-
|
|
26
|
-
geminiPricingSource,
|
|
26
|
+
googleProvider,
|
|
27
27
|
} from '@gullabs/any-llm'
|
|
28
28
|
|
|
29
29
|
const client = createClient({
|
|
30
|
-
|
|
31
|
-
pricingSources: { google: geminiPricingSource() },
|
|
30
|
+
...composeProviders([googleProvider()]),
|
|
32
31
|
})
|
|
33
32
|
|
|
34
33
|
const summarize = defineCallSite({
|
|
@@ -71,17 +70,20 @@ intended. `priority` remains rejected by the library for now.
|
|
|
71
70
|
## Key exports
|
|
72
71
|
|
|
73
72
|
This package re-exports the full public API of `@gullabs/core` and `@gullabs/google` verbatim —
|
|
74
|
-
`createClient`, `
|
|
75
|
-
other named export from both packages. See their
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
73
|
+
`createClient`, `composeProviders`, `defineCallSite`, `googleProvider`, `geminiAdapter`,
|
|
74
|
+
`geminiPricingSource`, `LlmError`, and every other named export from both packages. See their
|
|
75
|
+
READMEs for details:
|
|
76
|
+
|
|
77
|
+
| Export | What it is |
|
|
78
|
+
| --------------------------- | ----------------------------------------------------------- |
|
|
79
|
+
| `createClient(config)` | Wires ports into a `{ generate, runStructured }` client |
|
|
80
|
+
| `composeProviders(plugins)` | Merges `ProviderPlugin`s into `ClientConfig` fields |
|
|
81
|
+
| `defineCallSite(opts)` | Defines a typed, reusable prompt template bound to a model |
|
|
82
|
+
| `googleProvider(opts?)` | The Gemini `ProviderPlugin` factory, from `@gullabs/google` |
|
|
83
|
+
| `geminiAdapter(opts?)` | The Gemini `ProviderAdapter`, from `@gullabs/google` |
|
|
84
|
+
| `geminiPricingSource()` | Built-in Gemini pricing snapshot, from `@gullabs/google` |
|
|
85
|
+
| `LlmError` | Typed error class — always thrown on call failure |
|
|
86
|
+
| `ANY_LLM_VERSION` | This package's version, sourced from `package.json` |
|
|
85
87
|
|
|
86
88
|
Use `@gullabs/core` and `@gullabs/google` directly only when you want modular dependency control.
|
|
87
89
|
|
package/dist/index.cjs
CHANGED
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":";;;;;;;;AAEE,IAAA,OAAA,GAAW,OAAA;;;ACcN,IAAM,eAAA,GAA0B","file":"index.cjs","sourcesContent":["{\n \"name\": \"@gullabs/any-llm\",\n \"version\": \"0.
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":";;;;;;;;AAEE,IAAA,OAAA,GAAW,OAAA;;;ACcN,IAAM,eAAA,GAA0B","file":"index.cjs","sourcesContent":["{\n \"name\": \"@gullabs/any-llm\",\n \"version\": \"0.8.0\",\n \"description\": \"Batteries-included any-llm client for Gemini: engine, adapter, and Google SDK in one install.\",\n \"type\": \"module\",\n \"license\": \"Apache-2.0\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/gullabs/any-llm.git\",\n \"directory\": \"packages/any-llm\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"scripts\": {\n \"build\": \"tsup\"\n },\n \"dependencies\": {\n \"@google/genai\": \"^1.45.0 || ^2\",\n \"@gullabs/core\": \"workspace:*\",\n \"@gullabs/google\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=20.9.0\"\n },\n \"sideEffects\": false,\n \"keywords\": [\n \"llm\",\n \"gemini\",\n \"google-genai\",\n \"ai\",\n \"tokens\",\n \"cost\",\n \"usage\",\n \"observability\",\n \"typescript\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"homepage\": \"https://github.com/gullabs/any-llm/tree/main/packages/any-llm#readme\",\n \"bugs\": \"https://github.com/gullabs/any-llm/issues\"\n}\n","/**\n * @gullabs/any-llm — batteries-included public entrypoint.\n *\n * This package is the default client install path. It re-exports the core\n * engine and Gemini adapter while depending on the Gemini SDK for a one-package\n * setup.\n *\n * @module\n */\n\nexport * from '@gullabs/core'\nexport * from '@gullabs/google'\n\nimport { version } from '../package.json'\n\n/** Library version, sourced from package.json at build time. */\nexport const ANY_LLM_VERSION: string = version\n"]}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":";;;;;;AAEE,IAAA,OAAA,GAAW,OAAA;;;ACcN,IAAM,eAAA,GAA0B","file":"index.js","sourcesContent":["{\n \"name\": \"@gullabs/any-llm\",\n \"version\": \"0.
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/index.ts"],"names":[],"mappings":";;;;;;AAEE,IAAA,OAAA,GAAW,OAAA;;;ACcN,IAAM,eAAA,GAA0B","file":"index.js","sourcesContent":["{\n \"name\": \"@gullabs/any-llm\",\n \"version\": \"0.8.0\",\n \"description\": \"Batteries-included any-llm client for Gemini: engine, adapter, and Google SDK in one install.\",\n \"type\": \"module\",\n \"license\": \"Apache-2.0\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/gullabs/any-llm.git\",\n \"directory\": \"packages/any-llm\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"scripts\": {\n \"build\": \"tsup\"\n },\n \"dependencies\": {\n \"@google/genai\": \"^1.45.0 || ^2\",\n \"@gullabs/core\": \"workspace:*\",\n \"@gullabs/google\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=20.9.0\"\n },\n \"sideEffects\": false,\n \"keywords\": [\n \"llm\",\n \"gemini\",\n \"google-genai\",\n \"ai\",\n \"tokens\",\n \"cost\",\n \"usage\",\n \"observability\",\n \"typescript\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"homepage\": \"https://github.com/gullabs/any-llm/tree/main/packages/any-llm#readme\",\n \"bugs\": \"https://github.com/gullabs/any-llm/issues\"\n}\n","/**\n * @gullabs/any-llm — batteries-included public entrypoint.\n *\n * This package is the default client install path. It re-exports the core\n * engine and Gemini adapter while depending on the Gemini SDK for a one-package\n * setup.\n *\n * @module\n */\n\nexport * from '@gullabs/core'\nexport * from '@gullabs/google'\n\nimport { version } from '../package.json'\n\n/** Library version, sourced from package.json at build time. */\nexport const ANY_LLM_VERSION: string = version\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gullabs/any-llm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Batteries-included any-llm client for Gemini: engine, adapter, and Google SDK in one install.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@google/genai": "^1.45.0 || ^2",
|
|
28
|
-
"@gullabs/core": "0.
|
|
29
|
-
"@gullabs/google": "0.
|
|
28
|
+
"@gullabs/core": "0.8.0",
|
|
29
|
+
"@gullabs/google": "0.8.0"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=20.9.0"
|
package/skills/any-llm/SKILL.md
CHANGED
|
@@ -2,31 +2,42 @@
|
|
|
2
2
|
name: any-llm
|
|
3
3
|
description: >-
|
|
4
4
|
Guidance for writing, reviewing, or debugging TypeScript code that calls
|
|
5
|
-
@gullabs/any-llm, @gullabs/core, or @gullabs/
|
|
6
|
-
Applies when adding a new LLM call site,
|
|
5
|
+
@gullabs/any-llm, @gullabs/core, @gullabs/google, or @gullabs/xai to talk to Gemini
|
|
6
|
+
or xAI Grok models. Applies when adding a new LLM call site, composing provider
|
|
7
|
+
plugins with composeProviders, wiring createClient/generate/runStructured/countTokens,
|
|
7
8
|
defining a defineCallSite prompt template, requesting structured JSON output,
|
|
8
|
-
catching or narrowing an LlmError, configuring reasoning/thinking budgets,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
catching or narrowing an LlmError, configuring reasoning/thinking budgets, wiring a
|
|
10
|
+
UsageSink for cost tracking, augmenting ProviderOptionsMap for a new provider
|
|
11
|
+
package, using GoogleCacheStore for Gemini context caching, or migrating raw
|
|
12
|
+
@google/genai prompts via geminiContentToMessages. Also applies whenever the user
|
|
13
|
+
mentions any-llm, the Gemini adapter, the xAI/Grok adapter, Gemini Flex tier,
|
|
14
|
+
structured-output validation, token counting, or per-call auth for this library.
|
|
15
|
+
Covers the mandatory per-call `{ auth: { apiKey } }` pattern (there is no env-var or
|
|
16
|
+
ambient auth), the caller-owned output-validation contract, the descriptor-owned
|
|
17
|
+
strict model-config boundary (`configSchema` / `configJsonSchema`), the
|
|
18
|
+
provider-qualified `(provider, model)` identity contract, and the reject-don't-map
|
|
19
|
+
error philosophy — the things a developer used to other LLM SDKs would otherwise get
|
|
20
|
+
wrong by default.
|
|
16
21
|
---
|
|
17
22
|
|
|
18
23
|
# any-llm
|
|
19
24
|
|
|
20
|
-
Typed, provider-agnostic-by-design
|
|
21
|
-
|
|
25
|
+
Typed, provider-agnostic-by-design LLM call engine with cost tracking, retries, rate
|
|
26
|
+
limiting, structured output, and per-call observability. Providers are plugged in
|
|
27
|
+
explicitly via `composeProviders` — nothing is auto-wired.
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
Core packages:
|
|
24
30
|
|
|
25
|
-
- `@gullabs/core` — engine (`createClient`), types, errors, `defineCallSite
|
|
26
|
-
|
|
27
|
-
- `@gullabs/
|
|
31
|
+
- `@gullabs/core` — engine (`createClient`), types, errors, `defineCallSite`,
|
|
32
|
+
`composeProviders`.
|
|
33
|
+
- `@gullabs/google` — Gemini + Gemma adapter (`geminiAdapter`) over `@google/genai`.
|
|
34
|
+
- `@gullabs/xai` — xAI Grok adapter (`xaiAdapter`) over the Responses API.
|
|
35
|
+
- `@gullabs/any-llm` — batteries-included: re-exports `@gullabs/core` + `@gullabs/google`
|
|
36
|
+
plus `@google/genai` as a dependency. Does **not** bundle `@gullabs/xai` or any other
|
|
37
|
+
provider package — install those separately and compose them alongside.
|
|
28
38
|
|
|
29
|
-
Install `@gullabs/any-llm` for a one-package setup, or
|
|
39
|
+
Install `@gullabs/any-llm` for a one-package Gemini setup, or `@gullabs/core` plus
|
|
40
|
+
whichever provider package(s) you need (`@gullabs/google`, `@gullabs/xai`, ...) for
|
|
30
41
|
explicit dependency control. Import names are identical either way.
|
|
31
42
|
|
|
32
43
|
## #1 gotcha: auth is per-call, always
|
|
@@ -39,8 +50,7 @@ explicitly. `createClient()` itself takes no credentials.
|
|
|
39
50
|
// WRONG — GenerateOptions.auth is a required field; this will not type-check, and if
|
|
40
51
|
// bypassed with `as any` it throws LlmError({ kind: 'invalid_auth' }) before any I/O.
|
|
41
52
|
const client = createClient({
|
|
42
|
-
|
|
43
|
-
pricingSources: { google: geminiPricingSource() },
|
|
53
|
+
...composeProviders([googleProvider()]),
|
|
44
54
|
})
|
|
45
55
|
await client.generate(request, {} as GenerateOptions)
|
|
46
56
|
|
|
@@ -69,12 +79,12 @@ same bare model id can exist under multiple providers with different config sche
|
|
|
69
79
|
## Quickstart
|
|
70
80
|
|
|
71
81
|
```ts
|
|
72
|
-
import { createClient,
|
|
73
|
-
// (or: from '@gullabs/core'
|
|
82
|
+
import { createClient, composeProviders, googleProvider } from '@gullabs/any-llm'
|
|
83
|
+
// (or: composeProviders from '@gullabs/core', googleProvider from '@gullabs/google',
|
|
84
|
+
// if using modular install)
|
|
74
85
|
|
|
75
86
|
const client = createClient({
|
|
76
|
-
|
|
77
|
-
pricingSources: { google: geminiPricingSource() },
|
|
87
|
+
...composeProviders([googleProvider()]),
|
|
78
88
|
})
|
|
79
89
|
|
|
80
90
|
const result = await client.generate(
|
|
@@ -95,6 +105,104 @@ console.log(result.cost?.microUsd) // integer micro-USD, or null if unpriced
|
|
|
95
105
|
`{ kind: 'text', text }`, `{ kind: 'inline-media', mimeType, data /* raw base64, no data: prefix */ }`,
|
|
96
106
|
and `{ kind: 'file-uri', uri, mimeType }` freely in one `parts` array.
|
|
97
107
|
|
|
108
|
+
## Counting tokens without generating
|
|
109
|
+
|
|
110
|
+
`client.countTokens` is a metadata-only dry run — no generation, no cost, no
|
|
111
|
+
`result.output`. Same `(provider, model)` routing and required `auth` as `generate`;
|
|
112
|
+
throws `LlmError('bad_request')` when the pair is unregistered or the resolved adapter
|
|
113
|
+
doesn't implement token counting (`ProviderAdapter.countTokens` is optional).
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const count = await client.countTokens(
|
|
117
|
+
{
|
|
118
|
+
provider: 'google',
|
|
119
|
+
model: 'gemini-2.5-flash',
|
|
120
|
+
system: 'You are a concise summarizer.',
|
|
121
|
+
messages: [{ role: 'user', parts: [{ kind: 'text', text: 'Hello!' }] }],
|
|
122
|
+
},
|
|
123
|
+
{ auth: { apiKey: myResolvedGeminiKey } },
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
console.log(count.totalTokens) // number
|
|
127
|
+
console.log(count.details) // optional per-category breakdown, e.g. { cached: 128 }
|
|
128
|
+
console.log(count.raw) // provider's raw token-count response, verbatim
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`TokenCountRequest` is deliberately narrower than a generate request — no `config`, no
|
|
132
|
+
`output`, no `providerOptions`; token counting only needs `provider`, `model`,
|
|
133
|
+
`system`, and `messages`.
|
|
134
|
+
|
|
135
|
+
## Composing multiple providers — xAI Grok example
|
|
136
|
+
|
|
137
|
+
`composeProviders` takes any number of plugins; pass every provider a single client
|
|
138
|
+
should route to. `@gullabs/xai`'s `xaiProvider()` follows the identical plugin shape
|
|
139
|
+
as `googleProvider()`:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { createClient, composeProviders } from '@gullabs/core'
|
|
143
|
+
import { googleProvider } from '@gullabs/google'
|
|
144
|
+
import { xaiProvider } from '@gullabs/xai'
|
|
145
|
+
|
|
146
|
+
const client = createClient({
|
|
147
|
+
...composeProviders([googleProvider(), xaiProvider()]),
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const result = await client.generate(
|
|
151
|
+
{
|
|
152
|
+
provider: 'xai',
|
|
153
|
+
model: 'grok-4.5',
|
|
154
|
+
messages: [{ role: 'user', parts: [{ kind: 'text', text: 'Hello, Grok.' }] }],
|
|
155
|
+
config: { reasoning: { effort: 'high' } },
|
|
156
|
+
},
|
|
157
|
+
{ auth: { apiKey: myResolvedXaiKey } },
|
|
158
|
+
)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`grok-4.5`'s `reasoning.effort` admits only `'low' | 'high'` (live-verified) —
|
|
162
|
+
`'none'`, `'medium'`, and `'xhigh'` are rejected by both the live API and
|
|
163
|
+
`Grok45ConfigSchema`. Unlike Gemini, xai has no `serviceTier` concept and no `topK`;
|
|
164
|
+
its config schema is a single strict object with no tier branching.
|
|
165
|
+
|
|
166
|
+
## Migrating raw `@google/genai` prompts
|
|
167
|
+
|
|
168
|
+
`geminiContentToMessages` (from `@gullabs/google`) converts hand-authored
|
|
169
|
+
`@google/genai` `Content[]` into any-llm's normalized `{ system?, messages }` shape.
|
|
170
|
+
Reject-don't-map: a missing/unrecognized `Content.role` (only `'user'` and `'model'`
|
|
171
|
+
are accepted, never inferred), a `systemInstruction` containing anything other than
|
|
172
|
+
plain text parts, or any `Part` sub-field this library can't losslessly represent
|
|
173
|
+
(function calls, executable code, tool results, thought-flagged parts,
|
|
174
|
+
`thoughtSignature`, unknown future fields, etc.) throws `LlmError('bad_request')`
|
|
175
|
+
naming the offending field — nothing is ever silently dropped.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { geminiContentToMessages } from '@gullabs/google'
|
|
179
|
+
import type { Content } from '@google/genai'
|
|
180
|
+
|
|
181
|
+
const contents: Content[] = [
|
|
182
|
+
{
|
|
183
|
+
role: 'user',
|
|
184
|
+
parts: [
|
|
185
|
+
{ text: 'Describe this image.' },
|
|
186
|
+
{ inlineData: { mimeType: 'image/png', data } },
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
{ role: 'model', parts: [{ text: 'A red bicycle leaning against a brick wall.' }] },
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
const { system, messages } = geminiContentToMessages({
|
|
193
|
+
contents,
|
|
194
|
+
systemInstruction: 'You are a concise visual describer.',
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const result = await client.generate(
|
|
198
|
+
{ provider: 'google', model: 'gemini-2.5-pro', system, messages },
|
|
199
|
+
{ auth: { apiKey: myResolvedGeminiKey } },
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`system` is derived only from the explicit `systemInstruction` input — never inferred
|
|
204
|
+
from `contents`.
|
|
205
|
+
|
|
98
206
|
## `defineCallSite` — reusable prompt templates
|
|
99
207
|
|
|
100
208
|
```ts
|
|
@@ -125,7 +233,7 @@ the selected descriptor's strict runtime schema before dispatch.
|
|
|
125
233
|
Treat model config as descriptor-owned:
|
|
126
234
|
|
|
127
235
|
```ts
|
|
128
|
-
import { defaultGeminiRegistry } from '@gullabs/
|
|
236
|
+
import { defaultGeminiRegistry } from '@gullabs/google'
|
|
129
237
|
|
|
130
238
|
const descriptor = defaultGeminiRegistry.resolve('google', 'gemini-3.5-flash')
|
|
131
239
|
if (!descriptor) throw new Error('unknown model')
|
|
@@ -148,6 +256,47 @@ Important distinctions:
|
|
|
148
256
|
- `providerOptions.google` is a typed provider-extension lane, not a caller-wins
|
|
149
257
|
override lane for `serviceTier`, sampling, reasoning, or response schema.
|
|
150
258
|
|
|
259
|
+
## Extending `ProviderOptionsMap` for a third-party provider
|
|
260
|
+
|
|
261
|
+
`GenConfig.providerOptions` is typed as `ProviderOptions`, an alias for
|
|
262
|
+
`ProviderOptionsMap` — an empty, augmentable interface owned by `@gullabs/core`. It
|
|
263
|
+
carries no keys until a provider package augments it via TypeScript declaration
|
|
264
|
+
merging. `@gullabs/google` and `@gullabs/xai` are the two reference implementations
|
|
265
|
+
(`packages/google/src/types.ts`, `packages/xai/src/types.ts`):
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
declare module '@gullabs/core' {
|
|
269
|
+
interface ProviderOptionsMap {
|
|
270
|
+
google?: GoogleProviderOptions
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
A third-party provider package follows the identical pattern. For a hypothetical
|
|
276
|
+
`@acme/my-provider` package:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
// packages/my-provider/src/types.ts
|
|
280
|
+
export type MyProviderOptions = {
|
|
281
|
+
someAllowlistedKnob?: string
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
declare module '@gullabs/core' {
|
|
285
|
+
interface ProviderOptionsMap {
|
|
286
|
+
myProvider?: MyProviderOptions
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Importing anything from that module — even a type-only import — pulls in the
|
|
292
|
+
augmentation, so re-export it unconditionally from the package's `index.ts` (the way
|
|
293
|
+
`packages/google/src/index.ts` and `packages/xai/src/index.ts` both do) to guarantee
|
|
294
|
+
the `myProvider` key is visible on `ProviderOptionsMap` whenever a caller imports
|
|
295
|
+
anything from the package. Once loaded, `config.providerOptions.myProvider`
|
|
296
|
+
type-checks at call sites — but the model's `configSchema` must also allowlist that
|
|
297
|
+
key for the value to survive validation; the Zod schema remains the runtime boundary,
|
|
298
|
+
the type augmentation only makes the shape visible to the compiler.
|
|
299
|
+
|
|
151
300
|
## Structured output — auth + validation together
|
|
152
301
|
|
|
153
302
|
`request.output = { jsonSchema }` (or `callSite.jsonSchema`) is forwarded to the
|
|
@@ -156,12 +305,11 @@ response and sets `outputParsed`; `result.output` is always `unknown`. **The cal
|
|
|
156
305
|
owns shape validation** — this library does not validate output shape itself.
|
|
157
306
|
|
|
158
307
|
```ts
|
|
159
|
-
import { createClient,
|
|
308
|
+
import { createClient, composeProviders, googleProvider } from '@gullabs/any-llm'
|
|
160
309
|
import type { StandardSchemaV1 } from '@gullabs/core'
|
|
161
310
|
|
|
162
311
|
const client = createClient({
|
|
163
|
-
|
|
164
|
-
pricingSources: { google: geminiPricingSource() },
|
|
312
|
+
...composeProviders([googleProvider()]),
|
|
165
313
|
})
|
|
166
314
|
|
|
167
315
|
const result = await client.generate(
|
|
@@ -273,6 +421,47 @@ Exact model reminders:
|
|
|
273
421
|
- Omit `serviceTier` for provider-standard; set `flex` explicitly
|
|
274
422
|
- `priority` remains rejected by the library even though Google documents it
|
|
275
423
|
|
|
424
|
+
## Context caching — `GoogleCacheStore`
|
|
425
|
+
|
|
426
|
+
`GoogleCacheStore` (from `@gullabs/google`) is a thin, **process-scoped** wrapper over
|
|
427
|
+
the Gemini Context Cache API (`create` / `getOrCreate` / `refreshIfExpiringSoon` /
|
|
428
|
+
`delete`). Pass the resulting `cacheName` as `providerOptions.google.cachedContent` on
|
|
429
|
+
a request. Reuse is only within this store instance's in-memory map — it is not
|
|
430
|
+
shared across processes, workers, or restarts.
|
|
431
|
+
|
|
432
|
+
Optional preflight gate: pass `preflight` to the constructor to refuse a cache
|
|
433
|
+
`create()` — including through `getOrCreate()` and its coalesced in-flight path —
|
|
434
|
+
when the token-bearing payload (`model` + `contents` + `systemInstruction` only;
|
|
435
|
+
`ttl` and `displayName` are excluded) doesn't clear a minimum token count. This
|
|
436
|
+
mirrors Gemini's own explicit-caching minimum (2048 tokens on 3.x) without
|
|
437
|
+
hard-coding it into the store.
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { GoogleCacheStore } from '@gullabs/google'
|
|
441
|
+
|
|
442
|
+
const cacheStore = new GoogleCacheStore({
|
|
443
|
+
auth: { apiKey: myResolvedGeminiKey },
|
|
444
|
+
preflight: {
|
|
445
|
+
minTokens: 2048,
|
|
446
|
+
// Receives genai-native Content[]/Content|string — NOT the library's
|
|
447
|
+
// Message[] shape; there is no automatic conversion. Hosts building from
|
|
448
|
+
// Message[] should call client.countTokens separately instead.
|
|
449
|
+
countTokens: async (payload) => {
|
|
450
|
+
const result = await genaiClient.models.countTokens(payload)
|
|
451
|
+
return result.totalTokens ?? 0
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
const handle = await cacheStore.create({
|
|
457
|
+
model: 'gemini-3.1-pro-preview',
|
|
458
|
+
ttlSeconds: 3600,
|
|
459
|
+
contents: myGenaiContents,
|
|
460
|
+
})
|
|
461
|
+
// Throws LlmError('bad_request') before any I/O if preflight.countTokens resolves
|
|
462
|
+
// below minTokens — nothing is silently allowed through under the minimum.
|
|
463
|
+
```
|
|
464
|
+
|
|
276
465
|
## Rate limiting and cost tracking
|
|
277
466
|
|
|
278
467
|
- Pre-send backpressure is a `RateLimiter` port (`ClientConfig.rateLimiter`); default
|