pyric 0.1.0-alpha.19 → 0.1.0-alpha.21
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 +64 -89
- package/dist/ai/blocked.d.ts +52 -0
- package/dist/ai/blocked.d.ts.map +1 -0
- package/dist/ai/blocked.js +83 -0
- package/dist/ai/blocked.js.map +1 -0
- package/dist/ai/broker/broker.d.ts +50 -0
- package/dist/ai/broker/broker.d.ts.map +1 -1
- package/dist/ai/broker/broker.js +157 -21
- package/dist/ai/broker/broker.js.map +1 -1
- package/dist/ai/broker/gemini-engine.d.ts +56 -0
- package/dist/ai/broker/gemini-engine.d.ts.map +1 -0
- package/dist/ai/broker/gemini-engine.js +235 -0
- package/dist/ai/broker/gemini-engine.js.map +1 -0
- package/dist/ai/broker/index.d.ts +2 -1
- package/dist/ai/broker/index.d.ts.map +1 -1
- package/dist/ai/broker/index.js +2 -1
- package/dist/ai/broker/index.js.map +1 -1
- package/dist/ai/broker/openai-engine.d.ts +10 -1
- package/dist/ai/broker/openai-engine.d.ts.map +1 -1
- package/dist/ai/broker/openai-engine.js +26 -4
- package/dist/ai/broker/openai-engine.js.map +1 -1
- package/dist/ai/broker/synthesizer.d.ts +14 -0
- package/dist/ai/broker/synthesizer.d.ts.map +1 -1
- package/dist/ai/broker/synthesizer.js +31 -0
- package/dist/ai/broker/synthesizer.js.map +1 -1
- package/dist/ai/broker/types.d.ts +29 -0
- package/dist/ai/broker/types.d.ts.map +1 -1
- package/dist/ai/instances.js +1 -1
- package/dist/ai/instances.js.map +1 -1
- package/dist/ai/internal.d.ts +5 -0
- package/dist/ai/internal.d.ts.map +1 -1
- package/dist/ai/internal.js +5 -0
- package/dist/ai/internal.js.map +1 -1
- package/dist/ai/response-helpers.d.ts.map +1 -1
- package/dist/ai/response-helpers.js +6 -24
- package/dist/ai/response-helpers.js.map +1 -1
- package/dist/analytics/index.d.ts +40 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +25 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/app-check/index.d.ts +31 -0
- package/dist/app-check/index.d.ts.map +1 -0
- package/dist/app-check/index.js +20 -0
- package/dist/app-check/index.js.map +1 -0
- package/dist/auth/instances.js +1 -1
- package/dist/auth/instances.js.map +1 -1
- package/dist/database/controls.d.ts +1 -1
- package/dist/database/controls.js +1 -1
- package/dist/database/instances.js +1 -1
- package/dist/database/instances.js.map +1 -1
- package/dist/database/sandbox/backend.d.ts +1 -0
- package/dist/database/sandbox/backend.d.ts.map +1 -1
- package/dist/database/sandbox/backend.js +1 -0
- package/dist/database/sandbox/backend.js.map +1 -1
- package/dist/database/sandbox/rules-eval.d.ts +6 -2
- package/dist/database/sandbox/rules-eval.d.ts.map +1 -1
- package/dist/database/sandbox/rules-eval.js +15 -2
- package/dist/database/sandbox/rules-eval.js.map +1 -1
- package/dist/database/sandbox/write-plane.d.ts +1 -0
- package/dist/database/sandbox/write-plane.d.ts.map +1 -1
- package/dist/database/sandbox/write-plane.js +7 -2
- package/dist/database/sandbox/write-plane.js.map +1 -1
- package/dist/database/sandbox-controls.d.ts +12 -5
- package/dist/database/sandbox-controls.d.ts.map +1 -1
- package/dist/database/sandbox-controls.js +20 -9
- package/dist/database/sandbox-controls.js.map +1 -1
- package/dist/database/sandbox-namespace.d.ts +19 -1
- package/dist/database/sandbox-namespace.d.ts.map +1 -1
- package/dist/database/sandbox-namespace.js +24 -1
- package/dist/database/sandbox-namespace.js.map +1 -1
- package/dist/deferred/entry.d.ts +75 -0
- package/dist/deferred/entry.d.ts.map +1 -0
- package/dist/deferred/entry.js +143 -0
- package/dist/deferred/entry.js.map +1 -0
- package/dist/firestore/instances.js +1 -1
- package/dist/firestore/instances.js.map +1 -1
- package/dist/firestore/lite.d.ts +72 -0
- package/dist/firestore/lite.d.ts.map +1 -0
- package/dist/firestore/lite.js +22 -0
- package/dist/firestore/lite.js.map +1 -0
- package/dist/firestore/persistence.d.ts +2 -2
- package/dist/firestore/persistence.js +1 -1
- package/dist/firestore/sandbox-controls.d.ts +2 -0
- package/dist/firestore/sandbox-controls.d.ts.map +1 -1
- package/dist/firestore/sandbox-controls.js +8 -0
- package/dist/firestore/sandbox-controls.js.map +1 -1
- package/dist/functions/index.d.ts +29 -0
- package/dist/functions/index.d.ts.map +1 -0
- package/dist/functions/index.js +20 -0
- package/dist/functions/index.js.map +1 -0
- package/dist/messaging/client.d.ts +2 -1
- package/dist/messaging/client.d.ts.map +1 -1
- package/dist/messaging/client.js +2 -2
- package/dist/messaging/client.js.map +1 -1
- package/dist/messaging/instance.d.ts +2 -2
- package/dist/messaging/instance.d.ts.map +1 -1
- package/dist/messaging/instance.js +40 -6
- package/dist/messaging/instance.js.map +1 -1
- package/dist/messaging/sw.d.ts +2 -1
- package/dist/messaging/sw.d.ts.map +1 -1
- package/dist/messaging/sw.js +2 -2
- package/dist/messaging/sw.js.map +1 -1
- package/dist/performance/index.d.ts +22 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +19 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/remote-config/index.d.ts +40 -0
- package/dist/remote-config/index.d.ts.map +1 -0
- package/dist/remote-config/index.js +25 -0
- package/dist/remote-config/index.js.map +1 -0
- package/dist/rules/grammar/FirestoreValidator.d.ts.map +1 -1
- package/dist/rules/grammar/FirestoreValidator.js +43 -27
- package/dist/rules/grammar/FirestoreValidator.js.map +1 -1
- package/dist/rules/grammar/document-access-count.d.ts +24 -0
- package/dist/rules/grammar/document-access-count.d.ts.map +1 -0
- package/dist/rules/grammar/document-access-count.js +100 -0
- package/dist/rules/grammar/document-access-count.js.map +1 -0
- package/dist/rules/linter/ast-utils.d.ts +2 -6
- package/dist/rules/linter/ast-utils.d.ts.map +1 -1
- package/dist/rules/linter/ast-utils.js +2 -63
- package/dist/rules/linter/ast-utils.js.map +1 -1
- package/dist/rules/linter/hallucinations.d.ts +8 -0
- package/dist/rules/linter/hallucinations.d.ts.map +1 -1
- package/dist/rules/linter/hallucinations.js +121 -31
- package/dist/rules/linter/hallucinations.js.map +1 -1
- package/dist/rules/linter/linter.d.ts +10 -1
- package/dist/rules/linter/linter.d.ts.map +1 -1
- package/dist/rules/linter/linter.js +23 -9
- package/dist/rules/linter/linter.js.map +1 -1
- package/dist/rules/rtdb/grammar/RtdbExpr.ohm +5 -2
- package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.d.ts +1 -1
- package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.d.ts.map +1 -1
- package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.js +5 -2
- package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.js.map +1 -1
- package/dist/rules/rtdb/grammar/simulator.d.ts.map +1 -1
- package/dist/rules/rtdb/grammar/simulator.js +61 -15
- package/dist/rules/rtdb/grammar/simulator.js.map +1 -1
- package/dist/rules/rtdb/simulation/handler.d.ts.map +1 -1
- package/dist/rules/rtdb/simulation/handler.js +96 -35
- package/dist/rules/rtdb/simulation/handler.js.map +1 -1
- package/dist/rules/simulator/document-lookups.d.ts +4 -0
- package/dist/rules/simulator/document-lookups.d.ts.map +1 -1
- package/dist/rules/simulator/document-lookups.js +42 -3
- package/dist/rules/simulator/document-lookups.js.map +1 -1
- package/dist/rules/simulator/eval-error.d.ts +11 -0
- package/dist/rules/simulator/eval-error.d.ts.map +1 -1
- package/dist/rules/simulator/eval-error.js +14 -0
- package/dist/rules/simulator/eval-error.js.map +1 -1
- package/dist/rules/simulator/evaluation-builtins.d.ts.map +1 -1
- package/dist/rules/simulator/evaluation-builtins.js +33 -8
- package/dist/rules/simulator/evaluation-builtins.js.map +1 -1
- package/dist/rules/simulator/evaluation-context.d.ts +12 -0
- package/dist/rules/simulator/evaluation-context.d.ts.map +1 -1
- package/dist/rules/simulator/evaluator.d.ts +1 -1
- package/dist/rules/simulator/evaluator.d.ts.map +1 -1
- package/dist/rules/simulator/evaluator.js +16 -4
- package/dist/rules/simulator/evaluator.js.map +1 -1
- package/dist/rules/simulator/handler.d.ts.map +1 -1
- package/dist/rules/simulator/handler.js +52 -5
- package/dist/rules/simulator/handler.js.map +1 -1
- package/dist/rules/simulator/lookup-budget.d.ts +37 -0
- package/dist/rules/simulator/lookup-budget.d.ts.map +1 -0
- package/dist/rules/simulator/lookup-budget.js +96 -0
- package/dist/rules/simulator/lookup-budget.js.map +1 -0
- package/dist/rules/stdlib-modules.d.ts +7 -0
- package/dist/rules/stdlib-modules.d.ts.map +1 -1
- package/dist/rules/stdlib-modules.js +11 -11
- package/dist/rules/stdlib-modules.js.map +1 -1
- package/dist/sandbox/admin-firestore/get-firestore.d.ts +6 -0
- package/dist/sandbox/admin-firestore/get-firestore.d.ts.map +1 -1
- package/dist/sandbox/admin-firestore/get-firestore.js +22 -3
- package/dist/sandbox/admin-firestore/get-firestore.js.map +1 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/persistence/types.d.ts +1 -1
- package/dist/sandbox/remote.d.ts +2 -2
- package/dist/sandbox/sandbox-context.d.ts +8 -1
- package/dist/sandbox/sandbox-context.d.ts.map +1 -1
- package/dist/sandbox/sandbox-context.js +59 -8
- package/dist/sandbox/sandbox-context.js.map +1 -1
- package/dist/sandbox/types/auth-state.d.ts +1 -0
- package/dist/sandbox/types/auth-state.d.ts.map +1 -1
- package/dist/sandbox/types/events.d.ts +2 -1
- package/dist/sandbox/types/events.d.ts.map +1 -1
- package/dist/sandbox/types/operation.d.ts +1 -0
- package/dist/sandbox/types/operation.d.ts.map +1 -1
- package/dist/storage/enforce.d.ts +20 -1
- package/dist/storage/enforce.d.ts.map +1 -1
- package/dist/storage/enforce.js +47 -14
- package/dist/storage/enforce.js.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/list.js +1 -1
- package/dist/storage/list.js.map +1 -1
- package/dist/storage/persistence.d.ts.map +1 -1
- package/dist/storage/persistence.js +66 -14
- package/dist/storage/persistence.js.map +1 -1
- package/dist/storage/sandbox/rules-evaluation-error.d.ts +25 -0
- package/dist/storage/sandbox/rules-evaluation-error.d.ts.map +1 -1
- package/dist/storage/sandbox/rules-evaluation-error.js +29 -0
- package/dist/storage/sandbox/rules-evaluation-error.js.map +1 -1
- package/dist/storage/sandbox/rules-evaluator.d.ts +10 -5
- package/dist/storage/sandbox/rules-evaluator.d.ts.map +1 -1
- package/dist/storage/sandbox/rules-evaluator.js +105 -33
- package/dist/storage/sandbox/rules-evaluator.js.map +1 -1
- package/dist/storage/sandbox/rules-methods.d.ts.map +1 -1
- package/dist/storage/sandbox/rules-methods.js +17 -7
- package/dist/storage/sandbox/rules-methods.js.map +1 -1
- package/dist/storage/service.d.ts +23 -2
- package/dist/storage/service.d.ts.map +1 -1
- package/dist/storage/service.js +37 -26
- package/dist/storage/service.js.map +1 -1
- package/package.json +42 -2
- package/src/ai/blocked.ts +97 -0
- package/src/ai/broker/broker.ts +153 -22
- package/src/ai/broker/gemini-engine.ts +311 -0
- package/src/ai/broker/index.ts +2 -0
- package/src/ai/broker/openai-engine.ts +31 -3
- package/src/ai/broker/synthesizer.ts +34 -0
- package/src/ai/broker/types.ts +27 -1
- package/src/ai/instances.ts +1 -1
- package/src/ai/internal.ts +5 -0
- package/src/ai/response-helpers.ts +6 -25
- package/src/analytics/index.ts +53 -0
- package/src/app/dispatch.test.ts +3 -1
- package/src/app-check/index.ts +41 -0
- package/src/auth/instances.ts +1 -1
- package/src/database/controls.ts +1 -1
- package/src/database/instances.ts +1 -1
- package/src/database/sandbox/backend.ts +1 -0
- package/src/database/sandbox/rules-eval.ts +18 -2
- package/src/database/sandbox/write-plane.ts +8 -2
- package/src/database/sandbox-controls.ts +29 -9
- package/src/database/sandbox-namespace.ts +28 -1
- package/src/deferred/entry.ts +167 -0
- package/src/firestore/instances.ts +1 -1
- package/src/firestore/lite.ts +91 -0
- package/src/firestore/persistence.ts +2 -2
- package/src/firestore/sandbox-controls.ts +10 -0
- package/src/functions/index.ts +38 -0
- package/src/messaging/client.ts +3 -2
- package/src/messaging/instance.ts +44 -7
- package/src/messaging/sw.ts +3 -2
- package/src/performance/index.ts +31 -0
- package/src/remote-config/index.ts +53 -0
- package/src/rules/grammar/FirestoreValidator.ts +44 -28
- package/src/rules/grammar/document-access-count.ts +75 -0
- package/src/rules/linter/ast-utils.ts +2 -36
- package/src/rules/linter/hallucinations.ts +123 -25
- package/src/rules/linter/linter.ts +32 -10
- package/src/rules/rtdb/grammar/RtdbExpr.ohm +5 -2
- package/src/rules/rtdb/grammar/RtdbExpr.ohm.generated.ts +5 -2
- package/src/rules/rtdb/grammar/simulator.ts +41 -15
- package/src/rules/rtdb/simulation/handler.ts +149 -46
- package/src/rules/simulator/document-lookups.ts +46 -3
- package/src/rules/simulator/eval-error.ts +16 -0
- package/src/rules/simulator/evaluation-builtins.ts +35 -8
- package/src/rules/simulator/evaluation-context.ts +12 -0
- package/src/rules/simulator/evaluator.ts +14 -3
- package/src/rules/simulator/handler.ts +68 -4
- package/src/rules/simulator/lookup-budget.ts +101 -0
- package/src/rules/stdlib-modules.ts +19 -11
- package/src/sandbox/admin-firestore/get-firestore.ts +19 -3
- package/src/sandbox/index.ts +5 -1
- package/src/sandbox/persistence/types.ts +1 -1
- package/src/sandbox/remote.ts +2 -2
- package/src/sandbox/sandbox-context.ts +69 -9
- package/src/sandbox/types/auth-state.ts +1 -1
- package/src/sandbox/types/events.ts +2 -1
- package/src/sandbox/types/operation.ts +1 -1
- package/src/storage/enforce.ts +53 -14
- package/src/storage/index.ts +1 -1
- package/src/storage/list.ts +1 -1
- package/src/storage/persistence.ts +74 -16
- package/src/storage/sandbox/rules-evaluation-error.ts +32 -0
- package/src/storage/sandbox/rules-evaluator.ts +109 -32
- package/src/storage/sandbox/rules-methods.ts +21 -7
- package/src/storage/service.ts +93 -27
- package/README.md.orig +0 -53
package/dist/storage/service.js
CHANGED
|
@@ -45,14 +45,15 @@ export function storageAuth(target) {
|
|
|
45
45
|
export class StorageService {
|
|
46
46
|
backend;
|
|
47
47
|
rules;
|
|
48
|
-
|
|
48
|
+
crossServiceIam;
|
|
49
|
+
constructor(backend, rules = null, crossServiceIam = 'granted') {
|
|
49
50
|
this.backend = backend;
|
|
50
51
|
this.rules = rules;
|
|
52
|
+
this.crossServiceIam = crossServiceIam;
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const SERVICES = new WeakMap();
|
|
55
|
+
/** One open storage service, with its opening configuration, per `Sandbox`. */
|
|
56
|
+
const OPEN_SERVICES = new WeakMap();
|
|
56
57
|
/** One sandbox `FirebaseStorage` handle per `SandboxContext`. */
|
|
57
58
|
const SANDBOX_HANDLES = new WeakMap();
|
|
58
59
|
/**
|
|
@@ -65,15 +66,25 @@ const SANDBOX_HANDLES = new WeakMap();
|
|
|
65
66
|
*/
|
|
66
67
|
const BARE_SANDBOX_HANDLES = new WeakMap();
|
|
67
68
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
69
|
+
* Reject a late option that differs from the one the service is already
|
|
70
|
+
* open with. Both `rules` and `crossServiceIam` are honored only on the
|
|
71
|
+
* FIRST storage call per `Sandbox`. Silently discarding a later, differing
|
|
72
|
+
* value would wipe the rules, or reopen the false-ALLOW surface the IAM
|
|
73
|
+
* mode models, without a word. Re-supplying the IDENTICAL value stays fine
|
|
74
|
+
* (idempotent multi-handle construction, for example per-user contexts),
|
|
75
|
+
* and so does omitting it.
|
|
74
76
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
function rejectDifferingLateConfig(option, descriptor, supplied, openedWith, caller) {
|
|
78
|
+
if (supplied === undefined || supplied === openedWith)
|
|
79
|
+
return;
|
|
80
|
+
const openedDescription = openedWith === null
|
|
81
|
+
? `without ${option}`
|
|
82
|
+
: `with a different ${descriptor}`;
|
|
83
|
+
throw new Error(`pyric/storage: ${caller} received a ${descriptor}, but this sandbox's storage service ` +
|
|
84
|
+
`is already open ${openedDescription}. ${option} is honored only on the FIRST storage ` +
|
|
85
|
+
'call per Sandbox, so this value would be silently discarded. Configure it on the first ' +
|
|
86
|
+
'storage call for this sandbox.');
|
|
87
|
+
}
|
|
77
88
|
/**
|
|
78
89
|
* Get (or open) the ONE per-sandbox `StorageService`. Loud on the
|
|
79
90
|
* silent-rules-wipe hazard: when the service is already open and the
|
|
@@ -82,16 +93,11 @@ const SERVICE_RULES_RESOLUTION = new WeakMap();
|
|
|
82
93
|
* on the first storage call for the sandbox instead.
|
|
83
94
|
*/
|
|
84
95
|
function ensureService(sandbox, options, caller) {
|
|
85
|
-
const existing =
|
|
96
|
+
const existing = OPEN_SERVICES.get(sandbox);
|
|
86
97
|
if (existing) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`service is already open ${openedWith === null ? 'without rules' : 'with a different rules source'} ` +
|
|
91
|
-
'— rules are honored only on the FIRST storage call per Sandbox, so these rules ' +
|
|
92
|
-
'would be silently discarded. Configure rules on the first storage call for this sandbox.');
|
|
93
|
-
}
|
|
94
|
-
return existing;
|
|
98
|
+
rejectDifferingLateConfig('rules', 'rules source', options.rules, existing.rulesSource, caller);
|
|
99
|
+
rejectDifferingLateConfig('crossServiceIam', 'crossServiceIam mode', options.crossServiceIam, existing.crossServiceIam, caller);
|
|
100
|
+
return existing.service;
|
|
95
101
|
}
|
|
96
102
|
let rules = null;
|
|
97
103
|
let resolution = null;
|
|
@@ -116,10 +122,14 @@ function ensureService(sandbox, options, caller) {
|
|
|
116
122
|
}
|
|
117
123
|
// Explicit dbName wins; otherwise scope the default by project identity so
|
|
118
124
|
// two projects on one origin never share a storage database (issue #359).
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
const crossServiceIam = options.crossServiceIam ?? 'granted';
|
|
126
|
+
const servicePromise = openStorageBackend(options.dbName ?? storageDbName(options.projectId)).then((backend) => new StorageService(backend, rules, crossServiceIam));
|
|
127
|
+
OPEN_SERVICES.set(sandbox, {
|
|
128
|
+
service: servicePromise,
|
|
129
|
+
rulesSource: options.rules ?? null,
|
|
130
|
+
rulesResolution: resolution,
|
|
131
|
+
crossServiceIam,
|
|
132
|
+
});
|
|
123
133
|
// Join the sandbox's persistable-service REGISTRY so `sandbox.resetAll()`
|
|
124
134
|
// reaches storage (issue #359: Studio's reset cleared Firestore + auth but
|
|
125
135
|
// never storage — storage was invisible to the sandbox). Storage does NOT
|
|
@@ -176,6 +186,7 @@ export function getStorageSandbox(target, options = {}) {
|
|
|
176
186
|
kind: 'sandbox',
|
|
177
187
|
sandbox,
|
|
178
188
|
context: ctx,
|
|
189
|
+
currentAuth: fromBareSandbox ? () => sandbox.currentUser : undefined,
|
|
179
190
|
bucket,
|
|
180
191
|
servicePromise,
|
|
181
192
|
};
|
|
@@ -286,6 +297,6 @@ export function getStorageService(storage) {
|
|
|
286
297
|
}
|
|
287
298
|
/** Internal assurance seam for the exact rules source retained by Storage setup. */
|
|
288
299
|
export function getStorageRulesResolution(storage) {
|
|
289
|
-
return
|
|
300
|
+
return OPEN_SERVICES.get(targetOf(storage).sandbox)?.rulesResolution ?? null;
|
|
290
301
|
}
|
|
291
302
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAuB,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAqB,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EACL,4BAA4B,GAE7B,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,MAAM,cAAc,GAAG,eAAe,CAAC;AAEvC;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,sBAAsB,CAAC,CAAC;AA6B3E,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,OAAO,MAAM,CAAC,WAAW,EAAE,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;AACvD,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pyric",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.21",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://pyric.dev",
|
|
6
6
|
"repository": {
|
|
@@ -13,6 +13,22 @@
|
|
|
13
13
|
},
|
|
14
14
|
"description": "Firebase for agents \u2014 modular Web SDK adapters (firestore, auth, database, storage) with swappable sandbox/prod backends, plus rules tooling and in-process sandbox. Mirrors firebase.",
|
|
15
15
|
"type": "module",
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"./dist/app/register.js",
|
|
18
|
+
"./dist/app/registry.js",
|
|
19
|
+
"./dist/app/runtime.js",
|
|
20
|
+
"./dist/firestore/sandbox/write-runtime.js",
|
|
21
|
+
"./dist/rules/grammar/FirestoreParser.js",
|
|
22
|
+
"./dist/sandbox/admin-firestore/listeners.js",
|
|
23
|
+
"./dist/sandbox/internal/firebase-error.js",
|
|
24
|
+
"./src/app/register.ts",
|
|
25
|
+
"./src/app/registry.ts",
|
|
26
|
+
"./src/app/runtime.ts",
|
|
27
|
+
"./src/firestore/sandbox/write-runtime.ts",
|
|
28
|
+
"./src/rules/grammar/FirestoreParser.ts",
|
|
29
|
+
"./src/sandbox/admin-firestore/listeners.ts",
|
|
30
|
+
"./src/sandbox/internal/firebase-error.ts"
|
|
31
|
+
],
|
|
16
32
|
"exports": {
|
|
17
33
|
"./app": {
|
|
18
34
|
"types": "./dist/app/index.d.ts",
|
|
@@ -38,6 +54,10 @@
|
|
|
38
54
|
"types": "./dist/firestore/internal/value-codec.d.ts",
|
|
39
55
|
"import": "./dist/firestore/internal/value-codec.js"
|
|
40
56
|
},
|
|
57
|
+
"./firestore/lite": {
|
|
58
|
+
"types": "./dist/firestore/lite.d.ts",
|
|
59
|
+
"import": "./dist/firestore/lite.js"
|
|
60
|
+
},
|
|
41
61
|
"./auth": {
|
|
42
62
|
"types": "./dist/auth/index.d.ts",
|
|
43
63
|
"import": "./dist/auth/index.js"
|
|
@@ -125,6 +145,26 @@
|
|
|
125
145
|
"./sandbox/admin-firestore": {
|
|
126
146
|
"types": "./dist/sandbox/admin-firestore/index.d.ts",
|
|
127
147
|
"import": "./dist/sandbox/admin-firestore/index.js"
|
|
148
|
+
},
|
|
149
|
+
"./app-check": {
|
|
150
|
+
"types": "./dist/app-check/index.d.ts",
|
|
151
|
+
"import": "./dist/app-check/index.js"
|
|
152
|
+
},
|
|
153
|
+
"./functions": {
|
|
154
|
+
"types": "./dist/functions/index.d.ts",
|
|
155
|
+
"import": "./dist/functions/index.js"
|
|
156
|
+
},
|
|
157
|
+
"./analytics": {
|
|
158
|
+
"types": "./dist/analytics/index.d.ts",
|
|
159
|
+
"import": "./dist/analytics/index.js"
|
|
160
|
+
},
|
|
161
|
+
"./performance": {
|
|
162
|
+
"types": "./dist/performance/index.d.ts",
|
|
163
|
+
"import": "./dist/performance/index.js"
|
|
164
|
+
},
|
|
165
|
+
"./remote-config": {
|
|
166
|
+
"types": "./dist/remote-config/index.d.ts",
|
|
167
|
+
"import": "./dist/remote-config/index.js"
|
|
128
168
|
}
|
|
129
169
|
},
|
|
130
170
|
"files": [
|
|
@@ -150,7 +190,7 @@
|
|
|
150
190
|
"zod": "^3.23.0"
|
|
151
191
|
},
|
|
152
192
|
"devDependencies": {
|
|
153
|
-
"@types/bun": "
|
|
193
|
+
"@types/bun": "1.3.14",
|
|
154
194
|
"firebase": "^12.12.0",
|
|
155
195
|
"firebase-admin": "^13.0.0",
|
|
156
196
|
"typescript": "^5.7.0"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blocked-response detection: the ONE definition of "this envelope carries
|
|
3
|
+
* no usable content because a filter fired", shared by the two places that
|
|
4
|
+
* need it:
|
|
5
|
+
*
|
|
6
|
+
* - `response-helpers.ts`, which throws upstream's exact
|
|
7
|
+
* `Response error: Candidate was blocked due to <reason>` out of
|
|
8
|
+
* `text()` / `functionCalls()` / `inlineDataParts()` when the caller
|
|
9
|
+
* reaches for content that isn't there;
|
|
10
|
+
* - `broker/broker.ts`, which lands a `response_blocked` event on the
|
|
11
|
+
* sandbox's unified stream at RESPONSE time, so the block is visible
|
|
12
|
+
* even when nobody ever calls `text()` (an app that renders
|
|
13
|
+
* `response.text ?? ''` used to show an empty bubble and nothing else).
|
|
14
|
+
*
|
|
15
|
+
* A block is not a rejection: production answers HTTP 200 with an empty
|
|
16
|
+
* candidate and the reason in `finishReason` (SAFETY, RECITATION,
|
|
17
|
+
* BLOCKLIST, PROHIBITED_CONTENT, SPII, the IMAGE_* variants), or, when the
|
|
18
|
+
* PROMPT itself was refused, with no candidates at all and a
|
|
19
|
+
* `promptFeedback.blockReason`. Nothing throws on the wire path.
|
|
20
|
+
*
|
|
21
|
+
* Leaf module by construction: it imports only `./enums.js` (which imports
|
|
22
|
+
* nothing) and a type. `broker/` must NOT reach into `response-helpers.ts`
|
|
23
|
+
* for this: that file pulls `./errors.js`, which imports `./broker/index.js`
|
|
24
|
+
* as a value, and the round trip would be a real runtime cycle.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { WireResponse } from './broker/types.js';
|
|
28
|
+
import { FinishReason } from './enums.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Finish reasons that mean "no usable content": the installed
|
|
32
|
+
* `@firebase/ai@2.12.0` `badFinishReasons` set, verbatim and in upstream's
|
|
33
|
+
* order. Every one of these makes the response helpers throw instead of
|
|
34
|
+
* returning text, which is exactly the set worth announcing to a developer.
|
|
35
|
+
*/
|
|
36
|
+
const BLOCKING_FINISH_REASONS: readonly string[] = [
|
|
37
|
+
FinishReason.RECITATION,
|
|
38
|
+
FinishReason.SAFETY,
|
|
39
|
+
FinishReason.BLOCKLIST,
|
|
40
|
+
FinishReason.PROHIBITED_CONTENT,
|
|
41
|
+
FinishReason.SPII,
|
|
42
|
+
FinishReason.MALFORMED_FUNCTION_CALL,
|
|
43
|
+
FinishReason.IMAGE_SAFETY,
|
|
44
|
+
FinishReason.IMAGE_PROHIBITED_CONTENT,
|
|
45
|
+
FinishReason.IMAGE_OTHER,
|
|
46
|
+
FinishReason.NO_IMAGE,
|
|
47
|
+
FinishReason.IMAGE_RECITATION,
|
|
48
|
+
FinishReason.LANGUAGE,
|
|
49
|
+
FinishReason.UNEXPECTED_TOOL_CALL,
|
|
50
|
+
FinishReason.TOO_MANY_TOOL_CALLS,
|
|
51
|
+
FinishReason.MISSING_THOUGHT_SIGNATURE,
|
|
52
|
+
FinishReason.MALFORMED_RESPONSE,
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/** Does this candidate's finish reason mean the content was withheld? */
|
|
56
|
+
export function isBlockingFinishReason(finishReason: string | undefined): boolean {
|
|
57
|
+
if (!finishReason) return false;
|
|
58
|
+
return BLOCKING_FINISH_REASONS.some((reason) => reason === finishReason);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** What a block was, in the wire's own vocabulary. Exactly one of
|
|
62
|
+
* `finishReason` (a candidate was withheld) / `blockReason` (the prompt was
|
|
63
|
+
* refused) is normally set; both may be absent when production sent
|
|
64
|
+
* `promptFeedback` with no reason at all. */
|
|
65
|
+
export interface ResponseBlock {
|
|
66
|
+
/** Candidate-level: the blocking `finishReason` off candidate 0. */
|
|
67
|
+
finishReason?: string;
|
|
68
|
+
/** Candidate-level: production's own explanatory `finishMessage`. */
|
|
69
|
+
finishMessage?: string;
|
|
70
|
+
/** Prompt-level: `promptFeedback.blockReason`. */
|
|
71
|
+
blockReason?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Classify an envelope, mirroring `hasValidCandidates` in
|
|
76
|
+
* `response-helpers.ts` EXACTLY so the event stream and the thrown error can
|
|
77
|
+
* never disagree: candidate 0 with a blocking finish reason is a block; no
|
|
78
|
+
* candidate 0 at all plus any `promptFeedback` is a block; everything else
|
|
79
|
+
* (including a plain `STOP` or `MAX_TOKENS`) is not.
|
|
80
|
+
*
|
|
81
|
+
* Returns `null` when the response is fine, so a caller reads as
|
|
82
|
+
* `const block = describeResponseBlock(res); if (block !== null) …`.
|
|
83
|
+
*/
|
|
84
|
+
export function describeResponseBlock(response: WireResponse): ResponseBlock | null {
|
|
85
|
+
const firstCandidate = response.candidates?.[0];
|
|
86
|
+
if (firstCandidate !== undefined) {
|
|
87
|
+
if (!isBlockingFinishReason(firstCandidate.finishReason)) return null;
|
|
88
|
+
const candidateBlock: ResponseBlock = {};
|
|
89
|
+
candidateBlock.finishReason = firstCandidate.finishReason;
|
|
90
|
+
candidateBlock.finishMessage = firstCandidate.finishMessage;
|
|
91
|
+
return candidateBlock;
|
|
92
|
+
}
|
|
93
|
+
if (response.promptFeedback === undefined) return null;
|
|
94
|
+
const promptBlock: ResponseBlock = {};
|
|
95
|
+
promptBlock.blockReason = response.promptFeedback.blockReason;
|
|
96
|
+
return promptBlock;
|
|
97
|
+
}
|
package/src/ai/broker/broker.ts
CHANGED
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
* - a model-turn functionCall part with no thoughtSignature
|
|
19
19
|
* → `ai-error-fncall-missing-thought-signature`
|
|
20
20
|
*
|
|
21
|
+
* It also announces the refusals production does NOT throw for: a safety /
|
|
22
|
+
* recitation / blocklist block arrives as a 200 with an empty candidate, so
|
|
23
|
+
* the broker lands a `response_blocked` event ({@link emitBlockIfBlocked})
|
|
24
|
+
* alongside the normal op event. Without it a filtered answer is
|
|
25
|
+
* indistinguishable from a model that simply had nothing to say.
|
|
26
|
+
*
|
|
27
|
+
* The same goes for a model SUBSTITUTION ({@link emitModelSubstitution}): an
|
|
28
|
+
* engine that redirects the requested model (an openai `modelMap` entry or
|
|
29
|
+
* catch-all `model`, a gemini experimental alias) answers normally, so
|
|
30
|
+
* nothing anywhere says the developer tested a model they never named.
|
|
31
|
+
*
|
|
21
32
|
* Event emission is best-effort behind one small choke-point ({@link emit}):
|
|
22
33
|
* a throw from the emit path (or any event consumer downstream) must never
|
|
23
34
|
* poison the AI operation the caller just completed — handler errors are the
|
|
@@ -28,15 +39,18 @@
|
|
|
28
39
|
import type { Sandbox } from 'pyric/sandbox';
|
|
29
40
|
import { emitSandboxEvent, makeServiceMutationEvent } from 'pyric/sandbox/internal';
|
|
30
41
|
|
|
42
|
+
import { describeResponseBlock } from '../blocked.js';
|
|
31
43
|
import { AiBrokerError, Synthesizer, badRole, emptyContents, missingThoughtSignature } from './synthesizer.js';
|
|
32
44
|
import { ScriptedEngine } from './scripted-engine.js';
|
|
33
45
|
import { OpenAiEngine } from './openai-engine.js';
|
|
46
|
+
import { GeminiEngine } from './gemini-engine.js';
|
|
34
47
|
import type {
|
|
35
48
|
AnswerEngine,
|
|
36
49
|
CountTokensRequest,
|
|
37
50
|
CountTokensResponse,
|
|
38
51
|
EngineConfig,
|
|
39
52
|
GenerateContentRequest,
|
|
53
|
+
ModelResolution,
|
|
40
54
|
RawEnvelope,
|
|
41
55
|
WireChunk,
|
|
42
56
|
WireResponse,
|
|
@@ -75,7 +89,7 @@ function isAnswerEngine(value: EngineConfig | AnswerEngine): value is AnswerEngi
|
|
|
75
89
|
}
|
|
76
90
|
|
|
77
91
|
/** What Studio's stream (and the construction log line) name the engine. */
|
|
78
|
-
type EngineKind = 'scripted' | 'openai' | 'custom';
|
|
92
|
+
type EngineKind = 'scripted' | 'openai' | 'gemini' | 'custom';
|
|
79
93
|
|
|
80
94
|
export class AiBroker {
|
|
81
95
|
readonly engine: AnswerEngine;
|
|
@@ -93,6 +107,10 @@ export class AiBroker {
|
|
|
93
107
|
} else if (engine.kind === 'scripted') {
|
|
94
108
|
this.engine = new ScriptedEngine(engine.script ?? [], new Synthesizer());
|
|
95
109
|
this.engineKind = 'scripted';
|
|
110
|
+
} else if (engine.kind === 'gemini') {
|
|
111
|
+
this.engine = new GeminiEngine(engine);
|
|
112
|
+
this.engineKind = 'gemini';
|
|
113
|
+
this.engineBaseUrl = engine.baseUrl;
|
|
96
114
|
} else {
|
|
97
115
|
this.engine = new OpenAiEngine(engine);
|
|
98
116
|
this.engineKind = 'openai';
|
|
@@ -104,10 +122,16 @@ export class AiBroker {
|
|
|
104
122
|
|
|
105
123
|
/** One-line construction-time summary of what this broker resolved to. */
|
|
106
124
|
private describeEngine(): string {
|
|
107
|
-
|
|
125
|
+
const isOpenAiEngine = this.engineKind === 'openai';
|
|
126
|
+
if (isOpenAiEngine) {
|
|
108
127
|
return `[pyric/ai] engine resolved: openai (model=${this.engineModel ?? 'passthrough'}, upstream=${this.engineBaseUrl})`;
|
|
109
128
|
}
|
|
110
|
-
|
|
129
|
+
const isGeminiEngine = this.engineKind === 'gemini';
|
|
130
|
+
if (isGeminiEngine) {
|
|
131
|
+
return `[pyric/ai] engine resolved: gemini (upstream=${this.engineBaseUrl ?? 'https://generativelanguage.googleapis.com'})`;
|
|
132
|
+
}
|
|
133
|
+
const isCustomEngine = this.engineKind === 'custom';
|
|
134
|
+
if (isCustomEngine) {
|
|
111
135
|
return '[pyric/ai] engine resolved: custom AnswerEngine';
|
|
112
136
|
}
|
|
113
137
|
return '[pyric/ai] engine resolved: scripted (zero-config unless a script is queued)';
|
|
@@ -115,24 +139,109 @@ export class AiBroker {
|
|
|
115
139
|
|
|
116
140
|
/** Additive detail fields every emitted event carries so Studio can show the engine. */
|
|
117
141
|
private engineDetail(): Record<string, unknown> {
|
|
118
|
-
|
|
142
|
+
const detail: Record<string, unknown> = {
|
|
119
143
|
engine: this.engineKind,
|
|
120
|
-
...(this.engineKind === 'openai'
|
|
121
|
-
? { model: this.engineModel, baseUrl: this.engineBaseUrl }
|
|
122
|
-
: {}),
|
|
123
144
|
};
|
|
145
|
+
const isOpenAiEngine = this.engineKind === 'openai';
|
|
146
|
+
if (isOpenAiEngine) {
|
|
147
|
+
detail.model = this.engineModel;
|
|
148
|
+
detail.baseUrl = this.engineBaseUrl;
|
|
149
|
+
}
|
|
150
|
+
const isGeminiEngine = this.engineKind === 'gemini';
|
|
151
|
+
if (isGeminiEngine) {
|
|
152
|
+
detail.baseUrl = this.engineBaseUrl ?? 'https://generativelanguage.googleapis.com';
|
|
153
|
+
}
|
|
154
|
+
return detail;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Announce a SAFETY / RECITATION / BLOCKLIST / … block on the event stream.
|
|
159
|
+
*
|
|
160
|
+
* A block is the broker's SILENT refusal: unlike `request_rejected` nothing
|
|
161
|
+
* throws (production answers 200 with an empty candidate), so an app that
|
|
162
|
+
* renders whatever `text()` gave it just shows nothing, and the developer
|
|
163
|
+
* gets no signal anywhere. Emitting at RESPONSE time (not from the response
|
|
164
|
+
* helpers, which only fire if someone reaches for content) means the event
|
|
165
|
+
* lands whether or not the caller ever asks for the text.
|
|
166
|
+
*
|
|
167
|
+
* Detection is `blocked.ts`'s, shared verbatim with the response helpers'
|
|
168
|
+
* throw path, so the stream and the thrown error can never disagree.
|
|
169
|
+
*
|
|
170
|
+
* Returns whether the envelope WAS blocked, so the stream wrapper can
|
|
171
|
+
* announce at most once across its chunks.
|
|
172
|
+
*/
|
|
173
|
+
private emitBlockIfBlocked(model: string, response: WireResponse): boolean {
|
|
174
|
+
const block = describeResponseBlock(response);
|
|
175
|
+
if (block === null) return false;
|
|
176
|
+
this.emit('response_blocked', model, { ...block });
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Announce that the engine will answer as a DIFFERENT model than the one
|
|
182
|
+
* requested.
|
|
183
|
+
*
|
|
184
|
+
* A substitution is the quietest failure the broker has: nothing throws,
|
|
185
|
+
* content comes back, and the developer concludes they tested model X when
|
|
186
|
+
* model Y answered (an openai `modelMap` entry or catch-all `model`, a
|
|
187
|
+
* gemini experimental alias). Only engines that actually redirect implement
|
|
188
|
+
* {@link AnswerEngine.resolveEffectiveModel}; the rest are silent by
|
|
189
|
+
* construction.
|
|
190
|
+
*
|
|
191
|
+
* Fired at REQUEST time, before delegating: the swap is a property of the
|
|
192
|
+
* call about to go out, so it lands even when that call then fails.
|
|
193
|
+
* Comparison is bare-vs-bare, since `models/x` to `x` is the wire's prefix
|
|
194
|
+
* convention rather than a substitution, and an exact match emits nothing.
|
|
195
|
+
*
|
|
196
|
+
* `countTokens` is deliberately not announced: the openai engine never
|
|
197
|
+
* sends a model there at all (the count is synthesized locally), so an
|
|
198
|
+
* announcement would name an upstream call that never happens.
|
|
199
|
+
*/
|
|
200
|
+
private emitModelSubstitution(model: string): void {
|
|
201
|
+
const resolve = this.engine.resolveEffectiveModel;
|
|
202
|
+
if (typeof resolve !== 'function') return;
|
|
203
|
+
let effective: ModelResolution;
|
|
204
|
+
try {
|
|
205
|
+
effective = resolve.call(this.engine, model);
|
|
206
|
+
} catch {
|
|
207
|
+
// A misbehaving engine's self-report must never fail the op.
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const requestedBare = model.startsWith('models/') ? model.slice('models/'.length) : model;
|
|
211
|
+
if (effective.model === requestedBare) return;
|
|
212
|
+
this.emit('model_substituted', model, {
|
|
213
|
+
requestedModel: model,
|
|
214
|
+
effectiveModel: effective.model,
|
|
215
|
+
reason: effective.reason,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private emitRejectionIfBrokerError(model: string, err: unknown): void {
|
|
220
|
+
const code = err instanceof AiBrokerError ? err.envelope.error.code : 500;
|
|
221
|
+
const status = err instanceof AiBrokerError ? err.envelope.error.status : 'INTERNAL';
|
|
222
|
+
const message = err instanceof AiBrokerError
|
|
223
|
+
? err.envelope.error.message
|
|
224
|
+
: err instanceof Error ? err.message : String(err);
|
|
225
|
+
this.emit('request_rejected', model, { code, status, message });
|
|
124
226
|
}
|
|
125
227
|
|
|
126
228
|
async generateContent(req: GenerateContentRequest, model: string): Promise<WireResponse> {
|
|
127
229
|
this.validate(req, model);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
230
|
+
this.emitModelSubstitution(model);
|
|
231
|
+
try {
|
|
232
|
+
const response = await this.engine.generateContent(req, model);
|
|
233
|
+
this.emit('generate_content', model, {
|
|
234
|
+
contentCount: req.contents.length,
|
|
235
|
+
finishReason: response.candidates?.[0]?.finishReason,
|
|
236
|
+
totalTokenCount: response.usageMetadata?.totalTokenCount,
|
|
237
|
+
responseId: response.responseId,
|
|
238
|
+
});
|
|
239
|
+
this.emitBlockIfBlocked(model, response);
|
|
240
|
+
return response;
|
|
241
|
+
} catch (err) {
|
|
242
|
+
this.emitRejectionIfBrokerError(model, err);
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
136
245
|
}
|
|
137
246
|
|
|
138
247
|
/**
|
|
@@ -143,27 +252,49 @@ export class AiBroker {
|
|
|
143
252
|
*/
|
|
144
253
|
streamGenerateContent(req: GenerateContentRequest, model: string): AsyncIterable<WireChunk> {
|
|
145
254
|
this.validate(req, model);
|
|
255
|
+
this.emitModelSubstitution(model);
|
|
146
256
|
const inner = this.engine.streamGenerateContent(req, model);
|
|
147
257
|
const emit = (chunkCount: number) =>
|
|
148
258
|
this.emit('stream_generate_content', model, {
|
|
149
259
|
contentCount: req.contents.length,
|
|
150
260
|
chunkCount,
|
|
151
261
|
});
|
|
262
|
+
const emitRejection = (err: unknown) => this.emitRejectionIfBrokerError(model, err);
|
|
263
|
+
// A block surfaces on whichever chunk carries the finish reason (the last
|
|
264
|
+
// one, per the captured framing), so it is announced DURING iteration:
|
|
265
|
+
// an abandoned stream still reports why it produced nothing. At most one
|
|
266
|
+
// per stream: the aggregate has a single blocking reason, not one per chunk.
|
|
267
|
+
let announcedBlock = false;
|
|
268
|
+
const emitBlock = (chunk: WireChunk) => {
|
|
269
|
+
if (announcedBlock) return;
|
|
270
|
+
announcedBlock = this.emitBlockIfBlocked(model, chunk);
|
|
271
|
+
};
|
|
152
272
|
return (async function* wrapped(): AsyncGenerator<WireChunk> {
|
|
153
273
|
let chunkCount = 0;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
274
|
+
try {
|
|
275
|
+
for await (const chunk of inner) {
|
|
276
|
+
chunkCount++;
|
|
277
|
+
emitBlock(chunk);
|
|
278
|
+
yield chunk;
|
|
279
|
+
}
|
|
280
|
+
emit(chunkCount);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
emitRejection(err);
|
|
283
|
+
throw err;
|
|
157
284
|
}
|
|
158
|
-
emit(chunkCount);
|
|
159
285
|
})();
|
|
160
286
|
}
|
|
161
287
|
|
|
162
288
|
async countTokens(req: CountTokensRequest, model: string): Promise<CountTokensResponse> {
|
|
163
289
|
this.validateContents(req, model);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
290
|
+
try {
|
|
291
|
+
const response = await this.engine.countTokens(req, model);
|
|
292
|
+
this.emit('count_tokens', model, { totalTokens: response.totalTokens });
|
|
293
|
+
return response;
|
|
294
|
+
} catch (err) {
|
|
295
|
+
this.emitRejectionIfBrokerError(model, err);
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
167
298
|
}
|
|
168
299
|
|
|
169
300
|
// ── Production-shaped validation ──────────────────────────────────────────
|