@tstdl/base 0.92.12 → 0.92.14

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.
@@ -1,9 +1,18 @@
1
- import type { TypedOmit } from '../types.js';
2
- import type { AiService } from './ai.service.js';
3
- import type { Content, GenerationRequest, GenerationResult } from './types.js';
1
+ import type { SchemaTestable } from '../schema/index.js';
2
+ import type { Enumeration, OneOrMany, TypedOmit } from '../types.js';
3
+ import type { AiService, AnalyzeContentResult, ClassificationResult, SpecializedGenerationResult } from './ai.service.js';
4
+ import type { Content, ContentPart, GenerationOptions, GenerationRequest, GenerationResult, SchemaFunctionDeclarations, SchemaFunctionDeclarationsResult } from './types.js';
4
5
  export declare class AiSession {
5
6
  #private;
6
7
  readonly contents: Content[];
7
8
  constructor(aiService: AiService);
9
+ addContent(content: OneOrMany<Content>): void;
10
+ analyzeContent<T extends Enumeration>(parts: OneOrMany<ContentPart>, types: T, options?: GenerationOptions & {
11
+ targetLanguage?: string;
12
+ maximumNumberOfTags?: number;
13
+ }): Promise<SpecializedGenerationResult<AnalyzeContentResult<T>>>;
14
+ classify<T extends Enumeration>(parts: OneOrMany<ContentPart>, types: T, options?: GenerationOptions & Pick<GenerationRequest, 'model'>): Promise<SpecializedGenerationResult<ClassificationResult<T>>>;
15
+ extractData<T>(parts: OneOrMany<ContentPart>, schema: SchemaTestable<T>, options?: GenerationOptions & Pick<GenerationRequest, 'model'>): Promise<SpecializedGenerationResult<T>>;
16
+ callFunctions<const T extends SchemaFunctionDeclarations>(functions: T, content: Content | [Content, ...Content[]], options?: Pick<GenerationRequest, 'model' | 'systemInstruction'> & GenerationOptions): Promise<SpecializedGenerationResult<SchemaFunctionDeclarationsResult<T>[]>>;
8
17
  generate(content: Content | [Content, ...Content[]], request?: TypedOmit<GenerationRequest, 'contents'>): Promise<GenerationResult>;
9
18
  }
package/ai/ai-session.js CHANGED
@@ -5,13 +5,39 @@ export class AiSession {
5
5
  constructor(aiService) {
6
6
  this.#aiService = aiService;
7
7
  }
8
- async generate(content, request) {
9
- const newContents = toArray(content);
10
- const result = await this.#aiService.generate({
11
- ...request,
12
- contents: [...this.contents, ...newContents]
13
- });
8
+ addContent(content) {
9
+ this.contents.push(...toArray(content));
10
+ }
11
+ async analyzeContent(parts, types, options) {
12
+ const newContents = this.#aiService.getAnalyzeContentConents(parts);
14
13
  this.contents.push(...newContents);
14
+ const result = await this.#aiService.analyzeContent(parts, types, options);
15
+ this.contents.push(result.raw.content);
16
+ return result;
17
+ }
18
+ async classify(parts, types, options) {
19
+ const newContents = this.#aiService.getExtractDataConents(parts);
20
+ this.contents.push(...newContents);
21
+ const result = await this.#aiService.classify(parts, types, options);
22
+ this.contents.push(result.raw.content);
23
+ return result;
24
+ }
25
+ async extractData(parts, schema, options) {
26
+ const newContents = this.#aiService.getExtractDataConents(parts);
27
+ this.contents.push(...newContents);
28
+ const result = await this.#aiService.extractData(parts, schema, options);
29
+ this.contents.push(result.raw.content);
30
+ return result;
31
+ }
32
+ async callFunctions(functions, content, options) {
33
+ this.contents.push(...toArray(content));
34
+ const result = await this.#aiService.callFunctions(functions, this.contents, options);
35
+ this.contents.push(result.raw.content);
36
+ return result;
37
+ }
38
+ async generate(content, request) {
39
+ this.contents.push(...toArray(content));
40
+ const result = await this.#aiService.generate({ ...request, contents: this.contents });
15
41
  this.contents.push(result.content);
16
42
  return result;
17
43
  }
@@ -3,39 +3,44 @@ import { Resolvable, type resolveArgumentType } from '../injector/interfaces.js'
3
3
  import { OneOrMany, SchemaTestable } from '../schema/index.js';
4
4
  import { Enumeration as EnumerationType, EnumerationValue } from '../types.js';
5
5
  import { AiSession } from './ai-session.js';
6
- import { AiModel, Content, FileContentPart, FileInput, GenerationOptions, GenerationRequest, GenerationResult, GenerationUsage, SchemaFunctionDeclarations, SchemaFunctionDeclarationsResult } from './types.js';
6
+ import { AiModel, Content, ContentPart, FileContentPart, FileInput, GenerationOptions, GenerationRequest, GenerationResult, SchemaFunctionDeclarations, SchemaFunctionDeclarationsResult } from './types.js';
7
7
  export type SpecializedGenerationResult<T> = {
8
8
  result: T;
9
- usage: GenerationUsage;
9
+ raw: GenerationResult;
10
10
  };
11
11
  export type AiServiceOptions = {
12
12
  apiKey: string;
13
13
  defaultModel?: AiModel;
14
14
  };
15
15
  export type AiServiceArgument = AiServiceOptions;
16
+ export type ClassificationResult<T extends EnumerationType> = {
17
+ types: {
18
+ type: EnumerationValue<T>;
19
+ confidence: 'high' | 'medium' | 'low';
20
+ }[] | null;
21
+ };
22
+ export type AnalyzeContentResult<T extends EnumerationType> = {
23
+ content: string[];
24
+ documentTypes: ClassificationResult<T>[];
25
+ tags: string[];
26
+ };
16
27
  export declare class AiService implements Resolvable<AiServiceArgument> {
17
28
  #private;
18
29
  readonly defaultModel: AiModel;
19
30
  readonly [resolveArgumentType]: AiServiceArgument;
20
- newSession(): AiSession;
31
+ createSession(): AiSession;
21
32
  processFile(fileInput: FileInput): Promise<FileContentPart>;
22
33
  processFiles(fileInputs: FileInput[]): Promise<FileContentPart[]>;
23
- classify<T extends EnumerationType>(files: OneOrMany<FileContentPart>, types: T, model?: AiModel): Promise<SpecializedGenerationResult<{
24
- types: {
25
- type: EnumerationValue<T>;
26
- confidence: 'high' | 'medium' | 'low';
27
- }[] | null;
28
- }>>;
29
- extractData<T>(files: OneOrMany<FileContentPart>, schema: SchemaTestable<T>, options?: GenerationOptions): Promise<SpecializedGenerationResult<T>>;
30
- analyzeDocument<T extends EnumerationType>(file: FileContentPart, types: T, options?: GenerationOptions): Promise<SpecializedGenerationResult<{
31
- content: string[];
32
- documentTypes: {
33
- type: EnumerationValue<T>;
34
- confidence: 'high' | 'medium' | 'low';
35
- }[];
36
- tags: string[];
37
- }>>;
38
- callFunctions<const T extends SchemaFunctionDeclarations>(functions: T, contents: Content[], options?: Pick<GenerationRequest, 'model' | 'systemInstruction'> & GenerationOptions): Promise<SchemaFunctionDeclarationsResult<T>[]>;
34
+ classify<T extends EnumerationType>(parts: OneOrMany<ContentPart>, types: T, options?: GenerationOptions & Pick<GenerationRequest, 'model'>): Promise<SpecializedGenerationResult<ClassificationResult<T>>>;
35
+ getClassifyConents(parts: OneOrMany<ContentPart>): Content[];
36
+ extractData<T>(parts: OneOrMany<ContentPart>, schema: SchemaTestable<T>, options?: GenerationOptions & Pick<GenerationRequest, 'model'>): Promise<SpecializedGenerationResult<T>>;
37
+ getExtractDataConents(parts: OneOrMany<ContentPart>): Content[];
38
+ analyzeContent<T extends EnumerationType>(parts: OneOrMany<ContentPart>, types: T, options?: GenerationOptions & {
39
+ targetLanguage?: string;
40
+ maximumNumberOfTags?: number;
41
+ }): Promise<SpecializedGenerationResult<AnalyzeContentResult<T>>>;
42
+ getAnalyzeContentConents(parts: OneOrMany<ContentPart>): Content[];
43
+ callFunctions<const T extends SchemaFunctionDeclarations>(functions: T, contents: Content[], options?: Pick<GenerationRequest, 'model' | 'systemInstruction'> & GenerationOptions): Promise<SpecializedGenerationResult<SchemaFunctionDeclarationsResult<T>[]>>;
39
44
  generate(request: GenerationRequest): Promise<GenerationResult>;
40
45
  private convertContents;
41
46
  private convertContent;
package/ai/ai.service.js CHANGED
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import '../polyfills.js';
8
- import { GoogleGenerativeAI } from '@google/generative-ai';
8
+ import { FunctionCallingMode as GoogleFunctionCallingMode, GoogleGenerativeAI } from '@google/generative-ai';
9
9
  import { NotSupportedError } from '../errors/not-supported.error.js';
10
10
  import { Singleton } from '../injector/decorators.js';
11
11
  import { inject, injectArgument } from '../injector/inject.js';
@@ -17,12 +17,17 @@ import { assertDefinedPass, assertNotNullPass, isDefined } from '../utils/type-g
17
17
  import { AiFileService } from './ai-file.service.js';
18
18
  import { AiSession } from './ai-session.js';
19
19
  import { isSchemaFunctionDeclarationWithHandler } from './types.js';
20
+ const functionCallingModeMap = {
21
+ auto: GoogleFunctionCallingMode.AUTO,
22
+ force: GoogleFunctionCallingMode.ANY,
23
+ none: GoogleFunctionCallingMode.NONE
24
+ };
20
25
  let AiService = class AiService {
21
26
  #options = injectArgument(this);
22
27
  #fileService = inject(AiFileService, this.#options);
23
28
  #genAI = new GoogleGenerativeAI(this.#options.apiKey);
24
29
  defaultModel = this.#options.defaultModel ?? 'gemini-2.0-flash-exp';
25
- newSession() {
30
+ createSession() {
26
31
  return new AiSession(this);
27
32
  }
28
33
  async processFile(fileInput) {
@@ -31,38 +36,42 @@ let AiService = class AiService {
31
36
  async processFiles(fileInputs) {
32
37
  return this.#fileService.processFiles(fileInputs);
33
38
  }
34
- async classify(files, types, model) {
39
+ async classify(parts, types, options) {
35
40
  const generationSchema = object({
36
41
  types: nullable(array(object({
37
42
  type: enumeration(types, { description: 'Type of document' }),
38
- confidence: enumeration(['high', 'medium', 'low'], { description: 'How sure/certain you are about the classficiation.' })
43
+ confidence: enumeration(['high', 'medium', 'low'], { description: 'How sure/certain you are about the classficiation' })
39
44
  }), { description: 'One or more document types that matches' }))
40
45
  });
41
- const result = await this.generate({
42
- model,
46
+ const generation = await this.generate({
47
+ model: options?.model,
43
48
  generationOptions: {
44
49
  maxOutputTokens: 1048,
45
- temperature: 0.5
50
+ temperature: 0.5,
51
+ ...options
46
52
  },
47
53
  generationSchema,
48
54
  systemInstruction: 'You are a highly accurate document classification AI. Your task is to analyze the content of a given document and determine its type based on a predefined list of possible document types.',
49
- contents: [{
50
- role: 'user',
51
- parts: [
52
- ...toArray(files),
53
- { text: 'Classify the document. Output as JSON using the provided schema\n\nIf none of the provided document types are a suitable match, return null for types.' }
54
- ]
55
- }]
55
+ contents: this.getClassifyConents(parts)
56
56
  });
57
57
  return {
58
- usage: result.usage,
59
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
58
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
59
+ raw: generation
60
60
  };
61
61
  }
62
- async extractData(files, schema, options) {
63
- const result = await this.generate({
62
+ getClassifyConents(parts) {
63
+ return [{
64
+ role: 'user',
65
+ parts: [
66
+ ...toArray(parts),
67
+ { text: 'Classify the document. Output as JSON using the provided schema\n\nIf none of the provided document types are a suitable match, return null for types.' }
68
+ ]
69
+ }];
70
+ }
71
+ async extractData(parts, schema, options) {
72
+ const generation = await this.generate({
73
+ model: options?.model,
64
74
  generationOptions: {
65
- maxOutputTokens: 8192,
66
75
  temperature: 0.5,
67
76
  ...options
68
77
  },
@@ -71,61 +80,62 @@ let AiService = class AiService {
71
80
 
72
81
  **Instructions:**
73
82
  Carefully read and analyze the provided document. Identify relevant information that corresponds to each field in the JSON schema. Focus on accuracy and avoid making assumptions. If a field has multiple possible values, extract all relevant ones into the correct array structures ONLY IF the schema defines that field as an array; otherwise, extract only the single most relevant value.`,
74
- contents: [
75
- {
76
- role: 'user',
77
- parts: [
78
- ...toArray(files),
79
- { text: 'Classify the document. Output as JSON.' }
80
- ]
81
- }
82
- ]
83
+ contents: this.getExtractDataConents(parts)
83
84
  });
84
85
  return {
85
- usage: result.usage,
86
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
86
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
87
+ raw: generation
87
88
  };
88
89
  }
89
- async analyzeDocument(file, types, options) {
90
+ getExtractDataConents(parts) {
91
+ return [{
92
+ role: 'user',
93
+ parts: [
94
+ ...toArray(parts),
95
+ { text: 'Extract the data as specified in the schema. Output as JSON.' }
96
+ ]
97
+ }];
98
+ }
99
+ async analyzeContent(parts, types, options) {
90
100
  const schema = object({
91
101
  content: string({ description: 'Content of the document with important details only' }),
92
- documentTypes: array(object({
102
+ types: nullable(array(object({
93
103
  type: enumeration(types, { description: 'Type of document' }),
94
104
  confidence: enumeration(['high', 'medium', 'low'], { description: 'How sure/certain you are about the classficiation' })
95
- }), { description: 'One or more document types that matches' }),
96
- tags: array(string({ description: 'Tag which describes the content' }), { description: 'List of tags', maximum: 5 })
105
+ }), { description: 'One or more document types that matches' })),
106
+ tags: array(string({ description: 'Tag which describes the content' }), { description: 'List of tags', maximum: options?.maximumNumberOfTags ?? 5 })
97
107
  });
98
- const result = await this.generate({
108
+ const generation = await this.generate({
99
109
  generationOptions: {
100
110
  maxOutputTokens: 2048,
101
111
  temperature: 0.5,
102
112
  ...options
103
113
  },
104
114
  generationSchema: schema,
105
- systemInstruction: `You are a highly skilled data extraction AI, specializing in accurately identifying and extracting information from unstructured text documents and converting it into a structured JSON format. Your primary goal is to meticulously follow the provided JSON schema and populate it with data extracted from the given document.
115
+ systemInstruction: `You are a highly skilled data extraction AI, specializing in accurately identifying and extracting information from unstructured content and converting it into a structured JSON format. Your primary goal is to meticulously follow the provided JSON schema and populate it with data extracted from the given document.
106
116
 
107
117
  **Instructions:**
108
- Carefully read and analyze the provided document.
118
+ Carefully read and analyze the provided content.
109
119
  Identify key points and relevant information that describes the content. Focus on a general overview without providing specific details.
110
- Only output as many content items as it is necessary to describe relevant content. It should be around 5 to 10 items.
111
- Classify the document based on the provided list of types.
112
- Output up to 5 tags.
113
- Always output the content and tags in Deutsch.`,
114
- contents: [
115
- {
116
- role: 'user',
117
- parts: [
118
- file,
119
- { text: 'Classify the document. Output as JSON.' }
120
- ]
121
- }
122
- ]
120
+ Classify the content based on the provided list of types.
121
+ Output up to ${options?.maximumNumberOfTags ?? 5} tags.
122
+ Always output the content and tags in ${options?.targetLanguage ?? 'the same language as the content'}.`,
123
+ contents: this.getAnalyzeContentConents(parts)
123
124
  });
124
125
  return {
125
- usage: result.usage,
126
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
126
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
127
+ raw: generation
127
128
  };
128
129
  }
130
+ getAnalyzeContentConents(parts) {
131
+ return [{
132
+ role: 'user',
133
+ parts: [
134
+ ...toArray(parts),
135
+ { text: 'Classify the document. Output as JSON.' }
136
+ ]
137
+ }];
138
+ }
129
139
  async callFunctions(functions, contents, options) {
130
140
  const generation = await this.generate({
131
141
  model: options?.model,
@@ -146,7 +156,10 @@ Always output the content and tags in Deutsch.`,
146
156
  const handlerResult = isSchemaFunctionDeclarationWithHandler(fn) ? await fn.handler(parameters) : undefined;
147
157
  result.push({ functionName: call.name, parameters: parameters, handlerResult: handlerResult });
148
158
  }
149
- return result;
159
+ return {
160
+ result,
161
+ raw: generation
162
+ };
150
163
  }
151
164
  async generate(request) {
152
165
  const googleContent = this.convertContents(request.contents);
@@ -159,7 +172,7 @@ Always output the content and tags in Deutsch.`,
159
172
  systemInstruction: request.systemInstruction,
160
173
  tools: isDefined(googleFunctionDeclarations) ? [{ functionDeclarations: googleFunctionDeclarations }] : undefined,
161
174
  toolConfig: isDefined(request.functionCallingMode)
162
- ? { functionCallingConfig: { mode: (request.functionCallingMode ?? 'auto').toUpperCase() } }
175
+ ? { functionCallingConfig: { mode: functionCallingModeMap[request.functionCallingMode] } }
163
176
  : undefined,
164
177
  contents: googleContent
165
178
  });
@@ -216,7 +229,7 @@ Always output the content and tags in Deutsch.`,
216
229
  return { text: part.text };
217
230
  }
218
231
  if (isDefined(part.fileData)) {
219
- const file = assertDefinedPass(this.#fileService.getFileByUri(part.fileData.fileUri), `File not found.`);
232
+ const file = assertDefinedPass(this.#fileService.getFileByUri(part.fileData.fileUri), 'File not found.');
220
233
  return { file: file.id };
221
234
  }
222
235
  ;
package/ai/types.d.ts CHANGED
@@ -46,7 +46,7 @@ export type FunctionResultContentPart = {
46
46
  export type ContentPart = TextContentPart | FileContentPart | FunctionCallContentPart | FunctionResultContentPart;
47
47
  export type Content = {
48
48
  role: ContentRole;
49
- parts: ContentPart[];
49
+ parts: readonly ContentPart[];
50
50
  };
51
51
  export type FunctionCallingMode = 'auto' | 'force' | 'none';
52
52
  export type AiModel = LiteralUnion<'gemini-2.0-flash-exp' | 'gemini-exp-1206' | 'gemini-2.0-flash-thinking-exp-1219', string>;
@@ -61,7 +61,7 @@ export type GenerationOptions = {
61
61
  export type GenerationRequest = {
62
62
  model?: AiModel;
63
63
  systemInstruction?: string;
64
- contents: Content[];
64
+ contents: readonly Content[];
65
65
  functions?: SchemaFunctionDeclarations;
66
66
  functionCallingMode?: FunctionCallingMode;
67
67
  generationSchema?: SchemaTestable;
package/ai/types.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { hasOwnProperty } from '../utils/object/object.js';
2
+ import { isDefined } from '../utils/type-guards.js';
2
3
  export function declareFunctions(declarations) {
3
4
  return declarations;
4
5
  }
5
6
  export function declareFunction(description, parameters, handler) {
6
- return { description, parameters, handler };
7
+ if (isDefined(handler)) {
8
+ return { description, parameters, handler };
9
+ }
10
+ return { description, parameters };
7
11
  }
8
12
  export function isSchemaFunctionDeclarationWithHandler(declaration) {
9
13
  return hasOwnProperty(declaration, 'handler');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.12",
3
+ "version": "0.92.14",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"