@gullabs/google 0.8.3 → 0.9.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 +13 -0
- package/dist/index.cjs +76 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -6
- package/dist/index.d.ts +30 -6
- package/dist/index.js +76 -8
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -23,6 +23,19 @@ pnpm add @gullabs/google @gullabs/core @google/genai
|
|
|
23
23
|
| `geminiModelDescriptors`, `gemmaModelDescriptors`, `defaultGeminiRegistry` | Built-in model descriptors + pre-built registry |
|
|
24
24
|
| `geminiPricingSource()`, `GEMINI_PRICING`, `TIER_FACTOR` | Built-in Gemini pricing snapshot and tier-factor map |
|
|
25
25
|
|
|
26
|
+
|
|
27
|
+
| `GoogleFileStore` | Files API: upload + poll ACTIVE + delete |
|
|
28
|
+
| `FileDeleteOptions` | `{ failClosed?, signal? }` — opt-in fail-closed delete (parity with `@gullabs/xai`) |
|
|
29
|
+
|
|
30
|
+
## File store delete modes
|
|
31
|
+
|
|
32
|
+
`GoogleFileStore.delete` defaults to **fail-open** (errors → `onDeleteError`, resolve). Pass `{ failClosed: true }` when the host gates durable state on known success; HTTP/SDK not-found remains success (idempotent). Empty `handle.name` always throws `bad_request`.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
await store.delete(handle) // fail-open
|
|
36
|
+
await store.delete(handle, { failClosed: true }) // throw on non-not-found failure
|
|
37
|
+
```
|
|
38
|
+
|
|
26
39
|
## Quick example
|
|
27
40
|
|
|
28
41
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -411,6 +411,11 @@ function mapPart(p) {
|
|
|
411
411
|
...p.mediaResolution !== void 0 ? { mediaResolution: { level: mapMediaResolution(p.mediaResolution) } } : {}
|
|
412
412
|
};
|
|
413
413
|
}
|
|
414
|
+
case "file-ref":
|
|
415
|
+
throw new core.LlmError(
|
|
416
|
+
"Google Gemini expects FileUriPart with a Files API uri; got file-ref (provider file id). Upload via GoogleFileStore and pass the returned uri.",
|
|
417
|
+
{ kind: "bad_request", retryable: false, provider: "google" }
|
|
418
|
+
);
|
|
414
419
|
default:
|
|
415
420
|
return core.assertNever(p);
|
|
416
421
|
}
|
|
@@ -2868,25 +2873,88 @@ var GoogleFileStore = class {
|
|
|
2868
2873
|
}
|
|
2869
2874
|
}
|
|
2870
2875
|
/**
|
|
2871
|
-
* Delete a single uploaded file.
|
|
2872
|
-
*
|
|
2876
|
+
* Delete a single uploaded file. Idempotent: not-found → success.
|
|
2877
|
+
*
|
|
2878
|
+
* Default (`failClosed` omitted/false): errors go to `onDeleteError` and
|
|
2879
|
+
* resolve (P5 fail-open). With `failClosed: true`, non-not-found errors
|
|
2880
|
+
* throw typed `LlmError` and `onDeleteError` is not called.
|
|
2881
|
+
*
|
|
2882
|
+
* Empty/blank `handle.name` always throws `bad_request`.
|
|
2873
2883
|
*/
|
|
2874
|
-
async delete(handle) {
|
|
2884
|
+
async delete(handle, opts) {
|
|
2885
|
+
const name = handle.name;
|
|
2886
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
2887
|
+
throw new core.LlmError("GoogleFileHandle.name must be a non-empty string.", {
|
|
2888
|
+
kind: "bad_request",
|
|
2889
|
+
retryable: false,
|
|
2890
|
+
provider: "google"
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
const failClosed = opts?.failClosed === true;
|
|
2875
2894
|
try {
|
|
2895
|
+
if (opts?.signal?.aborted === true) {
|
|
2896
|
+
throw new core.LlmError("Google file delete aborted", {
|
|
2897
|
+
kind: "aborted",
|
|
2898
|
+
retryable: false,
|
|
2899
|
+
provider: "google"
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2876
2902
|
const client = await this.getClient();
|
|
2877
|
-
await client.delete({ name
|
|
2903
|
+
await client.delete({ name });
|
|
2878
2904
|
} catch (err) {
|
|
2879
|
-
|
|
2905
|
+
if (isGoogleNotFoundError(err)) {
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
const classified = err instanceof core.LlmError ? err : core.classifyError(err);
|
|
2909
|
+
const withProvider = classified.provider === void 0 ? new core.LlmError(classified.message, {
|
|
2910
|
+
kind: classified.kind,
|
|
2911
|
+
retryable: classified.retryable,
|
|
2912
|
+
...classified.httpStatus !== void 0 ? { httpStatus: classified.httpStatus } : {},
|
|
2913
|
+
...classified.retryAfterMs !== void 0 ? { retryAfterMs: classified.retryAfterMs } : {},
|
|
2914
|
+
provider: "google",
|
|
2915
|
+
cause: classified.cause ?? err
|
|
2916
|
+
}) : classified;
|
|
2917
|
+
if (failClosed) {
|
|
2918
|
+
throw withProvider;
|
|
2919
|
+
}
|
|
2920
|
+
this.onDeleteError(name, withProvider);
|
|
2880
2921
|
}
|
|
2881
2922
|
}
|
|
2882
2923
|
/**
|
|
2883
2924
|
* Delete multiple files.
|
|
2884
|
-
*
|
|
2925
|
+
*
|
|
2926
|
+
* Fail-open (default): `Promise.allSettled` per handle.
|
|
2927
|
+
* Fail-closed: `Promise.all` — first throw rejects; in-flight siblings are
|
|
2928
|
+
* not cancelled. Prefer per-handle delete when gating durable release state.
|
|
2885
2929
|
*/
|
|
2886
|
-
async deleteAll(handles) {
|
|
2887
|
-
|
|
2930
|
+
async deleteAll(handles, opts) {
|
|
2931
|
+
if (opts?.failClosed === true) {
|
|
2932
|
+
await Promise.all(handles.map((h) => this.delete(h, opts)));
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
await Promise.allSettled(handles.map((h) => this.delete(h, opts)));
|
|
2888
2936
|
}
|
|
2889
2937
|
};
|
|
2938
|
+
function isGoogleNotFoundError(err) {
|
|
2939
|
+
if (err instanceof core.LlmError && err.httpStatus === 404) {
|
|
2940
|
+
return true;
|
|
2941
|
+
}
|
|
2942
|
+
if (typeof err !== "object" || err === null) {
|
|
2943
|
+
return false;
|
|
2944
|
+
}
|
|
2945
|
+
const obj = err;
|
|
2946
|
+
if (obj["status"] === 404 || obj["httpStatus"] === 404 || obj["code"] === 404) {
|
|
2947
|
+
return true;
|
|
2948
|
+
}
|
|
2949
|
+
if (obj["status"] === "NOT_FOUND" || obj["code"] === "NOT_FOUND") {
|
|
2950
|
+
return true;
|
|
2951
|
+
}
|
|
2952
|
+
const msg = typeof obj["message"] === "string" ? obj["message"] : "";
|
|
2953
|
+
if (/not\s*found|404/i.test(msg) && /file/i.test(msg)) {
|
|
2954
|
+
return true;
|
|
2955
|
+
}
|
|
2956
|
+
return false;
|
|
2957
|
+
}
|
|
2890
2958
|
var DEFAULT_SKEW_SECONDS = 30;
|
|
2891
2959
|
var DEFAULT_EXTENSION_SECONDS = 3600;
|
|
2892
2960
|
async function buildCachesClient(auth) {
|