@gullabs/any-llm 0.8.1 → 0.8.3
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/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 +110 -4
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.8.
|
|
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.3\",\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.8.
|
|
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.3\",\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.8.
|
|
3
|
+
"version": "0.8.3",
|
|
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.8.
|
|
28
|
+
"@gullabs/core": "0.9.0",
|
|
29
|
+
"@gullabs/google": "0.8.2"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=20.9.0"
|
package/skills/any-llm/SKILL.md
CHANGED
|
@@ -229,6 +229,17 @@ const result = await client.generate(
|
|
|
229
229
|
`system` is derived only from the explicit `systemInstruction` input — never inferred
|
|
230
230
|
from `contents`.
|
|
231
231
|
|
|
232
|
+
## Testing with `@gullabs/testing`
|
|
233
|
+
|
|
234
|
+
Real hosts don't call `createClient()` at call sites — they own a factory module that
|
|
235
|
+
assembles the client once and hand call sites the built client. `@gullabs/testing`'s
|
|
236
|
+
fakes (`makeFakeGemini`, `FakeAdapter`, `RecordingSink`, `FakeClock`, `FakeIds`, ...) are
|
|
237
|
+
designed to inject through that same host-owned factory unchanged, via injectable
|
|
238
|
+
override parameters with production defaults — not via `vi.mock()`. See
|
|
239
|
+
`packages/testing/README.md` § "Wiring fakes through a host-owned factory" for a
|
|
240
|
+
complete two-file (factory + vitest test) example, including the port-level
|
|
241
|
+
`FakeAdapter` variant for bypassing the Gemini SDK shape entirely.
|
|
242
|
+
|
|
232
243
|
## `defineCallSite` — reusable prompt templates
|
|
233
244
|
|
|
234
245
|
```ts
|
|
@@ -248,12 +259,106 @@ const result = await client.runStructured(summarize, { article: text }, { auth }
|
|
|
248
259
|
|
|
249
260
|
`{{var}}` interpolation is non-recursive (substituted values are never re-scanned for
|
|
250
261
|
further `{{...}}`, preventing template injection) and applies to both `system` and
|
|
251
|
-
`userTemplate`.
|
|
252
|
-
|
|
253
|
-
|
|
262
|
+
`userTemplate`. **Strict by default (no opt-out):** every `{{var}}` placeholder must have
|
|
263
|
+
a string-typed value in `vars`, or `runStructured` throws before any request is built —
|
|
264
|
+
see "Input contracts" below. `runStructured` also accepts a two-arg form, `(callSite,
|
|
265
|
+
opts)`, when the template has no vars. Config resolution order everywhere is
|
|
254
266
|
`clientDefaults → callSite.config → opts.config`, and the merged config must still pass
|
|
255
267
|
the selected descriptor's strict runtime schema before dispatch.
|
|
256
268
|
|
|
269
|
+
## Input contracts — strict interpolation, `inputSchema`, `inputContract`
|
|
270
|
+
|
|
271
|
+
**Strict template interpolation is the default, with no opt-out.** Every `{{var}}`
|
|
272
|
+
placeholder referenced by `callSite.system` or `callSite.userTemplate` must have a
|
|
273
|
+
string-typed value present in `vars`, or `runStructured` refuses the call before any
|
|
274
|
+
request is built — zero tokens spent:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { defineCallSite, LlmError } from '@gullabs/core'
|
|
278
|
+
|
|
279
|
+
const summarize = defineCallSite({
|
|
280
|
+
id: 'summarize-article',
|
|
281
|
+
provider: 'google',
|
|
282
|
+
model: 'gemini-2.5-flash',
|
|
283
|
+
userTemplate: 'Summarize this article in 3 sentences:\n\n{{article}}',
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
// Missing `article` — throws before any I/O.
|
|
288
|
+
await client.runStructured(summarize, {}, { auth })
|
|
289
|
+
} catch (e) {
|
|
290
|
+
if (e instanceof LlmError && e.kind === 'bad_request') {
|
|
291
|
+
console.log(e.issues) // [{ path: 'article', message: '...' }]
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`null`, `undefined`, and non-string values (numbers, objects) are all violations — never
|
|
297
|
+
coerced to a string. Unused `vars` entries (present in `vars` but not referenced by any
|
|
298
|
+
template) are allowed. There is no escape syntax for literal `{{...}}` text.
|
|
299
|
+
|
|
300
|
+
**`CallSite.inputSchema`** validates `vars` with a `StandardSchemaV1` validator (zod,
|
|
301
|
+
valibot, ...) before interpolation runs, so a missing business field surfaces in your own
|
|
302
|
+
schema's vocabulary instead of as a downstream placeholder violation:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
import { z } from 'zod'
|
|
306
|
+
|
|
307
|
+
const reviewCallSite = defineCallSite({
|
|
308
|
+
id: 'code-review',
|
|
309
|
+
provider: 'google',
|
|
310
|
+
model: 'gemini-2.5-flash',
|
|
311
|
+
userTemplate: 'Review this diff as {{reviewer}}:\n\n{{diff}}',
|
|
312
|
+
inputSchema: z.object({
|
|
313
|
+
reviewer: z.string().min(1),
|
|
314
|
+
diff: z.string().min(1),
|
|
315
|
+
}),
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
await client.runStructured(
|
|
319
|
+
reviewCallSite,
|
|
320
|
+
{ reviewer: 'senior-reviewer', diff },
|
|
321
|
+
{ auth },
|
|
322
|
+
)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**`LlmRequest.inputContract`** is the equivalent opt-in contract for the `generate()`
|
|
326
|
+
path (callers who render their own prompt strings and never touch `CallSite`):
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
const result = await client.generate(
|
|
330
|
+
{
|
|
331
|
+
provider: 'google',
|
|
332
|
+
model: 'gemini-2.5-flash',
|
|
333
|
+
messages: [{ role: 'user', parts: [{ kind: 'text', text: renderedPrompt }] }],
|
|
334
|
+
inputContract: { schema: myZodSchema, value: sourceContext },
|
|
335
|
+
},
|
|
336
|
+
{ auth },
|
|
337
|
+
)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`inputContract` is validated once per logical call, before `@gullabs/quota` or any retry
|
|
341
|
+
middleware runs — a violation never consumes rate-limit budget and is never retried.
|
|
342
|
+
`generate()` and `runStructured()` are independent paths: `runStructured` never
|
|
343
|
+
auto-populates `inputContract` from `inputSchema`, and `generate()` never reads
|
|
344
|
+
`inputSchema`.
|
|
345
|
+
|
|
346
|
+
**`createClient({ requireInputContract: true })`** is a fleet-wide toggle: every
|
|
347
|
+
`generate()` call must carry `inputContract`, and every `runStructured()` call site must
|
|
348
|
+
carry `inputSchema`, or the call is refused. Off by default.
|
|
349
|
+
|
|
350
|
+
**All violations throw `LlmError('bad_request')`** with a structured `issues` array
|
|
351
|
+
(`{ path, message }[]`, one entry per violation) on top of the usual `.message` string.
|
|
352
|
+
|
|
353
|
+
**Ledger semantics of refusals.** A refusal that never got a `callId` (unresolved
|
|
354
|
+
placeholders, `CallSite.inputSchema`, or `requireInputContract` on the `runStructured`
|
|
355
|
+
path — all thrown in the `runStructured` prologue) writes **no** ledger row. A refusal
|
|
356
|
+
that already has a `callId` (`inputContract` violations and `requireInputContract` on the
|
|
357
|
+
`generate()` path, thrown inside the pipeline after `callId` assignment) writes **one**
|
|
358
|
+
zero-usage record with `attemptNumber: 0` — including `@gullabs/quota` denials, which get
|
|
359
|
+
the same treatment with no `@gullabs/quota` code changes. See ADR-025 in `DECISIONS.md`
|
|
360
|
+
for the full boundary table.
|
|
361
|
+
|
|
257
362
|
## Strict model-config boundary
|
|
258
363
|
|
|
259
364
|
Treat model config as descriptor-owned:
|
|
@@ -405,7 +510,8 @@ try {
|
|
|
405
510
|
```
|
|
406
511
|
|
|
407
512
|
`LlmError` also carries `httpStatus?`, `retryAfterMs?`, `provider?`, `callId?`,
|
|
408
|
-
`attemptId?`, `servedServiceTier?`,
|
|
513
|
+
`attemptId?`, `servedServiceTier?`, `issues?` (structured `{ path, message }[]` — see
|
|
514
|
+
"Input contracts" above), and `cause` (the original thrown value).
|
|
409
515
|
|
|
410
516
|
## Reject, don't map
|
|
411
517
|
|