@tstdl/base 0.92.13 → 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
@@ -27,7 +27,7 @@ let AiService = class AiService {
27
27
  #fileService = inject(AiFileService, this.#options);
28
28
  #genAI = new GoogleGenerativeAI(this.#options.apiKey);
29
29
  defaultModel = this.#options.defaultModel ?? 'gemini-2.0-flash-exp';
30
- newSession() {
30
+ createSession() {
31
31
  return new AiSession(this);
32
32
  }
33
33
  async processFile(fileInput) {
@@ -36,38 +36,42 @@ let AiService = class AiService {
36
36
  async processFiles(fileInputs) {
37
37
  return this.#fileService.processFiles(fileInputs);
38
38
  }
39
- async classify(files, types, model) {
39
+ async classify(parts, types, options) {
40
40
  const generationSchema = object({
41
41
  types: nullable(array(object({
42
42
  type: enumeration(types, { description: 'Type of document' }),
43
- 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' })
44
44
  }), { description: 'One or more document types that matches' }))
45
45
  });
46
- const result = await this.generate({
47
- model,
46
+ const generation = await this.generate({
47
+ model: options?.model,
48
48
  generationOptions: {
49
49
  maxOutputTokens: 1048,
50
- temperature: 0.5
50
+ temperature: 0.5,
51
+ ...options
51
52
  },
52
53
  generationSchema,
53
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.',
54
- contents: [{
55
- role: 'user',
56
- parts: [
57
- ...toArray(files),
58
- { 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.' }
59
- ]
60
- }]
55
+ contents: this.getClassifyConents(parts)
61
56
  });
62
57
  return {
63
- usage: result.usage,
64
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
58
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
59
+ raw: generation
65
60
  };
66
61
  }
67
- async extractData(files, schema, options) {
68
- 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,
69
74
  generationOptions: {
70
- maxOutputTokens: 8192,
71
75
  temperature: 0.5,
72
76
  ...options
73
77
  },
@@ -76,61 +80,62 @@ let AiService = class AiService {
76
80
 
77
81
  **Instructions:**
78
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.`,
79
- contents: [
80
- {
81
- role: 'user',
82
- parts: [
83
- ...toArray(files),
84
- { text: 'Classify the document. Output as JSON.' }
85
- ]
86
- }
87
- ]
83
+ contents: this.getExtractDataConents(parts)
88
84
  });
89
85
  return {
90
- usage: result.usage,
91
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
86
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
87
+ raw: generation
92
88
  };
93
89
  }
94
- 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) {
95
100
  const schema = object({
96
101
  content: string({ description: 'Content of the document with important details only' }),
97
- documentTypes: array(object({
102
+ types: nullable(array(object({
98
103
  type: enumeration(types, { description: 'Type of document' }),
99
104
  confidence: enumeration(['high', 'medium', 'low'], { description: 'How sure/certain you are about the classficiation' })
100
- }), { description: 'One or more document types that matches' }),
101
- 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 })
102
107
  });
103
- const result = await this.generate({
108
+ const generation = await this.generate({
104
109
  generationOptions: {
105
110
  maxOutputTokens: 2048,
106
111
  temperature: 0.5,
107
112
  ...options
108
113
  },
109
114
  generationSchema: schema,
110
- 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.
111
116
 
112
117
  **Instructions:**
113
- Carefully read and analyze the provided document.
118
+ Carefully read and analyze the provided content.
114
119
  Identify key points and relevant information that describes the content. Focus on a general overview without providing specific details.
115
- Only output as many content items as it is necessary to describe relevant content. It should be around 5 to 10 items.
116
- Classify the document based on the provided list of types.
117
- Output up to 5 tags.
118
- Always output the content and tags in Deutsch.`,
119
- contents: [
120
- {
121
- role: 'user',
122
- parts: [
123
- file,
124
- { text: 'Classify the document. Output as JSON.' }
125
- ]
126
- }
127
- ]
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)
128
124
  });
129
125
  return {
130
- usage: result.usage,
131
- result: JSON.parse(assertNotNullPass(result.text, 'No text returned.'))
126
+ result: JSON.parse(assertNotNullPass(generation.text, 'No text returned.')),
127
+ raw: generation
132
128
  };
133
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
+ }
134
139
  async callFunctions(functions, contents, options) {
135
140
  const generation = await this.generate({
136
141
  model: options?.model,
@@ -151,7 +156,10 @@ Always output the content and tags in Deutsch.`,
151
156
  const handlerResult = isSchemaFunctionDeclarationWithHandler(fn) ? await fn.handler(parameters) : undefined;
152
157
  result.push({ functionName: call.name, parameters: parameters, handlerResult: handlerResult });
153
158
  }
154
- return result;
159
+ return {
160
+ result,
161
+ raw: generation
162
+ };
155
163
  }
156
164
  async generate(request) {
157
165
  const googleContent = this.convertContents(request.contents);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.13",
3
+ "version": "0.92.14",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"