@tstdl/base 0.93.35 → 0.93.37
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/ai/ai.service.js +12 -5
- package/ai/types.d.ts +9 -3
- package/package.json +1 -1
- package/schema/converters/openapi-converter.js +31 -24
- package/schema/schemas/object.d.ts +4 -0
- package/schema/schemas/object.js +12 -1
- package/schema/schemas/string.d.ts +4 -0
- package/schema/schemas/string.js +11 -0
- package/test1.js +16 -5
package/ai/ai.service.js
CHANGED
|
@@ -209,8 +209,10 @@ let AiService = AiService_1 = class AiService {
|
|
|
209
209
|
const inputContent = this.convertContents(request.contents);
|
|
210
210
|
let iterations = 0;
|
|
211
211
|
let totalPromptTokens = 0;
|
|
212
|
+
let totalCachedTokens = 0;
|
|
213
|
+
let totalThoughtsTokens = 0;
|
|
212
214
|
let totalOutputTokens = 0;
|
|
213
|
-
let
|
|
215
|
+
let totalToolsTokens = 0;
|
|
214
216
|
while (totalOutputTokens < maxTotalOutputTokens) {
|
|
215
217
|
let generation;
|
|
216
218
|
let triesLeft = 10;
|
|
@@ -250,7 +252,7 @@ let AiService = AiService_1 = class AiService {
|
|
|
250
252
|
throw new Error('No content returned.');
|
|
251
253
|
}
|
|
252
254
|
inputContent.push(rawContent);
|
|
253
|
-
const {
|
|
255
|
+
const { cachedContentTokenCount = 0, candidatesTokenCount = 0, promptTokenCount = 0, thoughtsTokenCount = 0, toolUsePromptTokenCount = 0, totalTokenCount = 0, } = generationResponse.usageMetadata ?? {};
|
|
254
256
|
lastUsageMetadata = generationResponse.usageMetadata;
|
|
255
257
|
const content = this.convertGoogleContent(rawContent);
|
|
256
258
|
let text;
|
|
@@ -280,16 +282,21 @@ let AiService = AiService_1 = class AiService {
|
|
|
280
282
|
: 'unknown',
|
|
281
283
|
usage: {
|
|
282
284
|
iterations,
|
|
285
|
+
cached: totalCachedTokens + cachedContentTokenCount,
|
|
283
286
|
prompt: totalPromptTokens + promptTokenCount,
|
|
287
|
+
thoughts: totalThoughtsTokens + thoughtsTokenCount,
|
|
284
288
|
output: totalOutputTokens + candidatesTokenCount,
|
|
285
|
-
|
|
289
|
+
tools: totalToolsTokens + toolUsePromptTokenCount,
|
|
290
|
+
total: totalTokenCount,
|
|
286
291
|
},
|
|
287
292
|
};
|
|
288
293
|
yield result;
|
|
289
294
|
}
|
|
295
|
+
totalCachedTokens += lastUsageMetadata?.cachedContentTokenCount ?? 0;
|
|
290
296
|
totalPromptTokens += lastUsageMetadata?.promptTokenCount ?? 0;
|
|
297
|
+
totalThoughtsTokens += lastUsageMetadata?.thoughtsTokenCount ?? 0;
|
|
291
298
|
totalOutputTokens += lastUsageMetadata?.responseTokenCount ?? 0;
|
|
292
|
-
|
|
299
|
+
totalToolsTokens += lastUsageMetadata?.toolUsePromptTokenCount ?? 0;
|
|
293
300
|
if (candidate?.finishReason != FinishReason.MAX_TOKENS) {
|
|
294
301
|
break;
|
|
295
302
|
}
|
|
@@ -455,7 +462,7 @@ export function mergeGenerationStreamItems(items, schema) {
|
|
|
455
462
|
json: undefined,
|
|
456
463
|
functionCalls: [],
|
|
457
464
|
finishReason: 'unknown',
|
|
458
|
-
usage: { iterations: 0, prompt: 0, output: 0, total: 0 },
|
|
465
|
+
usage: { iterations: 0, cached: 0, prompt: 0, thoughts: 0, output: 0, tools: 0, total: 0 },
|
|
459
466
|
};
|
|
460
467
|
}
|
|
461
468
|
const parts = items.flatMap((item) => item.content.parts);
|
package/ai/types.d.ts
CHANGED
|
@@ -168,11 +168,17 @@ export type GenerationRequest<S = unknown> = {
|
|
|
168
168
|
export type GenerationUsage = {
|
|
169
169
|
/** The number of generation iterations (usually 1, more if `maxOutputTokens` is hit and generation continues). */
|
|
170
170
|
iterations: number;
|
|
171
|
-
/**
|
|
171
|
+
/** Tokens cached. */
|
|
172
|
+
cached: number;
|
|
173
|
+
/** Tokens used in the prompt. */
|
|
172
174
|
prompt: number;
|
|
173
|
-
/**
|
|
175
|
+
/** Tokens used for internal thoughts. */
|
|
176
|
+
thoughts: number;
|
|
177
|
+
/** Tokens in the generated output. */
|
|
174
178
|
output: number;
|
|
175
|
-
/**
|
|
179
|
+
/** Tokens used for tools. */
|
|
180
|
+
tools: number;
|
|
181
|
+
/** Total tokens used. */
|
|
176
182
|
total: number;
|
|
177
183
|
};
|
|
178
184
|
/**
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NotSupportedError } from '../../errors/not-supported.error.js';
|
|
2
|
-
import { fromEntries, hasOwnProperty, objectEntries } from '../../utils/object/object.js';
|
|
2
|
+
import { filterUndefinedObjectProperties, fromEntries, hasOwnProperty, objectEntries } from '../../utils/object/object.js';
|
|
3
3
|
import { isDefined, isNotNull, isNumber, isString } from '../../utils/type-guards.js';
|
|
4
4
|
import { ArraySchema } from '../schemas/array.js';
|
|
5
5
|
import { BooleanSchema } from '../schemas/boolean.js';
|
|
@@ -16,29 +16,36 @@ import { schemaTestableToSchema } from '../testable.js';
|
|
|
16
16
|
export function convertToOpenApiSchema(testable) {
|
|
17
17
|
const schema = schemaTestableToSchema(testable);
|
|
18
18
|
const openApiSchema = convertToOpenApiSchemaBase(schema);
|
|
19
|
-
return {
|
|
19
|
+
return filterUndefinedObjectProperties({
|
|
20
20
|
...openApiSchema,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
nullable: hasOwnProperty(openApiSchema, 'nullable') ? undefined : false,
|
|
22
|
+
title: ((schema instanceof ObjectSchema) && (schema.name != 'Object')) ? schema.name : undefined,
|
|
23
|
+
description: isNotNull(schema.description) ? schema.description : undefined,
|
|
24
|
+
example: isDefined(schema.example) ? schema.example : undefined,
|
|
25
|
+
});
|
|
25
26
|
}
|
|
26
27
|
function convertToOpenApiSchemaBase(schema) {
|
|
27
28
|
if (schema instanceof ObjectSchema) {
|
|
28
29
|
const entries = objectEntries(schema.properties);
|
|
29
30
|
const convertedEntries = entries.map(([property, propertySchema]) => [property, convertToOpenApiSchema(stripOptional(propertySchema))]);
|
|
30
|
-
|
|
31
|
+
const required = entries
|
|
32
|
+
.filter(([, propertySchema]) => !(propertySchema instanceof OptionalSchema) && !((propertySchema instanceof NullableSchema) && (propertySchema.schema instanceof OptionalSchema)))
|
|
33
|
+
.map(([property]) => property);
|
|
34
|
+
return filterUndefinedObjectProperties({
|
|
31
35
|
type: 'object',
|
|
32
36
|
properties: fromEntries(convertedEntries),
|
|
33
|
-
required:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
37
|
+
required: (required.length > 0) ? required : undefined,
|
|
38
|
+
minProperties: isNumber(schema.minimumPropertiesCount) ? schema.minimumPropertiesCount : undefined,
|
|
39
|
+
maxProperties: isNumber(schema.maximumPropertiesCount) ? schema.maximumPropertiesCount : undefined,
|
|
40
|
+
});
|
|
37
41
|
}
|
|
38
42
|
if (schema instanceof StringSchema) {
|
|
39
|
-
return {
|
|
43
|
+
return filterUndefinedObjectProperties({
|
|
40
44
|
type: 'string',
|
|
41
|
-
|
|
45
|
+
minLength: isNumber(schema.minimumLength) ? schema.minimumLength : undefined,
|
|
46
|
+
maxLength: isNumber(schema.maximumLength) ? schema.maximumLength : undefined,
|
|
47
|
+
pattern: isString(schema.pattern) ? schema.pattern : undefined,
|
|
48
|
+
});
|
|
42
49
|
}
|
|
43
50
|
if (schema instanceof DateSchema) {
|
|
44
51
|
return {
|
|
@@ -47,11 +54,11 @@ function convertToOpenApiSchemaBase(schema) {
|
|
|
47
54
|
};
|
|
48
55
|
}
|
|
49
56
|
if (schema instanceof NumberSchema) {
|
|
50
|
-
return {
|
|
57
|
+
return filterUndefinedObjectProperties({
|
|
51
58
|
type: schema.integer ? 'integer' : 'number',
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
59
|
+
minimum: isNumber(schema.minimum) ? schema.minimum : undefined,
|
|
60
|
+
maximum: isNumber(schema.maximum) ? schema.maximum : undefined,
|
|
61
|
+
});
|
|
55
62
|
}
|
|
56
63
|
if (schema instanceof BooleanSchema) {
|
|
57
64
|
return {
|
|
@@ -65,20 +72,20 @@ function convertToOpenApiSchemaBase(schema) {
|
|
|
65
72
|
};
|
|
66
73
|
}
|
|
67
74
|
if (schema instanceof ArraySchema) {
|
|
68
|
-
return {
|
|
75
|
+
return filterUndefinedObjectProperties({
|
|
69
76
|
type: 'array',
|
|
70
77
|
items: convertToOpenApiSchema(schema.itemSchema),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
78
|
+
minItems: isNumber(schema.minimum) ? schema.minimum : undefined,
|
|
79
|
+
maxItems: isNumber(schema.maximum) ? schema.maximum : undefined,
|
|
80
|
+
});
|
|
74
81
|
}
|
|
75
82
|
if (schema instanceof EnumerationSchema) {
|
|
76
83
|
const hasString = schema.allowedValues.some(isString);
|
|
77
84
|
const hasNumber = schema.allowedValues.some(isNumber);
|
|
78
|
-
if (schema.allowedValues.length
|
|
85
|
+
if (schema.allowedValues.length == 0) {
|
|
79
86
|
throw new NotSupportedError('Enum must have at least one value.');
|
|
80
87
|
}
|
|
81
|
-
if (
|
|
88
|
+
if (hasString && hasNumber) {
|
|
82
89
|
throw new NotSupportedError('Enum must be either string or number but not both.');
|
|
83
90
|
}
|
|
84
91
|
return {
|
|
@@ -102,7 +109,7 @@ function convertToOpenApiSchemaBase(schema) {
|
|
|
102
109
|
}
|
|
103
110
|
if (schema instanceof UnionSchema) {
|
|
104
111
|
return {
|
|
105
|
-
|
|
112
|
+
anyOf: schema.schemas.map((innerSchema) => convertToOpenApiSchema(innerSchema)),
|
|
106
113
|
};
|
|
107
114
|
}
|
|
108
115
|
throw new NotSupportedError(`Schema "${schema.name}" not supported.`);
|
|
@@ -20,6 +20,8 @@ export type ObjectSchemaOptions<T extends Record = Record, K extends PropertyKey
|
|
|
20
20
|
mask?: boolean | null;
|
|
21
21
|
unknownPropertiesKey?: SchemaTestable<K> | null;
|
|
22
22
|
unknownProperties?: SchemaTestable<V> | null;
|
|
23
|
+
minimumPropertiesCount?: number | null;
|
|
24
|
+
maximumPropertiesCount?: number | null;
|
|
23
25
|
factory?: ObjectSchemaFactory<T> | null;
|
|
24
26
|
};
|
|
25
27
|
export type ObjectSchemaOrType<T extends Record = any> = ObjectSchema<T> | AbstractConstructor<T>;
|
|
@@ -40,6 +42,8 @@ export declare class ObjectSchema<T extends Record = Record> extends Schema<T> {
|
|
|
40
42
|
readonly mask: boolean | null;
|
|
41
43
|
readonly unknownProperties: Schema | null;
|
|
42
44
|
readonly unknownPropertiesKey: Schema<PropertyKey> | null;
|
|
45
|
+
readonly minimumPropertiesCount: number | null;
|
|
46
|
+
readonly maximumPropertiesCount: number | null;
|
|
43
47
|
readonly factory: ObjectSchemaFactory<T> | null;
|
|
44
48
|
constructor(properties: ObjectSchemaProperties<T>, options?: ObjectSchemaOptions<T>);
|
|
45
49
|
_test(value: any, path: JsonPath, options: SchemaTestOptions): SchemaTestResult<T>;
|
package/schema/schemas/object.js
CHANGED
|
@@ -20,6 +20,8 @@ export class ObjectSchema extends Schema {
|
|
|
20
20
|
mask;
|
|
21
21
|
unknownProperties;
|
|
22
22
|
unknownPropertiesKey;
|
|
23
|
+
minimumPropertiesCount;
|
|
24
|
+
maximumPropertiesCount;
|
|
23
25
|
factory;
|
|
24
26
|
constructor(properties, options = {}) {
|
|
25
27
|
super(options);
|
|
@@ -27,6 +29,8 @@ export class ObjectSchema extends Schema {
|
|
|
27
29
|
this.mask = options.mask ?? null;
|
|
28
30
|
this.unknownProperties = isNotNullOrUndefined(options.unknownProperties) ? schemaTestableToSchema(options.unknownProperties) : null;
|
|
29
31
|
this.unknownPropertiesKey = isNotNullOrUndefined(options.unknownPropertiesKey) ? schemaTestableToSchema(options.unknownPropertiesKey) : null;
|
|
32
|
+
this.minimumPropertiesCount = options.minimumPropertiesCount ?? null;
|
|
33
|
+
this.maximumPropertiesCount = options.maximumPropertiesCount ?? null;
|
|
30
34
|
this.factory = options.factory ?? null;
|
|
31
35
|
this.allowUnknownProperties = isNotNull(this.unknownProperties) || isNotNull(this.unknownPropertiesKey);
|
|
32
36
|
this.propertyKeys = new Set(objectKeys(properties));
|
|
@@ -38,7 +42,14 @@ export class ObjectSchema extends Schema {
|
|
|
38
42
|
}
|
|
39
43
|
const mask = this.mask ?? options.mask ?? false;
|
|
40
44
|
const resultValue = isLiteralObject(this.factory) ? new this.factory.type() : {};
|
|
41
|
-
const
|
|
45
|
+
const keys = objectKeys(value);
|
|
46
|
+
if (isNotNull(this.minimumPropertiesCount) && (keys.length < this.minimumPropertiesCount)) {
|
|
47
|
+
return { valid: false, error: new SchemaError(`Object has too few properties (minimum ${this.minimumPropertiesCount}, got ${keys.length}).`, { path, fast: options.fastErrors }) };
|
|
48
|
+
}
|
|
49
|
+
if (isNotNull(this.maximumPropertiesCount) && (keys.length > this.maximumPropertiesCount)) {
|
|
50
|
+
return { valid: false, error: new SchemaError(`Object has too many properties (maximum ${this.maximumPropertiesCount}, got ${keys.length}).`, { path, fast: options.fastErrors }) };
|
|
51
|
+
}
|
|
52
|
+
const valueKeys = new Set(keys);
|
|
42
53
|
const unknownValuePropertyKeys = valueKeys.difference(this.propertyKeys);
|
|
43
54
|
if ((unknownValuePropertyKeys.size > 0) && !mask && !this.allowUnknownProperties) {
|
|
44
55
|
return { valid: false, error: new SchemaError('Unexpected property', { path: path.add(unknownValuePropertyKeys.values().next().value), fast: options.fastErrors }) };
|
|
@@ -3,11 +3,15 @@ import { SimpleSchema, type SimpleSchemaOptions } from './simple.js';
|
|
|
3
3
|
export type StringSchemaOptions = SimpleSchemaOptions<string> & {
|
|
4
4
|
pattern?: RegExp | string;
|
|
5
5
|
lowercase?: boolean;
|
|
6
|
+
minimumLength?: number;
|
|
7
|
+
maximumLength?: number;
|
|
6
8
|
};
|
|
7
9
|
export declare class StringSchema extends SimpleSchema<string> {
|
|
8
10
|
readonly name: string;
|
|
9
11
|
readonly pattern: RegExp | null;
|
|
10
12
|
readonly lowercase: boolean;
|
|
13
|
+
readonly minimumLength: number | null;
|
|
14
|
+
readonly maximumLength: number | null;
|
|
11
15
|
constructor(options?: StringSchemaOptions);
|
|
12
16
|
}
|
|
13
17
|
export declare function string(options?: StringSchemaOptions): StringSchema;
|
package/schema/schemas/string.js
CHANGED
|
@@ -5,6 +5,8 @@ export class StringSchema extends SimpleSchema {
|
|
|
5
5
|
name = 'string';
|
|
6
6
|
pattern;
|
|
7
7
|
lowercase;
|
|
8
|
+
minimumLength;
|
|
9
|
+
maximumLength;
|
|
8
10
|
constructor(options) {
|
|
9
11
|
super('string', isString, options, {
|
|
10
12
|
coercers: {
|
|
@@ -16,6 +18,12 @@ export class StringSchema extends SimpleSchema {
|
|
|
16
18
|
isDefined(options?.pattern)
|
|
17
19
|
? (value) => this.pattern.test(value) ? ({ success: true }) : ({ success: false, error: 'Value did not match pattern.' })
|
|
18
20
|
: null,
|
|
21
|
+
isDefined(options?.minimumLength)
|
|
22
|
+
? (value) => (value.length >= this.minimumLength) ? ({ success: true }) : ({ success: false, error: `String length is less than minimum length of ${this.minimumLength}.` })
|
|
23
|
+
: null,
|
|
24
|
+
isDefined(options?.maximumLength)
|
|
25
|
+
? (value) => (value.length <= this.maximumLength) ? ({ success: true }) : ({ success: false, error: `String length exceeds maximum length of ${this.maximumLength}.` })
|
|
26
|
+
: null,
|
|
19
27
|
],
|
|
20
28
|
transformers: (options?.lowercase ?? false) ? (value) => value.toLowerCase() : undefined,
|
|
21
29
|
});
|
|
@@ -24,6 +32,9 @@ export class StringSchema extends SimpleSchema {
|
|
|
24
32
|
: isRegExp(options?.pattern)
|
|
25
33
|
? options.pattern
|
|
26
34
|
: null;
|
|
35
|
+
this.lowercase = options?.lowercase ?? false;
|
|
36
|
+
this.minimumLength = options?.minimumLength ?? null;
|
|
37
|
+
this.maximumLength = options?.maximumLength ?? null;
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
export function string(options) {
|
package/test1.js
CHANGED
|
@@ -14,6 +14,7 @@ import { migrateTestSchema } from './test/module.js';
|
|
|
14
14
|
import { Test, testData } from './test/test.model.js';
|
|
15
15
|
import { timedBenchmarkAsync } from './utils/benchmark.js';
|
|
16
16
|
import * as configParser from './utils/config-parser.js';
|
|
17
|
+
import { string } from './utils/config-parser.js';
|
|
17
18
|
const config = {
|
|
18
19
|
database: {
|
|
19
20
|
host: configParser.string('DATABASE_HOST', '127.0.0.1'),
|
|
@@ -22,6 +23,14 @@ const config = {
|
|
|
22
23
|
pass: configParser.string('DATABASE_PASS', 'wf7rq6glrk5jykne'),
|
|
23
24
|
database: configParser.string('DATABASE_NAME', 'tstdl'),
|
|
24
25
|
},
|
|
26
|
+
ai: {
|
|
27
|
+
apiKey: string('AI_API_KEY', undefined),
|
|
28
|
+
keyFile: string('AI_API_KEY_FILE', undefined),
|
|
29
|
+
vertex: {
|
|
30
|
+
project: string('AI_VERTEX_PROJECT', undefined),
|
|
31
|
+
location: string('AI_VERTEX_LOCATION', undefined),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
25
34
|
};
|
|
26
35
|
async function bootstrap() {
|
|
27
36
|
const injector = inject(Injector);
|
|
@@ -30,7 +39,7 @@ async function bootstrap() {
|
|
|
30
39
|
project: '922353391551', // insolytics-application
|
|
31
40
|
location: 'europe-west4', // netherlands
|
|
32
41
|
},
|
|
33
|
-
keyFile: '/home/patrick/.secret-files/
|
|
42
|
+
keyFile: '/home/patrick/.secret-files/insolytic-application-service-key.json',
|
|
34
43
|
});
|
|
35
44
|
/*
|
|
36
45
|
configureOrm({
|
|
@@ -65,20 +74,22 @@ async function bootstrap() {
|
|
|
65
74
|
}
|
|
66
75
|
async function main(_cancellationSignal) {
|
|
67
76
|
const aiService = inject(AiService);
|
|
68
|
-
const
|
|
77
|
+
const aiStream = aiService.generateStream({
|
|
69
78
|
generationOptions: {
|
|
70
79
|
includeThoughts: true,
|
|
80
|
+
thinkingBudget: 1000,
|
|
71
81
|
},
|
|
72
82
|
model: 'gemini-2.5-flash-lite',
|
|
73
83
|
contents: [{
|
|
74
84
|
role: 'user',
|
|
75
85
|
parts: [
|
|
76
|
-
{ text: '
|
|
86
|
+
{ text: 'Count from 1 to 1000. List *every* number.' },
|
|
77
87
|
],
|
|
78
88
|
}],
|
|
79
89
|
});
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
for await (const { text, usage } of aiStream) {
|
|
91
|
+
console.log({ text, usage });
|
|
92
|
+
}
|
|
82
93
|
if (1 + 1 == 2)
|
|
83
94
|
process.exit(0);
|
|
84
95
|
}
|