@tstdl/base 0.92.108 → 0.92.110

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.
@@ -2,7 +2,7 @@ import type { Resolvable, resolveArgumentType } from '../injector/interfaces.js'
2
2
  import { type OneOrMany, type SchemaTestable } from '../schema/index.js';
3
3
  import type { Enumeration as EnumerationType, EnumerationValue } from '../types.js';
4
4
  import { AiSession } from './ai-session.js';
5
- import { type AiModel, type Content, type ContentPart, type FileContentPart, type FileInput, type GenerationOptions, type GenerationRequest, type GenerationResult, type SchemaFunctionDeclarations, type SchemaFunctionDeclarationsResult } from './types.js';
5
+ import { type AiModel, type Content, type ContentPart, type FileContentPart, type FileInput, type FunctionResultContentPart, type GenerationOptions, type GenerationRequest, type GenerationResult, type SchemaFunctionDeclarations, type SchemaFunctionDeclarationsResult } from './types.js';
6
6
  export type SpecializedGenerationResult<T> = {
7
7
  result: T;
8
8
  raw: GenerationResult;
@@ -32,7 +32,7 @@ export type AnalyzeContentResult<T extends EnumerationType> = {
32
32
  documentTypes: ClassificationResult<T>[];
33
33
  tags: string[];
34
34
  };
35
- export type CallFunctionsOptions<T extends SchemaFunctionDeclarations> = Pick<GenerationRequest, 'contents' | 'model' | 'systemInstruction'> & GenerationOptions & {
35
+ export type CallFunctionsOptions<T extends SchemaFunctionDeclarations> = Pick<GenerationRequest, 'contents' | 'model' | 'systemInstruction' | 'functionCallingMode'> & GenerationOptions & {
36
36
  functions: T;
37
37
  };
38
38
  export declare class AiService implements Resolvable<AiServiceArgument> {
@@ -52,7 +52,9 @@ export declare class AiService implements Resolvable<AiServiceArgument> {
52
52
  maximumNumberOfTags?: number;
53
53
  }): Promise<SpecializedGenerationResult<AnalyzeContentResult<T>>>;
54
54
  getAnalyzeContentConents(parts: OneOrMany<ContentPart>): Content[];
55
- callFunctions<const T extends SchemaFunctionDeclarations>(options: CallFunctionsOptions<T>): Promise<SpecializedGenerationResult<SchemaFunctionDeclarationsResult<T>[]>>;
55
+ callFunctions<const T extends SchemaFunctionDeclarations>(options: CallFunctionsOptions<T>): Promise<SpecializedGenerationResult<SchemaFunctionDeclarationsResult<T>[]> & {
56
+ getFunctionResultContentParts: () => FunctionResultContentPart[];
57
+ }>;
56
58
  callFunctionsStream<const T extends SchemaFunctionDeclarations>(options: CallFunctionsOptions<T>): SpecializedGenerationResultGenerator<SchemaFunctionDeclarationsResult<T>>;
57
59
  generate<S>(request: GenerationRequest<S>): Promise<GenerationResult<S>>;
58
60
  generateStream<S>(request: GenerationRequest<S>): AsyncGenerator<GenerationResult<S>>;
package/ai/ai.service.js CHANGED
@@ -41,6 +41,7 @@ const functionCallingModeMap = {
41
41
  force: GoogleFunctionCallingMode.ANY,
42
42
  none: GoogleFunctionCallingMode.NONE
43
43
  };
44
+ let generationCounter = 0;
44
45
  let AiService = AiService_1 = class AiService {
45
46
  #options = injectArgument(this, { optional: true }) ?? inject(AiServiceOptions);
46
47
  #fileService = inject(AiFileService, this.#options);
@@ -170,7 +171,7 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
170
171
  },
171
172
  systemInstruction: options.systemInstruction,
172
173
  functions: options.functions,
173
- functionCallingMode: 'force',
174
+ functionCallingMode: options.functionCallingMode ?? 'force',
174
175
  contents: options.contents
175
176
  });
176
177
  const result = [];
@@ -179,11 +180,17 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
179
180
  const parametersSchema = await resolveValueOrAsyncProvider(fn.parameters);
180
181
  const parameters = isDefined(parametersSchema) ? parametersSchema.parse(call.parameters) : call.parameters;
181
182
  const handlerResult = isSchemaFunctionDeclarationWithHandler(fn) ? await fn.handler(parameters) : undefined;
182
- result.push({ functionName: call.name, parameters: parameters, handlerResult: handlerResult });
183
+ result.push({
184
+ functionName: call.name,
185
+ parameters: parameters,
186
+ handlerResult: handlerResult,
187
+ getFunctionResultContentPart: () => ({ functionResult: { name: call.name, value: handlerResult } })
188
+ });
183
189
  }
184
190
  return {
185
191
  result,
186
- raw: generation
192
+ raw: generation,
193
+ getFunctionResultContentParts: () => result.map((result) => result.getFunctionResultContentPart())
187
194
  };
188
195
  }
189
196
  callFunctionsStream(options) {
@@ -200,7 +207,7 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
200
207
  return mergeGenerationStreamItems(items, request.generationSchema);
201
208
  }
202
209
  async *generateStream(request) {
203
- this.#logger.verbose('Generating...');
210
+ const generationNumber = ++generationCounter;
204
211
  const googleFunctionDeclarations = isDefined(request.functions) ? await this.convertFunctions(request.functions) : undefined;
205
212
  const generationConfig = {
206
213
  maxOutputTokens: request.generationOptions?.maxOutputTokens,
@@ -223,6 +230,7 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
223
230
  let generation;
224
231
  for (let i = 0;; i++) {
225
232
  try {
233
+ this.#logger.verbose(`[C:${generationNumber}] [I:${iterations + 1}] Generating...`);
226
234
  generation = await this.getModel(model).generateContentStream({
227
235
  generationConfig: {
228
236
  ...generationConfig,
@@ -308,7 +316,7 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
308
316
  },
309
317
  systemInstruction: options.systemInstruction,
310
318
  functions: options.functions,
311
- functionCallingMode: 'force',
319
+ functionCallingMode: options.functionCallingMode ?? 'force',
312
320
  contents: options.contents
313
321
  });
314
322
  const items = [];
@@ -319,7 +327,12 @@ Always output the content and tags in ${options?.targetLanguage ?? 'the same lan
319
327
  const parametersSchema = await resolveValueOrAsyncProvider(fn.parameters);
320
328
  const parameters = isDefined(parametersSchema) ? parametersSchema.parse(call.parameters) : call.parameters;
321
329
  const handlerResult = isSchemaFunctionDeclarationWithHandler(fn) ? await fn.handler(parameters) : undefined;
322
- yield { functionName: call.name, parameters: parameters, handlerResult: handlerResult };
330
+ yield {
331
+ functionName: call.name,
332
+ parameters: parameters,
333
+ handlerResult: handlerResult,
334
+ getFunctionResultContentPart: () => ({ functionResult: { name: call.name, value: handlerResult } })
335
+ };
323
336
  }
324
337
  }
325
338
  itemsPromise.resolve(items);
package/ai/types.d.ts CHANGED
@@ -22,6 +22,7 @@ export type SchemaFunctionDeclarationsResult<T extends SchemaFunctionDeclaration
22
22
  functionName: P;
23
23
  parameters: SchemaOutput<NonNullable<ResolvedValueOrProvider<T[P]['parameters']>>>;
24
24
  handlerResult: SchemaFunctionDeclarationHandlerResult<T[P]>;
25
+ getFunctionResultContentPart: () => FunctionResultContentPart;
25
26
  };
26
27
  }[keyof T];
27
28
  export type ContentRole = 'user' | 'model';
@@ -132,7 +132,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
132
132
  return this.collectionChangeMessageBus.allMessages$.pipe(filter((message) => isUndefined(message) || collectionIdsArray.some((id) => message.includes(id))), debounceTime(250), map(() => undefined));
133
133
  }
134
134
  processQueues(cancellationSignal) {
135
- this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
135
+ this.extractionQueue.process({ concurrency: 5, cancellationSignal }, async (job) => {
136
136
  const [entry] = objectEntries(job.data);
137
137
  this.logger.verbose(`Processing extraction job for ${entry?.[0]} ${entry?.[1]}`);
138
138
  await match(job.data)
@@ -141,7 +141,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
141
141
  .with({ requestAssignmentTaskId: P.string.select() }, async (requestAssignmentTaskId) => this.enrichDocumentRequestAssignmentTask(requestAssignmentTaskId))
142
142
  .exhaustive();
143
143
  }, this.logger);
144
- this.assignmentQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
144
+ this.assignmentQueue.process({ concurrency: 5, cancellationSignal }, async (job) => {
145
145
  this.logger.verbose(`Processing assignment job "${job.data.requestAssignmentTaskId}"`);
146
146
  await this.assignDocumentRequest(job.data.requestAssignmentTaskId);
147
147
  }, this.logger);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.108",
3
+ "version": "0.92.110",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -101,16 +101,24 @@ let PostgresQueue = class PostgresQueue extends Queue {
101
101
  return jobs[0];
102
102
  }
103
103
  async dequeueMany(count) {
104
- const rows = await this.#repository.session
105
- .update(job)
106
- .set(this.#dequeueUpdate)
107
- .where(inArray(job.id, this.#repository.session
104
+ /*
105
+ * Materialization required for LIMIT clause
106
+ * https://stackoverflow.com/questions/73966670/select-for-update-subquery-not-respecting-limit-clause-under-load
107
+ * https://dba.stackexchange.com/questions/69471/postgres-update-limit-1
108
+ */
109
+ const selection = this.#repository.session.$with('selection').as((qb) => qb
108
110
  .select({ id: job.id })
109
111
  .from(job)
110
- .where(this.#dequeueQuery)
112
+ .where(and(this.#dequeueQuery, sql `pg_sleep(0) IS NOT NULL` // workaround to force materialization until drizzle implements https://github.com/drizzle-team/drizzle-orm/issues/2318
113
+ ))
111
114
  .orderBy(asc(job.priority), asc(job.enqueueTimestamp), asc(job.lastDequeueTimestamp), asc(job.tries))
112
115
  .limit(count)
113
- .for('update', { skipLocked: true })))
116
+ .for('update', { skipLocked: true }));
117
+ const rows = await this.#repository.session
118
+ .with(selection)
119
+ .update(job)
120
+ .set(this.#dequeueUpdate)
121
+ .where(inArray(job.id, selection))
114
122
  .returning();
115
123
  return this.#repository.mapManyToEntity(rows);
116
124
  }