@frengki0707/google-cloud-clone 1.33.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/LICENSE +203 -0
- package/README.md +83 -0
- package/lib/auth.d.mts +33 -0
- package/lib/auth.d.ts +33 -0
- package/lib/auth.js +70 -0
- package/lib/auth.js.map +1 -0
- package/lib/auth.mjs +45 -0
- package/lib/auth.mjs.map +1 -0
- package/lib/gcpLogger.d.mts +25 -0
- package/lib/gcpLogger.d.ts +25 -0
- package/lib/gcpLogger.js +118 -0
- package/lib/gcpLogger.js.map +1 -0
- package/lib/gcpLogger.mjs +82 -0
- package/lib/gcpLogger.mjs.map +1 -0
- package/lib/gcpOpenTelemetry.d.mts +59 -0
- package/lib/gcpOpenTelemetry.d.ts +59 -0
- package/lib/gcpOpenTelemetry.js +374 -0
- package/lib/gcpOpenTelemetry.js.map +1 -0
- package/lib/gcpOpenTelemetry.mjs +364 -0
- package/lib/gcpOpenTelemetry.mjs.map +1 -0
- package/lib/index.d.mts +36 -0
- package/lib/index.d.ts +36 -0
- package/lib/index.js +56 -0
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +29 -0
- package/lib/index.mjs.map +1 -0
- package/lib/metrics.d.mts +65 -0
- package/lib/metrics.d.ts +65 -0
- package/lib/metrics.js +91 -0
- package/lib/metrics.js.map +1 -0
- package/lib/metrics.mjs +65 -0
- package/lib/metrics.mjs.map +1 -0
- package/lib/model-armor.d.mts +59 -0
- package/lib/model-armor.d.ts +59 -0
- package/lib/model-armor.js +205 -0
- package/lib/model-armor.js.map +1 -0
- package/lib/model-armor.mjs +181 -0
- package/lib/model-armor.mjs.map +1 -0
- package/lib/telemetry/action.d.mts +27 -0
- package/lib/telemetry/action.d.ts +27 -0
- package/lib/telemetry/action.js +92 -0
- package/lib/telemetry/action.js.map +1 -0
- package/lib/telemetry/action.mjs +73 -0
- package/lib/telemetry/action.mjs.map +1 -0
- package/lib/telemetry/defaults.d.mts +30 -0
- package/lib/telemetry/defaults.d.ts +30 -0
- package/lib/telemetry/defaults.js +70 -0
- package/lib/telemetry/defaults.js.map +1 -0
- package/lib/telemetry/defaults.mjs +46 -0
- package/lib/telemetry/defaults.mjs.map +1 -0
- package/lib/telemetry/engagement.d.mts +35 -0
- package/lib/telemetry/engagement.d.ts +35 -0
- package/lib/telemetry/engagement.js +106 -0
- package/lib/telemetry/engagement.js.map +1 -0
- package/lib/telemetry/engagement.mjs +85 -0
- package/lib/telemetry/engagement.mjs.map +1 -0
- package/lib/telemetry/feature.d.mts +35 -0
- package/lib/telemetry/feature.d.ts +35 -0
- package/lib/telemetry/feature.js +142 -0
- package/lib/telemetry/feature.js.map +1 -0
- package/lib/telemetry/feature.mjs +127 -0
- package/lib/telemetry/feature.mjs.map +1 -0
- package/lib/telemetry/generate.d.mts +53 -0
- package/lib/telemetry/generate.d.ts +53 -0
- package/lib/telemetry/generate.js +326 -0
- package/lib/telemetry/generate.js.map +1 -0
- package/lib/telemetry/generate.mjs +314 -0
- package/lib/telemetry/generate.mjs.map +1 -0
- package/lib/telemetry/path.d.mts +32 -0
- package/lib/telemetry/path.d.ts +32 -0
- package/lib/telemetry/path.js +91 -0
- package/lib/telemetry/path.js.map +1 -0
- package/lib/telemetry/path.mjs +78 -0
- package/lib/telemetry/path.mjs.map +1 -0
- package/lib/types.d.mts +121 -0
- package/lib/types.d.ts +121 -0
- package/lib/types.js +17 -0
- package/lib/types.js.map +1 -0
- package/lib/types.mjs +1 -0
- package/lib/types.mjs.map +1 -0
- package/lib/utils.d.mts +57 -0
- package/lib/utils.d.ts +57 -0
- package/lib/utils.js +143 -0
- package/lib/utils.js.map +1 -0
- package/lib/utils.mjs +104 -0
- package/lib/utils.mjs.map +1 -0
- package/package.json +89 -0
- package/src/auth.ts +89 -0
- package/src/gcpLogger.ts +124 -0
- package/src/gcpOpenTelemetry.ts +485 -0
- package/src/index.ts +59 -0
- package/src/metrics.ts +122 -0
- package/src/model-armor.ts +317 -0
- package/src/telemetry/action.ts +106 -0
- package/src/telemetry/defaults.ts +72 -0
- package/src/telemetry/engagement.ts +120 -0
- package/src/telemetry/feature.ts +170 -0
- package/src/telemetry/generate.ts +454 -0
- package/src/telemetry/path.ts +111 -0
- package/src/types.ts +133 -0
- package/src/utils.ts +175 -0
- package/tests/logs_no_input_output_test.ts +267 -0
- package/tests/logs_session_test.ts +219 -0
- package/tests/logs_test.ts +633 -0
- package/tests/metrics_test.ts +792 -0
- package/tests/model_armor_test.ts +336 -0
- package/tests/traces_test.ts +380 -0
- package/typedoc.json +3 -0
package/lib/metrics.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Meter, Counter, Histogram } from '@opentelemetry/api';
|
|
2
|
+
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Copyright 2024 Google LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
declare const METER_NAME = "genkit";
|
|
21
|
+
declare const METRIC_NAME_PREFIX = "genkit";
|
|
22
|
+
declare function internalMetricNamespaceWrap(...namespaces: any[]): string;
|
|
23
|
+
type MetricCreateFn<T> = (meter: Meter) => T;
|
|
24
|
+
/**
|
|
25
|
+
* Wrapper for OpenTelemetry metrics.
|
|
26
|
+
*
|
|
27
|
+
* The OpenTelemetry {MeterProvider} can only be accessed through the metrics
|
|
28
|
+
* API after the NodeSDK library has been initialized. To prevent race
|
|
29
|
+
* conditions we defer the instantiation of the metric to when it is first
|
|
30
|
+
* ticked.
|
|
31
|
+
*/
|
|
32
|
+
declare class Metric<T> {
|
|
33
|
+
readonly createFn: MetricCreateFn<T>;
|
|
34
|
+
readonly meterName: string;
|
|
35
|
+
metric?: T;
|
|
36
|
+
constructor(createFn: MetricCreateFn<T>, meterName?: string);
|
|
37
|
+
get(): T;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wrapper for an OpenTelemetry Counter.
|
|
41
|
+
*
|
|
42
|
+
* By using this wrapper, we defer initialization of the counter until it is
|
|
43
|
+
* need, which ensures that the OpenTelemetry SDK has been initialized before
|
|
44
|
+
* the metric has been defined.
|
|
45
|
+
*/
|
|
46
|
+
declare class MetricCounter extends Metric<Counter> {
|
|
47
|
+
constructor(name: string, options: any);
|
|
48
|
+
add(val?: number, opts?: any): void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Wrapper for an OpenTelemetry Histogram.
|
|
52
|
+
*
|
|
53
|
+
* By using this wrapper, we defer initialization of the counter until it is
|
|
54
|
+
* need, which ensures that the OpenTelemetry SDK has been initialized before
|
|
55
|
+
* the metric has been defined.
|
|
56
|
+
*/
|
|
57
|
+
declare class MetricHistogram extends Metric<Histogram> {
|
|
58
|
+
constructor(name: string, options: any);
|
|
59
|
+
record(val?: number, opts?: any): void;
|
|
60
|
+
}
|
|
61
|
+
interface Telemetry {
|
|
62
|
+
tick(span: ReadableSpan, logIO: boolean, projectId?: string): void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { METER_NAME, METRIC_NAME_PREFIX, MetricCounter, MetricHistogram, type Telemetry, internalMetricNamespaceWrap };
|
package/lib/metrics.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var metrics_exports = {};
|
|
20
|
+
__export(metrics_exports, {
|
|
21
|
+
METER_NAME: () => METER_NAME,
|
|
22
|
+
METRIC_NAME_PREFIX: () => METRIC_NAME_PREFIX,
|
|
23
|
+
MetricCounter: () => MetricCounter,
|
|
24
|
+
MetricHistogram: () => MetricHistogram,
|
|
25
|
+
internalMetricNamespaceWrap: () => internalMetricNamespaceWrap
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(metrics_exports);
|
|
28
|
+
var import_api = require("@opentelemetry/api");
|
|
29
|
+
const METER_NAME = "genkit";
|
|
30
|
+
const METRIC_NAME_PREFIX = "genkit";
|
|
31
|
+
const METRIC_DIMENSION_MAX_CHARS = 256;
|
|
32
|
+
function internalMetricNamespaceWrap(...namespaces) {
|
|
33
|
+
return [METRIC_NAME_PREFIX, ...namespaces].join("/");
|
|
34
|
+
}
|
|
35
|
+
class Metric {
|
|
36
|
+
createFn;
|
|
37
|
+
meterName;
|
|
38
|
+
metric;
|
|
39
|
+
constructor(createFn, meterName = METER_NAME) {
|
|
40
|
+
this.meterName = meterName;
|
|
41
|
+
this.createFn = createFn;
|
|
42
|
+
}
|
|
43
|
+
get() {
|
|
44
|
+
if (!this.metric) {
|
|
45
|
+
this.metric = this.createFn(
|
|
46
|
+
import_api.metrics.getMeterProvider().getMeter(this.meterName)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return this.metric;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
class MetricCounter extends Metric {
|
|
53
|
+
constructor(name, options) {
|
|
54
|
+
super((meter) => meter.createCounter(name, options));
|
|
55
|
+
}
|
|
56
|
+
add(val, opts) {
|
|
57
|
+
if (val) {
|
|
58
|
+
truncateDimensions(opts);
|
|
59
|
+
this.get().add(val, opts);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
class MetricHistogram extends Metric {
|
|
64
|
+
constructor(name, options) {
|
|
65
|
+
super((meter) => meter.createHistogram(name, options));
|
|
66
|
+
}
|
|
67
|
+
record(val, opts) {
|
|
68
|
+
if (val) {
|
|
69
|
+
truncateDimensions(opts);
|
|
70
|
+
this.get().record(val, opts);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function truncateDimensions(opts) {
|
|
75
|
+
if (opts) {
|
|
76
|
+
Object.keys(opts).forEach((k) => {
|
|
77
|
+
if (typeof opts[k] === "string") {
|
|
78
|
+
opts[k] = opts[k].substring(0, METRIC_DIMENSION_MAX_CHARS);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
84
|
+
0 && (module.exports = {
|
|
85
|
+
METER_NAME,
|
|
86
|
+
METRIC_NAME_PREFIX,
|
|
87
|
+
MetricCounter,
|
|
88
|
+
MetricHistogram,
|
|
89
|
+
internalMetricNamespaceWrap
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n metrics,\n type Counter,\n type Histogram,\n type Meter,\n} from '@opentelemetry/api';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\n\nexport const METER_NAME = 'genkit';\nexport const METRIC_NAME_PREFIX = 'genkit';\nconst METRIC_DIMENSION_MAX_CHARS = 256;\n\nexport function internalMetricNamespaceWrap(...namespaces) {\n return [METRIC_NAME_PREFIX, ...namespaces].join('/');\n}\n\ntype MetricCreateFn<T> = (meter: Meter) => T;\n\n/**\n * Wrapper for OpenTelemetry metrics.\n *\n * The OpenTelemetry {MeterProvider} can only be accessed through the metrics\n * API after the NodeSDK library has been initialized. To prevent race\n * conditions we defer the instantiation of the metric to when it is first\n * ticked.\n */\nclass Metric<T> {\n readonly createFn: MetricCreateFn<T>;\n readonly meterName: string;\n metric?: T;\n\n constructor(createFn: MetricCreateFn<T>, meterName: string = METER_NAME) {\n this.meterName = meterName;\n this.createFn = createFn;\n }\n\n get(): T {\n if (!this.metric) {\n this.metric = this.createFn(\n metrics.getMeterProvider().getMeter(this.meterName)\n );\n }\n\n return this.metric;\n }\n}\n\n/**\n * Wrapper for an OpenTelemetry Counter.\n *\n * By using this wrapper, we defer initialization of the counter until it is\n * need, which ensures that the OpenTelemetry SDK has been initialized before\n * the metric has been defined.\n */\nexport class MetricCounter extends Metric<Counter> {\n constructor(name: string, options: any) {\n super((meter) => meter.createCounter(name, options));\n }\n\n add(val?: number, opts?: any) {\n if (val) {\n truncateDimensions(opts);\n this.get().add(val, opts);\n }\n }\n}\n\n/**\n * Wrapper for an OpenTelemetry Histogram.\n *\n * By using this wrapper, we defer initialization of the counter until it is\n * need, which ensures that the OpenTelemetry SDK has been initialized before\n * the metric has been defined.\n */\nexport class MetricHistogram extends Metric<Histogram> {\n constructor(name: string, options: any) {\n super((meter) => meter.createHistogram(name, options));\n }\n\n record(val?: number, opts?: any) {\n if (val) {\n truncateDimensions(opts);\n this.get().record(val, opts);\n }\n }\n}\n\n/**\n * Truncates all of the metric dimensions to ensure they are below the trace\n * attribute max. Labels should be consistent between metrics and traces so that\n * data can be properly correlated. This will truncate all dimensions for\n * metrics such that they will be consistent with data included in traces.\n */\nfunction truncateDimensions(opts?: any) {\n if (opts) {\n Object.keys(opts).forEach((k) => {\n if (typeof opts[k] === 'string') {\n opts[k] = opts[k].substring(0, METRIC_DIMENSION_MAX_CHARS);\n }\n });\n }\n}\n\nexport interface Telemetry {\n tick(span: ReadableSpan, logIO: boolean, projectId?: string): void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,iBAKO;AAGA,MAAM,aAAa;AACnB,MAAM,qBAAqB;AAClC,MAAM,6BAA6B;AAE5B,SAAS,+BAA+B,YAAY;AACzD,SAAO,CAAC,oBAAoB,GAAG,UAAU,EAAE,KAAK,GAAG;AACrD;AAYA,MAAM,OAAU;AAAA,EACL;AAAA,EACA;AAAA,EACT;AAAA,EAEA,YAAY,UAA6B,YAAoB,YAAY;AACvE,SAAK,YAAY;AACjB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,MAAS;AACP,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,SAAS,KAAK;AAAA,QACjB,mBAAQ,iBAAiB,EAAE,SAAS,KAAK,SAAS;AAAA,MACpD;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;AASO,MAAM,sBAAsB,OAAgB;AAAA,EACjD,YAAY,MAAc,SAAc;AACtC,UAAM,CAAC,UAAU,MAAM,cAAc,MAAM,OAAO,CAAC;AAAA,EACrD;AAAA,EAEA,IAAI,KAAc,MAAY;AAC5B,QAAI,KAAK;AACP,yBAAmB,IAAI;AACvB,WAAK,IAAI,EAAE,IAAI,KAAK,IAAI;AAAA,IAC1B;AAAA,EACF;AACF;AASO,MAAM,wBAAwB,OAAkB;AAAA,EACrD,YAAY,MAAc,SAAc;AACtC,UAAM,CAAC,UAAU,MAAM,gBAAgB,MAAM,OAAO,CAAC;AAAA,EACvD;AAAA,EAEA,OAAO,KAAc,MAAY;AAC/B,QAAI,KAAK;AACP,yBAAmB,IAAI;AACvB,WAAK,IAAI,EAAE,OAAO,KAAK,IAAI;AAAA,IAC7B;AAAA,EACF;AACF;AAQA,SAAS,mBAAmB,MAAY;AACtC,MAAI,MAAM;AACR,WAAO,KAAK,IAAI,EAAE,QAAQ,CAAC,MAAM;AAC/B,UAAI,OAAO,KAAK,CAAC,MAAM,UAAU;AAC/B,aAAK,CAAC,IAAI,KAAK,CAAC,EAAE,UAAU,GAAG,0BAA0B;AAAA,MAC3D;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|
package/lib/metrics.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
metrics
|
|
3
|
+
} from "@opentelemetry/api";
|
|
4
|
+
const METER_NAME = "genkit";
|
|
5
|
+
const METRIC_NAME_PREFIX = "genkit";
|
|
6
|
+
const METRIC_DIMENSION_MAX_CHARS = 256;
|
|
7
|
+
function internalMetricNamespaceWrap(...namespaces) {
|
|
8
|
+
return [METRIC_NAME_PREFIX, ...namespaces].join("/");
|
|
9
|
+
}
|
|
10
|
+
class Metric {
|
|
11
|
+
createFn;
|
|
12
|
+
meterName;
|
|
13
|
+
metric;
|
|
14
|
+
constructor(createFn, meterName = METER_NAME) {
|
|
15
|
+
this.meterName = meterName;
|
|
16
|
+
this.createFn = createFn;
|
|
17
|
+
}
|
|
18
|
+
get() {
|
|
19
|
+
if (!this.metric) {
|
|
20
|
+
this.metric = this.createFn(
|
|
21
|
+
metrics.getMeterProvider().getMeter(this.meterName)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return this.metric;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
class MetricCounter extends Metric {
|
|
28
|
+
constructor(name, options) {
|
|
29
|
+
super((meter) => meter.createCounter(name, options));
|
|
30
|
+
}
|
|
31
|
+
add(val, opts) {
|
|
32
|
+
if (val) {
|
|
33
|
+
truncateDimensions(opts);
|
|
34
|
+
this.get().add(val, opts);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
class MetricHistogram extends Metric {
|
|
39
|
+
constructor(name, options) {
|
|
40
|
+
super((meter) => meter.createHistogram(name, options));
|
|
41
|
+
}
|
|
42
|
+
record(val, opts) {
|
|
43
|
+
if (val) {
|
|
44
|
+
truncateDimensions(opts);
|
|
45
|
+
this.get().record(val, opts);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function truncateDimensions(opts) {
|
|
50
|
+
if (opts) {
|
|
51
|
+
Object.keys(opts).forEach((k) => {
|
|
52
|
+
if (typeof opts[k] === "string") {
|
|
53
|
+
opts[k] = opts[k].substring(0, METRIC_DIMENSION_MAX_CHARS);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
METER_NAME,
|
|
60
|
+
METRIC_NAME_PREFIX,
|
|
61
|
+
MetricCounter,
|
|
62
|
+
MetricHistogram,
|
|
63
|
+
internalMetricNamespaceWrap
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=metrics.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n metrics,\n type Counter,\n type Histogram,\n type Meter,\n} from '@opentelemetry/api';\nimport type { ReadableSpan } from '@opentelemetry/sdk-trace-base';\n\nexport const METER_NAME = 'genkit';\nexport const METRIC_NAME_PREFIX = 'genkit';\nconst METRIC_DIMENSION_MAX_CHARS = 256;\n\nexport function internalMetricNamespaceWrap(...namespaces) {\n return [METRIC_NAME_PREFIX, ...namespaces].join('/');\n}\n\ntype MetricCreateFn<T> = (meter: Meter) => T;\n\n/**\n * Wrapper for OpenTelemetry metrics.\n *\n * The OpenTelemetry {MeterProvider} can only be accessed through the metrics\n * API after the NodeSDK library has been initialized. To prevent race\n * conditions we defer the instantiation of the metric to when it is first\n * ticked.\n */\nclass Metric<T> {\n readonly createFn: MetricCreateFn<T>;\n readonly meterName: string;\n metric?: T;\n\n constructor(createFn: MetricCreateFn<T>, meterName: string = METER_NAME) {\n this.meterName = meterName;\n this.createFn = createFn;\n }\n\n get(): T {\n if (!this.metric) {\n this.metric = this.createFn(\n metrics.getMeterProvider().getMeter(this.meterName)\n );\n }\n\n return this.metric;\n }\n}\n\n/**\n * Wrapper for an OpenTelemetry Counter.\n *\n * By using this wrapper, we defer initialization of the counter until it is\n * need, which ensures that the OpenTelemetry SDK has been initialized before\n * the metric has been defined.\n */\nexport class MetricCounter extends Metric<Counter> {\n constructor(name: string, options: any) {\n super((meter) => meter.createCounter(name, options));\n }\n\n add(val?: number, opts?: any) {\n if (val) {\n truncateDimensions(opts);\n this.get().add(val, opts);\n }\n }\n}\n\n/**\n * Wrapper for an OpenTelemetry Histogram.\n *\n * By using this wrapper, we defer initialization of the counter until it is\n * need, which ensures that the OpenTelemetry SDK has been initialized before\n * the metric has been defined.\n */\nexport class MetricHistogram extends Metric<Histogram> {\n constructor(name: string, options: any) {\n super((meter) => meter.createHistogram(name, options));\n }\n\n record(val?: number, opts?: any) {\n if (val) {\n truncateDimensions(opts);\n this.get().record(val, opts);\n }\n }\n}\n\n/**\n * Truncates all of the metric dimensions to ensure they are below the trace\n * attribute max. Labels should be consistent between metrics and traces so that\n * data can be properly correlated. This will truncate all dimensions for\n * metrics such that they will be consistent with data included in traces.\n */\nfunction truncateDimensions(opts?: any) {\n if (opts) {\n Object.keys(opts).forEach((k) => {\n if (typeof opts[k] === 'string') {\n opts[k] = opts[k].substring(0, METRIC_DIMENSION_MAX_CHARS);\n }\n });\n }\n}\n\nexport interface Telemetry {\n tick(span: ReadableSpan, logIO: boolean, projectId?: string): void;\n}\n"],"mappings":"AAgBA;AAAA,EACE;AAAA,OAIK;AAGA,MAAM,aAAa;AACnB,MAAM,qBAAqB;AAClC,MAAM,6BAA6B;AAE5B,SAAS,+BAA+B,YAAY;AACzD,SAAO,CAAC,oBAAoB,GAAG,UAAU,EAAE,KAAK,GAAG;AACrD;AAYA,MAAM,OAAU;AAAA,EACL;AAAA,EACA;AAAA,EACT;AAAA,EAEA,YAAY,UAA6B,YAAoB,YAAY;AACvE,SAAK,YAAY;AACjB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,MAAS;AACP,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,SAAS,KAAK;AAAA,QACjB,QAAQ,iBAAiB,EAAE,SAAS,KAAK,SAAS;AAAA,MACpD;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,EACd;AACF;AASO,MAAM,sBAAsB,OAAgB;AAAA,EACjD,YAAY,MAAc,SAAc;AACtC,UAAM,CAAC,UAAU,MAAM,cAAc,MAAM,OAAO,CAAC;AAAA,EACrD;AAAA,EAEA,IAAI,KAAc,MAAY;AAC5B,QAAI,KAAK;AACP,yBAAmB,IAAI;AACvB,WAAK,IAAI,EAAE,IAAI,KAAK,IAAI;AAAA,IAC1B;AAAA,EACF;AACF;AASO,MAAM,wBAAwB,OAAkB;AAAA,EACrD,YAAY,MAAc,SAAc;AACtC,UAAM,CAAC,UAAU,MAAM,gBAAgB,MAAM,OAAO,CAAC;AAAA,EACvD;AAAA,EAEA,OAAO,KAAc,MAAY;AAC/B,QAAI,KAAK;AACP,yBAAmB,IAAI;AACvB,WAAK,IAAI,EAAE,OAAO,KAAK,IAAI;AAAA,IAC7B;AAAA,EACF;AACF;AAQA,SAAS,mBAAmB,MAAY;AACtC,MAAI,MAAM;AACR,WAAO,KAAK,IAAI,EAAE,QAAQ,CAAC,MAAM;AAC/B,UAAI,OAAO,KAAK,CAAC,MAAM,UAAU;AAC/B,aAAK,CAAC,IAAI,KAAK,CAAC,EAAE,UAAU,GAAG,0BAA0B;AAAA,MAC3D;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ModelArmorClient, protos } from '@google-cloud/modelarmor';
|
|
2
|
+
import { MessageData, ModelMiddleware } from 'genkit/model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Copyright 2025 Google LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface ModelArmorOptions {
|
|
21
|
+
templateName: string;
|
|
22
|
+
client?: ModelArmorClient;
|
|
23
|
+
/**
|
|
24
|
+
* Options for the Model Armor client (e.g. apiEndpoint).
|
|
25
|
+
*/
|
|
26
|
+
clientOptions?: ConstructorParameters<typeof ModelArmorClient>[0];
|
|
27
|
+
/**
|
|
28
|
+
* What to sanitize. Defaults to 'all'.
|
|
29
|
+
*/
|
|
30
|
+
protectionTarget?: 'all' | 'userPrompt' | 'modelResponse';
|
|
31
|
+
/**
|
|
32
|
+
* Whether to block on SDP match even if the content was successfully de-identified.
|
|
33
|
+
* Defaults to false (lenient).
|
|
34
|
+
*/
|
|
35
|
+
strictSdpEnforcement?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* List of filters to enforce. If not specified, all filters are enforced.
|
|
38
|
+
* Possible values: 'rai', 'pi_and_jailbreak', 'malicious_uris', 'csam', 'sdp'.
|
|
39
|
+
*/
|
|
40
|
+
filters?: ('rai' | 'pi_and_jailbreak' | 'malicious_uris' | 'csam' | 'sdp' | (string & {}))[];
|
|
41
|
+
/**
|
|
42
|
+
* Whether to apply the de-identification results to the content.
|
|
43
|
+
* - If true, the default logic (replace text, preserve structure) is used.
|
|
44
|
+
* - If false, no changes are applied.
|
|
45
|
+
* - If a function, it is called with the messages and SDP result, and should return the new messages.
|
|
46
|
+
*
|
|
47
|
+
* Defaults to false.
|
|
48
|
+
*/
|
|
49
|
+
applyDeidentificationResults?: boolean | ((data: {
|
|
50
|
+
messages: MessageData[];
|
|
51
|
+
sdpResult: protos.google.cloud.modelarmor.v1.ISdpFilterResult;
|
|
52
|
+
}) => MessageData[] | undefined);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Model Middleware that uses Google Cloud Model Armor to sanitize user prompts and model responses.
|
|
56
|
+
*/
|
|
57
|
+
declare function modelArmor(options: ModelArmorOptions): ModelMiddleware;
|
|
58
|
+
|
|
59
|
+
export { type ModelArmorOptions, modelArmor };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ModelArmorClient, protos } from '@google-cloud/modelarmor';
|
|
2
|
+
import { MessageData, ModelMiddleware } from 'genkit/model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Copyright 2025 Google LLC
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface ModelArmorOptions {
|
|
21
|
+
templateName: string;
|
|
22
|
+
client?: ModelArmorClient;
|
|
23
|
+
/**
|
|
24
|
+
* Options for the Model Armor client (e.g. apiEndpoint).
|
|
25
|
+
*/
|
|
26
|
+
clientOptions?: ConstructorParameters<typeof ModelArmorClient>[0];
|
|
27
|
+
/**
|
|
28
|
+
* What to sanitize. Defaults to 'all'.
|
|
29
|
+
*/
|
|
30
|
+
protectionTarget?: 'all' | 'userPrompt' | 'modelResponse';
|
|
31
|
+
/**
|
|
32
|
+
* Whether to block on SDP match even if the content was successfully de-identified.
|
|
33
|
+
* Defaults to false (lenient).
|
|
34
|
+
*/
|
|
35
|
+
strictSdpEnforcement?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* List of filters to enforce. If not specified, all filters are enforced.
|
|
38
|
+
* Possible values: 'rai', 'pi_and_jailbreak', 'malicious_uris', 'csam', 'sdp'.
|
|
39
|
+
*/
|
|
40
|
+
filters?: ('rai' | 'pi_and_jailbreak' | 'malicious_uris' | 'csam' | 'sdp' | (string & {}))[];
|
|
41
|
+
/**
|
|
42
|
+
* Whether to apply the de-identification results to the content.
|
|
43
|
+
* - If true, the default logic (replace text, preserve structure) is used.
|
|
44
|
+
* - If false, no changes are applied.
|
|
45
|
+
* - If a function, it is called with the messages and SDP result, and should return the new messages.
|
|
46
|
+
*
|
|
47
|
+
* Defaults to false.
|
|
48
|
+
*/
|
|
49
|
+
applyDeidentificationResults?: boolean | ((data: {
|
|
50
|
+
messages: MessageData[];
|
|
51
|
+
sdpResult: protos.google.cloud.modelarmor.v1.ISdpFilterResult;
|
|
52
|
+
}) => MessageData[] | undefined);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Model Middleware that uses Google Cloud Model Armor to sanitize user prompts and model responses.
|
|
56
|
+
*/
|
|
57
|
+
declare function modelArmor(options: ModelArmorOptions): ModelMiddleware;
|
|
58
|
+
|
|
59
|
+
export { type ModelArmorOptions, modelArmor };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var model_armor_exports = {};
|
|
20
|
+
__export(model_armor_exports, {
|
|
21
|
+
modelArmor: () => modelArmor
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(model_armor_exports);
|
|
24
|
+
var import_modelarmor = require("@google-cloud/modelarmor");
|
|
25
|
+
var import_genkit = require("genkit");
|
|
26
|
+
var import_tracing = require("genkit/tracing");
|
|
27
|
+
function extractText(parts) {
|
|
28
|
+
return parts.map((p) => p.text || "").join("");
|
|
29
|
+
}
|
|
30
|
+
function applySdp(messages, targetIndex, result, options) {
|
|
31
|
+
const sdpFilterResult = result.filterResults?.["sdp"]?.sdpFilterResult;
|
|
32
|
+
if (!sdpFilterResult) {
|
|
33
|
+
return { sdpApplied: false, messages };
|
|
34
|
+
}
|
|
35
|
+
if (typeof options.applyDeidentificationResults === "function") {
|
|
36
|
+
const newMessages = options.applyDeidentificationResults({
|
|
37
|
+
messages,
|
|
38
|
+
sdpResult: sdpFilterResult
|
|
39
|
+
});
|
|
40
|
+
if (!newMessages) {
|
|
41
|
+
return { sdpApplied: false, messages };
|
|
42
|
+
}
|
|
43
|
+
const sdpApplied = !!sdpFilterResult.deidentifyResult?.data?.text;
|
|
44
|
+
return { sdpApplied, messages: newMessages };
|
|
45
|
+
}
|
|
46
|
+
if (options.applyDeidentificationResults === true) {
|
|
47
|
+
const deidentifyResult = sdpFilterResult.deidentifyResult;
|
|
48
|
+
if (deidentifyResult && deidentifyResult.data?.text) {
|
|
49
|
+
const targetMessage = messages[targetIndex];
|
|
50
|
+
const nonTextParts = targetMessage.content.filter((p) => !p.text);
|
|
51
|
+
const newContent = [
|
|
52
|
+
...nonTextParts,
|
|
53
|
+
{ text: deidentifyResult.data.text }
|
|
54
|
+
];
|
|
55
|
+
const newMessages = [...messages];
|
|
56
|
+
newMessages[targetIndex] = { ...targetMessage, content: newContent };
|
|
57
|
+
return {
|
|
58
|
+
sdpApplied: true,
|
|
59
|
+
messages: newMessages
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { sdpApplied: false, messages };
|
|
64
|
+
}
|
|
65
|
+
function shouldBlock(result, options, sdpApplied) {
|
|
66
|
+
if (result.filterMatchState !== "MATCH_FOUND") {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (options.strictSdpEnforcement && sdpApplied) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (result.filterResults) {
|
|
73
|
+
for (const [key, filterResult] of Object.entries(result.filterResults)) {
|
|
74
|
+
if (options.filters && !options.filters.includes(key)) continue;
|
|
75
|
+
if (key === "sdp" && sdpApplied) continue;
|
|
76
|
+
const nestedResult = Object.values(filterResult)[0];
|
|
77
|
+
if (nestedResult?.matchState === "MATCH_FOUND") {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
async function sanitizeUserPrompt(req, client, options) {
|
|
85
|
+
let targetMessageIndex = -1;
|
|
86
|
+
for (let i = req.messages.length - 1; i >= 0; i--) {
|
|
87
|
+
if (req.messages[i].role === "user") {
|
|
88
|
+
targetMessageIndex = i;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (targetMessageIndex !== -1) {
|
|
93
|
+
const userMessage = req.messages[targetMessageIndex];
|
|
94
|
+
const promptText = extractText(userMessage.content);
|
|
95
|
+
if (promptText) {
|
|
96
|
+
await (0, import_tracing.runInNewSpan)(
|
|
97
|
+
{ metadata: { name: "sanitizeUserPrompt" } },
|
|
98
|
+
async (meta) => {
|
|
99
|
+
meta.input = {
|
|
100
|
+
name: options.templateName,
|
|
101
|
+
userPromptData: {
|
|
102
|
+
text: promptText
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const [response] = await client.sanitizeUserPrompt({
|
|
106
|
+
name: options.templateName,
|
|
107
|
+
userPromptData: {
|
|
108
|
+
text: promptText
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
meta.output = response;
|
|
112
|
+
if (response.sanitizationResult) {
|
|
113
|
+
const result = response.sanitizationResult;
|
|
114
|
+
const { sdpApplied, messages: modifiedMessages } = applySdp(
|
|
115
|
+
req.messages,
|
|
116
|
+
targetMessageIndex,
|
|
117
|
+
result,
|
|
118
|
+
options
|
|
119
|
+
);
|
|
120
|
+
if (sdpApplied || typeof options.applyDeidentificationResults === "function") {
|
|
121
|
+
req.messages = modifiedMessages;
|
|
122
|
+
}
|
|
123
|
+
if (shouldBlock(result, options, sdpApplied)) {
|
|
124
|
+
throw new import_genkit.GenkitError({
|
|
125
|
+
status: "PERMISSION_DENIED",
|
|
126
|
+
message: "Model Armor blocked user prompt.",
|
|
127
|
+
detail: result
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function sanitizeModelResponse(response, client, options) {
|
|
137
|
+
const usingMessageProp = !!response.message;
|
|
138
|
+
const candidates = response.message ? [{ index: 0, message: response.message, finishReason: "stop" }] : response.candidates || [];
|
|
139
|
+
for (const candidate of candidates) {
|
|
140
|
+
const modelText = extractText(candidate.message.content);
|
|
141
|
+
if (modelText) {
|
|
142
|
+
await (0, import_tracing.runInNewSpan)(
|
|
143
|
+
{ metadata: { name: "sanitizeModelResponse" } },
|
|
144
|
+
async (meta) => {
|
|
145
|
+
meta.input = {
|
|
146
|
+
name: options.templateName,
|
|
147
|
+
modelResponseData: {
|
|
148
|
+
text: modelText
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const [apiResponse] = await client.sanitizeModelResponse({
|
|
152
|
+
name: options.templateName,
|
|
153
|
+
modelResponseData: {
|
|
154
|
+
text: modelText
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
meta.output = apiResponse;
|
|
158
|
+
if (apiResponse.sanitizationResult) {
|
|
159
|
+
const result = apiResponse.sanitizationResult;
|
|
160
|
+
const { sdpApplied, messages: modifiedMessages } = applySdp(
|
|
161
|
+
[candidate.message],
|
|
162
|
+
0,
|
|
163
|
+
result,
|
|
164
|
+
options
|
|
165
|
+
);
|
|
166
|
+
if (sdpApplied || typeof options.applyDeidentificationResults === "function") {
|
|
167
|
+
candidate.message = modifiedMessages[0];
|
|
168
|
+
}
|
|
169
|
+
if (shouldBlock(result, options, sdpApplied)) {
|
|
170
|
+
throw new import_genkit.GenkitError({
|
|
171
|
+
status: "PERMISSION_DENIED",
|
|
172
|
+
message: "Model Armor blocked model response.",
|
|
173
|
+
detail: result
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (usingMessageProp && candidates.length > 0) {
|
|
182
|
+
response.message = candidates[0].message;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function modelArmor(options) {
|
|
186
|
+
const client = options.client || new import_modelarmor.ModelArmorClient(options.clientOptions);
|
|
187
|
+
const protectionTarget = options.protectionTarget ?? "all";
|
|
188
|
+
const protectUserPrompt = protectionTarget === "all" || protectionTarget === "userPrompt";
|
|
189
|
+
const protectModelResponse = protectionTarget === "all" || protectionTarget === "modelResponse";
|
|
190
|
+
return async (req, next) => {
|
|
191
|
+
if (protectUserPrompt) {
|
|
192
|
+
await sanitizeUserPrompt(req, client, options);
|
|
193
|
+
}
|
|
194
|
+
const response = await next(req);
|
|
195
|
+
if (protectModelResponse) {
|
|
196
|
+
await sanitizeModelResponse(response, client, options);
|
|
197
|
+
}
|
|
198
|
+
return response;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
202
|
+
0 && (module.exports = {
|
|
203
|
+
modelArmor
|
|
204
|
+
});
|
|
205
|
+
//# sourceMappingURL=model-armor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/model-armor.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ModelArmorClient, protos } from '@google-cloud/modelarmor';\nimport { GenkitError } from 'genkit';\nimport {\n GenerateRequest,\n GenerateResponseData,\n MessageData,\n ModelMiddleware,\n Part,\n} from 'genkit/model';\nimport { runInNewSpan } from 'genkit/tracing';\n\nexport interface ModelArmorOptions {\n templateName: string;\n client?: ModelArmorClient;\n /**\n * Options for the Model Armor client (e.g. apiEndpoint).\n */\n clientOptions?: ConstructorParameters<typeof ModelArmorClient>[0];\n /**\n * What to sanitize. Defaults to 'all'.\n */\n protectionTarget?: 'all' | 'userPrompt' | 'modelResponse';\n /**\n * Whether to block on SDP match even if the content was successfully de-identified.\n * Defaults to false (lenient).\n */\n strictSdpEnforcement?: boolean;\n /**\n * List of filters to enforce. If not specified, all filters are enforced.\n * Possible values: 'rai', 'pi_and_jailbreak', 'malicious_uris', 'csam', 'sdp'.\n */\n filters?: (\n | 'rai'\n | 'pi_and_jailbreak'\n | 'malicious_uris'\n | 'csam'\n | 'sdp'\n | (string & {})\n )[];\n /**\n * Whether to apply the de-identification results to the content.\n * - If true, the default logic (replace text, preserve structure) is used.\n * - If false, no changes are applied.\n * - If a function, it is called with the messages and SDP result, and should return the new messages.\n *\n * Defaults to false.\n */\n applyDeidentificationResults?:\n | boolean\n | ((data: {\n messages: MessageData[];\n sdpResult: protos.google.cloud.modelarmor.v1.ISdpFilterResult;\n }) => MessageData[] | undefined);\n}\n\nfunction extractText(parts: Part[]): string {\n return parts.map((p) => p.text || '').join('');\n}\n\n/**\n * If SDP (Sensitive Data Protection) filter returns sanitized data,\n * we swap out the data with sanitized data.\n */\nfunction applySdp(\n messages: MessageData[],\n targetIndex: number,\n result: protos.google.cloud.modelarmor.v1.ISanitizationResult,\n options: ModelArmorOptions\n): { sdpApplied: boolean; messages: MessageData[] } {\n const sdpFilterResult = result.filterResults?.['sdp']?.sdpFilterResult;\n\n if (!sdpFilterResult) {\n return { sdpApplied: false, messages };\n }\n\n // If user provided applyDeidentificationResults, we use it to apply\n // the deidentification results.\n if (typeof options.applyDeidentificationResults === 'function') {\n const newMessages = options.applyDeidentificationResults({\n messages,\n sdpResult: sdpFilterResult,\n });\n if (!newMessages) {\n return { sdpApplied: false, messages };\n }\n const sdpApplied = !!sdpFilterResult.deidentifyResult?.data?.text;\n return { sdpApplied, messages: newMessages };\n }\n\n // if applyDeidentificationResults is set to true, we use the default/basic\n // approach to apply the results.\n if (options.applyDeidentificationResults === true) {\n const deidentifyResult = sdpFilterResult.deidentifyResult;\n if (deidentifyResult && deidentifyResult.data?.text) {\n const targetMessage = messages[targetIndex];\n const nonTextParts = targetMessage.content.filter((p) => !p.text);\n const newContent = [\n ...nonTextParts,\n { text: deidentifyResult.data.text },\n ];\n const newMessages = [...messages];\n newMessages[targetIndex] = { ...targetMessage, content: newContent };\n return {\n sdpApplied: true,\n messages: newMessages,\n };\n }\n }\n\n return { sdpApplied: false, messages };\n}\n\nfunction shouldBlock(\n result: protos.google.cloud.modelarmor.v1.ISanitizationResult,\n options: ModelArmorOptions,\n sdpApplied: boolean\n): boolean {\n if (result.filterMatchState !== 'MATCH_FOUND') {\n return false;\n }\n // Check if we should block.\n // If strict SDP enforcement is enabled and SDP was applied, we must block.\n if (options.strictSdpEnforcement && sdpApplied) {\n return true;\n }\n // Otherwise, check if any active filter matched.\n if (result.filterResults) {\n for (const [key, filterResult] of Object.entries(result.filterResults)) {\n if (options.filters && !options.filters.includes(key)) continue;\n if (key === 'sdp' && sdpApplied) continue;\n\n // Look for matchState in the nested object\n // e.g. filterResult.raiFilterResult.matchState\n const nestedResult = Object.values(filterResult)[0];\n if (nestedResult?.matchState === 'MATCH_FOUND') {\n return true;\n }\n }\n }\n return false;\n}\n\nasync function sanitizeUserPrompt(\n req: GenerateRequest,\n client: ModelArmorClient,\n options: ModelArmorOptions\n) {\n let targetMessageIndex = -1;\n // Find the last user message to sanitize\n for (let i = req.messages.length - 1; i >= 0; i--) {\n if (req.messages[i].role === 'user') {\n targetMessageIndex = i;\n break;\n }\n }\n\n if (targetMessageIndex !== -1) {\n const userMessage = req.messages[targetMessageIndex];\n const promptText = extractText(userMessage.content);\n\n if (promptText) {\n await runInNewSpan(\n { metadata: { name: 'sanitizeUserPrompt' } },\n async (meta) => {\n meta.input = {\n name: options.templateName,\n userPromptData: {\n text: promptText,\n },\n };\n const [response] = await client.sanitizeUserPrompt({\n name: options.templateName,\n userPromptData: {\n text: promptText,\n },\n });\n meta.output = response;\n\n if (response.sanitizationResult) {\n const result = response.sanitizationResult;\n const { sdpApplied, messages: modifiedMessages } = applySdp(\n req.messages,\n targetMessageIndex,\n result,\n options\n );\n\n if (\n sdpApplied ||\n typeof options.applyDeidentificationResults === 'function'\n ) {\n req.messages = modifiedMessages;\n }\n\n if (shouldBlock(result, options, sdpApplied)) {\n throw new GenkitError({\n status: 'PERMISSION_DENIED',\n message: 'Model Armor blocked user prompt.',\n detail: result,\n });\n }\n }\n }\n );\n }\n }\n}\n\nasync function sanitizeModelResponse(\n response: GenerateResponseData,\n client: ModelArmorClient,\n options: ModelArmorOptions\n) {\n const usingMessageProp = !!response.message;\n const candidates = response.message\n ? [{ index: 0, message: response.message, finishReason: 'stop' }]\n : response.candidates || [];\n\n for (const candidate of candidates) {\n const modelText = extractText(candidate.message.content);\n\n if (modelText) {\n await runInNewSpan(\n { metadata: { name: 'sanitizeModelResponse' } },\n async (meta) => {\n meta.input = {\n name: options.templateName,\n modelResponseData: {\n text: modelText,\n },\n };\n const [apiResponse] = await client.sanitizeModelResponse({\n name: options.templateName,\n modelResponseData: {\n text: modelText,\n },\n });\n meta.output = apiResponse;\n\n if (apiResponse.sanitizationResult) {\n const result = apiResponse.sanitizationResult;\n const { sdpApplied, messages: modifiedMessages } = applySdp(\n [candidate.message],\n 0,\n result,\n options\n );\n\n if (\n sdpApplied ||\n typeof options.applyDeidentificationResults === 'function'\n ) {\n candidate.message = modifiedMessages[0];\n }\n\n if (shouldBlock(result, options, sdpApplied)) {\n throw new GenkitError({\n status: 'PERMISSION_DENIED',\n message: 'Model Armor blocked model response.',\n detail: result,\n });\n }\n }\n }\n );\n }\n }\n\n if (usingMessageProp && candidates.length > 0) {\n response.message = candidates[0].message;\n }\n}\n\n/**\n * Model Middleware that uses Google Cloud Model Armor to sanitize user prompts and model responses.\n */\nexport function modelArmor(options: ModelArmorOptions): ModelMiddleware {\n const client = options.client || new ModelArmorClient(options.clientOptions);\n const protectionTarget = options.protectionTarget ?? 'all';\n const protectUserPrompt =\n protectionTarget === 'all' || protectionTarget === 'userPrompt';\n const protectModelResponse =\n protectionTarget === 'all' || protectionTarget === 'modelResponse';\n\n return async (req, next) => {\n // 1. Sanitize User Prompt\n if (protectUserPrompt) {\n await sanitizeUserPrompt(req, client, options);\n }\n\n // 2. Call Model\n const response = await next(req);\n\n // 3. Sanitize Model Response\n if (protectModelResponse) {\n await sanitizeModelResponse(response, client, options);\n }\n\n return response;\n };\n}\n"],"mappings":"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","names":[]}
|