@nestjs/ai 0.0.0-stage → 0.0.2

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.
Files changed (101) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +108 -2
  3. package/dist/ai.constants.d.ts +30 -0
  4. package/dist/ai.constants.js +34 -0
  5. package/dist/ai.module-definition.d.ts +23 -0
  6. package/dist/ai.module-definition.js +39 -0
  7. package/dist/ai.module.d.ts +11 -0
  8. package/dist/ai.module.js +45 -0
  9. package/dist/context/ai-execution-context.d.ts +32 -0
  10. package/dist/context/ai-execution-context.js +41 -0
  11. package/dist/context/ai-params.d.ts +20 -0
  12. package/dist/context/ai-params.js +44 -0
  13. package/dist/decorators/agent-function.decorator.d.ts +22 -0
  14. package/dist/decorators/agent-function.decorator.js +36 -0
  15. package/dist/decorators/index.d.ts +3 -0
  16. package/dist/decorators/index.js +3 -0
  17. package/dist/decorators/inject-embedding-model.decorator.d.ts +10 -0
  18. package/dist/decorators/inject-embedding-model.decorator.js +16 -0
  19. package/dist/decorators/inject-model.decorator.d.ts +10 -0
  20. package/dist/decorators/inject-model.decorator.js +16 -0
  21. package/dist/errors/ai.errors.d.ts +54 -0
  22. package/dist/errors/ai.errors.js +62 -0
  23. package/dist/errors/index.d.ts +1 -0
  24. package/dist/errors/index.js +1 -0
  25. package/dist/http/keep-alive.util.d.ts +6 -0
  26. package/dist/http/keep-alive.util.js +38 -0
  27. package/dist/http/node-response.util.d.ts +14 -0
  28. package/dist/http/node-response.util.js +104 -0
  29. package/dist/index.d.ts +14 -0
  30. package/dist/index.js +16 -0
  31. package/dist/interfaces/agent-function.interface.d.ts +53 -0
  32. package/dist/interfaces/agent-function.interface.js +1 -0
  33. package/dist/interfaces/ai-call-options.interface.d.ts +65 -0
  34. package/dist/interfaces/ai-call-options.interface.js +1 -0
  35. package/dist/interfaces/ai-embedding.interface.d.ts +79 -0
  36. package/dist/interfaces/ai-embedding.interface.js +1 -0
  37. package/dist/interfaces/ai-module-options.interface.d.ts +110 -0
  38. package/dist/interfaces/ai-module-options.interface.js +1 -0
  39. package/dist/interfaces/ai-result.interface.d.ts +57 -0
  40. package/dist/interfaces/ai-result.interface.js +1 -0
  41. package/dist/interfaces/ai-schema.interface.d.ts +44 -0
  42. package/dist/interfaces/ai-schema.interface.js +1 -0
  43. package/dist/interfaces/ai-tool.interface.d.ts +22 -0
  44. package/dist/interfaces/ai-tool.interface.js +1 -0
  45. package/dist/interfaces/ai-usage.interface.d.ts +30 -0
  46. package/dist/interfaces/ai-usage.interface.js +1 -0
  47. package/dist/interfaces/index.d.ts +8 -0
  48. package/dist/interfaces/index.js +8 -0
  49. package/dist/models/ai-embedding-models.service.d.ts +31 -0
  50. package/dist/models/ai-embedding-models.service.js +138 -0
  51. package/dist/models/ai-models.service.d.ts +29 -0
  52. package/dist/models/ai-models.service.js +112 -0
  53. package/dist/models/model-registry.util.d.ts +6 -0
  54. package/dist/models/model-registry.util.js +21 -0
  55. package/dist/models/model-requests.d.ts +5 -0
  56. package/dist/models/model-requests.js +20 -0
  57. package/dist/runtime/agent-loop.d.ts +30 -0
  58. package/dist/runtime/agent-loop.js +81 -0
  59. package/dist/runtime/call-plan.d.ts +54 -0
  60. package/dist/runtime/call-plan.js +105 -0
  61. package/dist/runtime/embedding-call.d.ts +38 -0
  62. package/dist/runtime/embedding-call.js +120 -0
  63. package/dist/runtime/model-policy.d.ts +53 -0
  64. package/dist/runtime/model-policy.js +202 -0
  65. package/dist/runtime/steps.d.ts +32 -0
  66. package/dist/runtime/steps.js +190 -0
  67. package/dist/runtime/text-stream.d.ts +64 -0
  68. package/dist/runtime/text-stream.js +211 -0
  69. package/dist/services/ai.service.d.ts +70 -0
  70. package/dist/services/ai.service.js +183 -0
  71. package/dist/testing/fake-embedding-model.d.ts +57 -0
  72. package/dist/testing/fake-embedding-model.js +82 -0
  73. package/dist/testing/fake-model.d.ts +96 -0
  74. package/dist/testing/fake-model.js +201 -0
  75. package/dist/testing/index.d.ts +32 -0
  76. package/dist/testing/index.js +27 -0
  77. package/dist/tools/ai-tools.service.d.ts +77 -0
  78. package/dist/tools/ai-tools.service.js +234 -0
  79. package/dist/tools/tool-output.util.d.ts +6 -0
  80. package/dist/tools/tool-output.util.js +38 -0
  81. package/dist/utils/duration.util.d.ts +3 -0
  82. package/dist/utils/duration.util.js +17 -0
  83. package/dist/utils/errors.util.d.ts +16 -0
  84. package/dist/utils/errors.util.js +35 -0
  85. package/dist/utils/names.util.d.ts +2 -0
  86. package/dist/utils/names.util.js +10 -0
  87. package/dist/utils/schema.util.d.ts +16 -0
  88. package/dist/utils/schema.util.js +70 -0
  89. package/dist/utils/usage.util.d.ts +10 -0
  90. package/dist/utils/usage.util.js +42 -0
  91. package/dist/workflows/ai-agent.interfaces.d.ts +70 -0
  92. package/dist/workflows/ai-agent.interfaces.js +1 -0
  93. package/dist/workflows/ai-agent.workflow.d.ts +15 -0
  94. package/dist/workflows/ai-agent.workflow.js +25 -0
  95. package/dist/workflows/ai-agents.service.d.ts +83 -0
  96. package/dist/workflows/ai-agents.service.js +277 -0
  97. package/dist/workflows/ai-workflows.module.d.ts +6 -0
  98. package/dist/workflows/ai-workflows.module.js +22 -0
  99. package/dist/workflows/index.d.ts +6 -0
  100. package/dist/workflows/index.js +5 -0
  101. package/package.json +115 -4
@@ -0,0 +1,57 @@
1
+ import type { FinishReason, ModelMessage } from 'ai';
2
+ import type { AiUsage } from './ai-usage.interface.js';
3
+ /** Which model answered. */
4
+ export interface AiModelInfo {
5
+ /**
6
+ * Its name in `models` or `embeddingModels`; `undefined` for an SDK model object passed directly
7
+ * to the call.
8
+ */
9
+ name?: string;
10
+ provider: string;
11
+ modelId: string;
12
+ }
13
+ export interface AiToolCall {
14
+ toolCallId: string;
15
+ toolName: string;
16
+ input: unknown;
17
+ }
18
+ export interface AiToolResult extends AiToolCall {
19
+ /** What the tool returned; absent when it failed. */
20
+ output?: unknown;
21
+ /** What the model was told about a failure. */
22
+ error?: string;
23
+ }
24
+ /** One model call of a text generation, with the tool calls it made and their results. */
25
+ export interface AiStep {
26
+ index: number;
27
+ model: AiModelInfo;
28
+ text: string;
29
+ toolCalls: AiToolCall[];
30
+ toolResults: AiToolResult[];
31
+ finishReason: FinishReason;
32
+ usage: AiUsage;
33
+ cost?: number;
34
+ /** Attempts made for this step, over every model tried. */
35
+ attempts: number;
36
+ }
37
+ export interface AiTextResult {
38
+ /** The text of the last step. */
39
+ text: string;
40
+ /** The last step's: `tool-calls` when `maxSteps` ran out while the model still asked for tools. */
41
+ finishReason: FinishReason;
42
+ steps: AiStep[];
43
+ /** Summed over the steps. */
44
+ usage: AiUsage;
45
+ /** Summed over the steps whose model has `pricing`; `undefined` when none had. */
46
+ cost?: number;
47
+ toolCalls: AiToolCall[];
48
+ toolResults: AiToolResult[];
49
+ /** The assistant and tool messages of every step, to append to a conversation. */
50
+ responseMessages: ModelMessage[];
51
+ /** The model of the last step. */
52
+ model: AiModelInfo;
53
+ }
54
+ export interface AiObjectResult<T> extends AiTextResult {
55
+ /** The answer, validated by the schema. */
56
+ object: T;
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A schema the package accepts: a Standard Schema that also produces JSON Schema (`~standard.jsonSchema`), as
3
+ * `@nestjs/mcp`'s schemas are, so that one schema serves a method's `@AgentFunction()` and its `@Tool()`. Zod 4.2+
4
+ * and ArkType schemas are, as they are; Valibot through
5
+ * `toStandardJsonSchema()` from `@valibot/to-json-schema`; a JSON Schema document through `fromJsonSchema()` from
6
+ * `@modelcontextprotocol/server`. The model is sent the JSON Schema, and what it answers is validated by the schema.
7
+ *
8
+ * The shape of the [Standard Schema](https://standardschema.dev) interfaces, declared here so that the package has no
9
+ * dependency for them.
10
+ */
11
+ export interface AiSchema<Input = any, Output = Input> {
12
+ readonly '~standard': {
13
+ readonly version: 1;
14
+ readonly vendor: string;
15
+ readonly types?: {
16
+ readonly input: Input;
17
+ readonly output: Output;
18
+ } | undefined;
19
+ readonly validate: (value: unknown, options?: {
20
+ readonly libraryOptions?: Record<string, unknown> | undefined;
21
+ } | undefined) => AiSchemaResult<Output> | Promise<AiSchemaResult<Output>>;
22
+ readonly jsonSchema: {
23
+ readonly input: (options: AiJsonSchemaOptions) => Record<string, unknown>;
24
+ readonly output: (options: AiJsonSchemaOptions) => Record<string, unknown>;
25
+ };
26
+ };
27
+ }
28
+ export interface AiJsonSchemaOptions {
29
+ readonly target: 'draft-2020-12' | 'draft-07' | 'openapi-3.0' | ({} & string);
30
+ readonly libraryOptions?: Record<string, unknown> | undefined;
31
+ }
32
+ export type AiSchemaResult<Output> = {
33
+ readonly value: Output;
34
+ readonly issues?: undefined;
35
+ } | {
36
+ readonly issues: ReadonlyArray<{
37
+ readonly message: string;
38
+ readonly path?: ReadonlyArray<PropertyKey | {
39
+ readonly key: PropertyKey;
40
+ }> | undefined;
41
+ }>;
42
+ };
43
+ /** The type a schema validates to. */
44
+ export type InferSchemaOutput<S extends AiSchema> = NonNullable<S['~standard']['types']>['output'];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Who an agent acts for. Agent functions receive it in their context (`AgentFunctionContext`), and guards read it
3
+ * there, as a route's guards read the request.
4
+ *
5
+ * ```ts
6
+ * caller: { request } // in a controller: the request's user, headers, session
7
+ * caller: { user: job.data.user, scopes } // in a queue job
8
+ * ```
9
+ */
10
+ export interface AiCaller<TUser = any, TRequest = any> {
11
+ /** The HTTP platform's request the agent runs in (Express, Fastify, the Node adapter). */
12
+ request?: TRequest;
13
+ /** The user, where there is no request (a queue job, a workflow); in a request, it is `request.user`. */
14
+ user?: TUser;
15
+ /** Scopes the caller holds, checked against an agent function's `scopes`. */
16
+ scopes?: readonly string[];
17
+ }
18
+ /**
19
+ * What a call's `tools` lists: agent function names, and provider classes (every agent function of the class). The
20
+ * model is offered each as a tool.
21
+ */
22
+ export type AiToolSelection = ReadonlyArray<string | (abstract new (...args: any[]) => unknown)>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { AiModelInfo } from './ai-result.interface.js';
2
+ /** Tokens a model call used. A count the provider did not report is 0. */
3
+ export interface AiUsage {
4
+ /** Every input token, cached ones included. */
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ totalTokens: number;
8
+ /** The part of `inputTokens` read from the provider's prompt cache. */
9
+ cachedInputTokens: number;
10
+ /** The part of `outputTokens` spent on reasoning. */
11
+ reasoningTokens: number;
12
+ }
13
+ /** One model call that answered: a step of `generateText()`, `generateObject()` or `streamText()`. */
14
+ export interface AiUsageEvent {
15
+ /** The call's `label`, to tell calls apart (`'triage'`). */
16
+ label?: string;
17
+ model: AiModelInfo;
18
+ /** 0-based step of the call: a call that uses tools makes several model calls. */
19
+ step: number;
20
+ /** 1-based attempt of this model for this step. */
21
+ attempt: number;
22
+ /** Whether it was a fallback model. */
23
+ fallback: boolean;
24
+ stream: boolean;
25
+ usage: AiUsage;
26
+ /** In the unit of the model's `pricing`; `undefined` without one. */
27
+ cost?: number;
28
+ durationMs: number;
29
+ finishReason: string;
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export * from './agent-function.interface.js';
2
+ export * from './ai-call-options.interface.js';
3
+ export * from './ai-embedding.interface.js';
4
+ export * from './ai-module-options.interface.js';
5
+ export * from './ai-result.interface.js';
6
+ export * from './ai-schema.interface.js';
7
+ export * from './ai-tool.interface.js';
8
+ export * from './ai-usage.interface.js';
@@ -0,0 +1,8 @@
1
+ export * from './agent-function.interface.js';
2
+ export * from './ai-call-options.interface.js';
3
+ export * from './ai-embedding.interface.js';
4
+ export * from './ai-module-options.interface.js';
5
+ export * from './ai-result.interface.js';
6
+ export * from './ai-schema.interface.js';
7
+ export * from './ai-tool.interface.js';
8
+ export * from './ai-usage.interface.js';
@@ -0,0 +1,31 @@
1
+ import type { EmbeddingModel } from 'ai';
2
+ import type { AiEmbeddingModelPricing, AiEmbeddingModelReference, AiEmbeddingSettings } from '../interfaces/ai-embedding.interface.js';
3
+ import type { AiModuleOptions } from '../interfaces/ai-module-options.interface.js';
4
+ import type { AiModelInfo } from '../interfaces/ai-result.interface.js';
5
+ /** One resolved embedding model. Internal; embedding calls never switch to fallback models. */
6
+ export interface ResolvedEmbeddingModel {
7
+ info: AiModelInfo;
8
+ model: EmbeddingModel;
9
+ pricing?: AiEmbeddingModelPricing;
10
+ settings: AiEmbeddingSettings;
11
+ }
12
+ /** The configured embedding models, independent of language-model names. */
13
+ export declare class AiEmbeddingModels {
14
+ private readonly models;
15
+ constructor(options: AiModuleOptions, overrides?: Record<string, EmbeddingModel> | null);
16
+ names(): string[];
17
+ /** The SDK model, for direct calls without `AiService`'s resilience or usage events. */
18
+ get(name?: string): EmbeddingModel;
19
+ has(name: string): boolean;
20
+ /**
21
+ * @internal What `@InjectEmbeddingModel()` injects; unknown names fail during module
22
+ * initialization.
23
+ */
24
+ injected(name: string, inquirer: unknown): EmbeddingModel;
25
+ /**
26
+ * @internal Resolves a name or an SDK object. Direct objects have no registered settings or
27
+ * pricing.
28
+ */
29
+ resolve(reference: AiEmbeddingModelReference | undefined): ResolvedEmbeddingModel;
30
+ private known;
31
+ }
@@ -0,0 +1,138 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable, Optional } from '@nestjs/common';
14
+ import { AI_EMBEDDING_MODEL_OVERRIDES, AI_MODULE_OPTIONS, DEFAULT_MODEL_NAME, } from '../ai.constants.js';
15
+ import { AiConfigurationError } from '../errors/ai.errors.js';
16
+ import { assertModelRegistries } from './model-registry.util.js';
17
+ /** The configured embedding models, independent of language-model names. */
18
+ let AiEmbeddingModels = class AiEmbeddingModels {
19
+ constructor(options, overrides) {
20
+ this.models = new Map();
21
+ assertModelRegistries(options);
22
+ assertSettings(options.embeddingDefaults, 'AiModule: `embeddingDefaults`');
23
+ for (const [name, entry] of Object.entries(options.embeddingModels ?? {})) {
24
+ const { model, pricing, ...settings } = isDefinition(entry) ? entry : { model: entry };
25
+ assertModel(model, `AiModule: the embedding model "${name}"`);
26
+ assertSettings(settings, `AiModule: the embedding model "${name}"`);
27
+ if (pricing !== undefined &&
28
+ (typeof pricing !== 'object' ||
29
+ pricing === null ||
30
+ !Number.isFinite(pricing.input) ||
31
+ pricing.input < 0)) {
32
+ throw new AiConfigurationError(`AiModule: the embedding model "${name}" pricing.input must be ` +
33
+ 'a finite nonnegative number.');
34
+ }
35
+ const used = overrides?.[name] ?? model;
36
+ assertModel(used, `AiModule: the replacement embedding model "${name}"`);
37
+ this.models.set(name, {
38
+ info: { name, ...identify(used) },
39
+ model: used,
40
+ ...(pricing !== undefined && { pricing }),
41
+ settings,
42
+ });
43
+ }
44
+ for (const name of Object.keys(overrides ?? {})) {
45
+ if (!this.models.has(name)) {
46
+ throw new AiConfigurationError(`overrideEmbeddingModels(): there is no embedding model "${name}" to override. ` +
47
+ this.known());
48
+ }
49
+ }
50
+ }
51
+ names() {
52
+ return [...this.models.keys()];
53
+ }
54
+ /** The SDK model, for direct calls without `AiService`'s resilience or usage events. */
55
+ get(name = DEFAULT_MODEL_NAME) {
56
+ return this.resolve(name).model;
57
+ }
58
+ has(name) {
59
+ return this.models.has(name);
60
+ }
61
+ /**
62
+ * @internal What `@InjectEmbeddingModel()` injects; unknown names fail during module
63
+ * initialization.
64
+ */
65
+ injected(name, inquirer) {
66
+ const resolved = this.models.get(name);
67
+ if (!resolved) {
68
+ const requester = inquirer?.constructor
69
+ ?.name;
70
+ throw new AiConfigurationError(`${requester ? `${requester}: ` : ''}@InjectEmbeddingModel('${name}') ` +
71
+ 'asks for an embedding model AiModule does not configure. ' +
72
+ `${this.known()} Add "${name}" to \`embeddingModels\` ` +
73
+ '(in forRootAsync(), to what the factory returns).');
74
+ }
75
+ return resolved.model;
76
+ }
77
+ /**
78
+ * @internal Resolves a name or an SDK object. Direct objects have no registered settings or
79
+ * pricing.
80
+ */
81
+ resolve(reference) {
82
+ if (reference === undefined || typeof reference === 'string') {
83
+ const name = reference ?? DEFAULT_MODEL_NAME;
84
+ const resolved = this.models.get(name);
85
+ if (!resolved) {
86
+ throw new AiConfigurationError(`There is no embedding model named "${name}". ${this.known()}`);
87
+ }
88
+ return resolved;
89
+ }
90
+ assertModel(reference, 'The embedding `model` option');
91
+ return { info: identify(reference), model: reference, settings: {} };
92
+ }
93
+ known() {
94
+ return `Configured embedding models: ${this.names().join(', ')}.`;
95
+ }
96
+ };
97
+ AiEmbeddingModels = __decorate([
98
+ Injectable(),
99
+ __param(0, Inject(AI_MODULE_OPTIONS)),
100
+ __param(1, Optional()),
101
+ __param(1, Inject(AI_EMBEDDING_MODEL_OVERRIDES)),
102
+ __metadata("design:paramtypes", [Object, Object])
103
+ ], AiEmbeddingModels);
104
+ export { AiEmbeddingModels };
105
+ function isDefinition(entry) {
106
+ return (typeof entry === 'object' &&
107
+ entry !== null &&
108
+ 'model' in entry &&
109
+ !('specificationVersion' in entry));
110
+ }
111
+ function assertModel(model, where) {
112
+ const valid = (typeof model === 'string' && model.length > 0) ||
113
+ (typeof model === 'object' &&
114
+ model !== null &&
115
+ typeof model.doEmbed === 'function');
116
+ if (!valid) {
117
+ throw new AiConfigurationError(`${where} is not an embedding model. ` +
118
+ 'Pass an embedding model from an AI SDK provider package.');
119
+ }
120
+ }
121
+ function assertSettings(settings, where) {
122
+ if (settings === undefined) {
123
+ return;
124
+ }
125
+ if (typeof settings !== 'object' || settings === null || Array.isArray(settings)) {
126
+ throw new AiConfigurationError(`${where} must contain embedding settings.`);
127
+ }
128
+ if (settings.maxParallelCalls !== undefined &&
129
+ (!Number.isSafeInteger(settings.maxParallelCalls) || settings.maxParallelCalls < 1)) {
130
+ throw new AiConfigurationError(`${where} maxParallelCalls must be a finite positive safe integer.`);
131
+ }
132
+ }
133
+ /** Registered string ids are resolved by the SDK's default gateway provider. */
134
+ function identify(model) {
135
+ return typeof model === 'string'
136
+ ? { provider: 'gateway', modelId: model }
137
+ : { provider: model.provider, modelId: model.modelId };
138
+ }
@@ -0,0 +1,29 @@
1
+ import type { LanguageModel } from 'ai';
2
+ import type { AiModelReference } from '../interfaces/ai-call-options.interface.js';
3
+ import type { AiCallSettings, AiModelPricing, AiModuleOptions } from '../interfaces/ai-module-options.interface.js';
4
+ import type { AiModelInfo } from '../interfaces/ai-result.interface.js';
5
+ /** A model, as calls use it. Internal. */
6
+ export interface ResolvedModel {
7
+ info: AiModelInfo;
8
+ model: LanguageModel;
9
+ pricing?: AiModelPricing;
10
+ settings: AiCallSettings;
11
+ /** The names of the models tried after it. */
12
+ fallback: string[];
13
+ }
14
+ /** The configured models, by name. */
15
+ export declare class AiModels {
16
+ private readonly models;
17
+ constructor(options: AiModuleOptions, overrides?: Record<string, LanguageModel> | null);
18
+ /** The names of the configured models. */
19
+ names(): string[];
20
+ /** The `LanguageModel` called `name`, for code that calls `ai` directly. */
21
+ get(name?: string): LanguageModel;
22
+ /** Whether a model called `name` is configured. */
23
+ has(name: string): boolean;
24
+ /** @internal What `@InjectModel(name)` injects into `inquirer`; a name that is not configured fails at startup. */
25
+ injected(name: string, inquirer: unknown): LanguageModel;
26
+ /** @internal A name, or a `LanguageModel` given to a call (with no settings of its own). */
27
+ resolve(reference: AiModelReference | undefined): ResolvedModel;
28
+ private known;
29
+ }
@@ -0,0 +1,112 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable, Optional } from '@nestjs/common';
14
+ import { AiConfigurationError } from '../errors/ai.errors.js';
15
+ import { AI_MODEL_OVERRIDES, AI_MODULE_OPTIONS, DEFAULT_MODEL_NAME } from '../ai.constants.js';
16
+ import { assertModelRegistries } from './model-registry.util.js';
17
+ /** The configured models, by name. */
18
+ let AiModels = class AiModels {
19
+ constructor(options, overrides) {
20
+ this.models = new Map();
21
+ assertModelRegistries(options);
22
+ const models = options.models ?? {};
23
+ for (const [name, entry] of Object.entries(models)) {
24
+ const { model, pricing, fallback, ...settings } = isDefinition(entry) ? entry : { model: entry };
25
+ assertModel(model, `AiModule: the model "${name}"`);
26
+ const replacement = overrides?.[name];
27
+ const used = replacement ?? model;
28
+ this.models.set(name, {
29
+ info: { name, ...identify(used) },
30
+ model: used,
31
+ ...(pricing !== undefined && { pricing }),
32
+ settings,
33
+ fallback: fallback === undefined ? [] : [fallback].flat(),
34
+ });
35
+ }
36
+ for (const name of Object.keys(overrides ?? {})) {
37
+ if (!this.models.has(name)) {
38
+ throw new AiConfigurationError(`overrideModels(): there is no model "${name}" to override. ${this.known()}`);
39
+ }
40
+ }
41
+ for (const [name, resolved] of this.models) {
42
+ for (const next of resolved.fallback) {
43
+ if (!this.models.has(next)) {
44
+ throw new AiConfigurationError(`AiModule: the model "${name}" falls back to "${next}", which is not configured. ${this.known()}`);
45
+ }
46
+ }
47
+ }
48
+ }
49
+ /** The names of the configured models. */
50
+ names() {
51
+ return [...this.models.keys()];
52
+ }
53
+ /** The `LanguageModel` called `name`, for code that calls `ai` directly. */
54
+ get(name = DEFAULT_MODEL_NAME) {
55
+ return this.resolve(name).model;
56
+ }
57
+ /** Whether a model called `name` is configured. */
58
+ has(name) {
59
+ return this.models.has(name);
60
+ }
61
+ /** @internal What `@InjectModel(name)` injects into `inquirer`; a name that is not configured fails at startup. */
62
+ injected(name, inquirer) {
63
+ const resolved = this.models.get(name);
64
+ if (!resolved) {
65
+ const requester = inquirer?.constructor?.name;
66
+ throw new AiConfigurationError(`${requester ? `${requester}: ` : ''}@InjectModel('${name}') asks for a model AiModule does not configure. ` +
67
+ `${this.known()} Add "${name}" to \`models\` (in forRootAsync(), to what the factory returns).`);
68
+ }
69
+ return resolved.model;
70
+ }
71
+ /** @internal A name, or a `LanguageModel` given to a call (with no settings of its own). */
72
+ resolve(reference) {
73
+ if (reference === undefined || typeof reference === 'string') {
74
+ const name = reference ?? DEFAULT_MODEL_NAME;
75
+ const resolved = this.models.get(name);
76
+ if (!resolved) {
77
+ throw new AiConfigurationError(`There is no model named "${name}". ${this.known()}`);
78
+ }
79
+ return resolved;
80
+ }
81
+ assertModel(reference, 'The `model` option');
82
+ return { info: identify(reference), model: reference, settings: {}, fallback: [] };
83
+ }
84
+ known() {
85
+ return `Configured: ${this.names().join(', ')}.`;
86
+ }
87
+ };
88
+ AiModels = __decorate([
89
+ Injectable(),
90
+ __param(0, Inject(AI_MODULE_OPTIONS)),
91
+ __param(1, Optional()),
92
+ __param(1, Inject(AI_MODEL_OVERRIDES)),
93
+ __metadata("design:paramtypes", [Object, Object])
94
+ ], AiModels);
95
+ export { AiModels };
96
+ function isDefinition(entry) {
97
+ return typeof entry === 'object' && entry !== null && 'model' in entry && !('specificationVersion' in entry);
98
+ }
99
+ function assertModel(model, where) {
100
+ const valid = (typeof model === 'string' && model.length > 0) ||
101
+ (typeof model === 'object' && model !== null && typeof model.doGenerate === 'function');
102
+ if (!valid) {
103
+ throw new AiConfigurationError(`${where} is not a language model. Pass one from a provider package, e.g. anthropic('claude-opus-5-5') from @ai-sdk/anthropic.`);
104
+ }
105
+ }
106
+ /** A string model id goes through the AI Gateway (`ai`'s default provider). */
107
+ function identify(model) {
108
+ if (typeof model === 'string') {
109
+ return { provider: 'gateway', modelId: model };
110
+ }
111
+ return { provider: model.provider, modelId: model.modelId };
112
+ }
@@ -0,0 +1,6 @@
1
+ import type { AiModuleOptions } from '../interfaces/ai-module-options.interface.js';
2
+ /**
3
+ * Checks registry structure without contacting a provider. Either kind may be omitted, but not
4
+ * both.
5
+ */
6
+ export declare function assertModelRegistries(options: AiModuleOptions): void;
@@ -0,0 +1,21 @@
1
+ import { AiConfigurationError } from '../errors/ai.errors.js';
2
+ /**
3
+ * Checks registry structure without contacting a provider. Either kind may be omitted, but not
4
+ * both.
5
+ */
6
+ export function assertModelRegistries(options) {
7
+ let count = 0;
8
+ for (const key of ['models', 'embeddingModels']) {
9
+ const entries = options?.[key];
10
+ if (entries === undefined) {
11
+ continue;
12
+ }
13
+ if (typeof entries !== 'object' || entries === null || Array.isArray(entries)) {
14
+ throw new AiConfigurationError(`AiModule: \`${key}\` must be a record of named models.`);
15
+ }
16
+ count += Object.keys(entries).length;
17
+ }
18
+ if (count === 0) {
19
+ throw new AiConfigurationError('AiModule: configure at least one model in `models` or `embeddingModels`.');
20
+ }
21
+ }
@@ -0,0 +1,5 @@
1
+ export declare function recordModelRequest(name: string): void;
2
+ export declare function requestedModelNames(): string[];
3
+ /** Embedding injection requests use a separate namespace, even when the names are identical. */
4
+ export declare function recordEmbeddingModelRequest(name: string): void;
5
+ export declare function requestedEmbeddingModelNames(): string[];
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The model names `@InjectModel()` asks for, recorded as the decorators run. `AiModule` provides a token for each
3
+ * name asked for by the time it is defined (with `modelNames` and the names it configures), so that a name it does
4
+ * not configure fails with a message of its own, not with Nest's missing-provider error.
5
+ */
6
+ const requested = new Set();
7
+ const requestedEmbeddings = new Set();
8
+ export function recordModelRequest(name) {
9
+ requested.add(name);
10
+ }
11
+ export function requestedModelNames() {
12
+ return [...requested];
13
+ }
14
+ /** Embedding injection requests use a separate namespace, even when the names are identical. */
15
+ export function recordEmbeddingModelRequest(name) {
16
+ requestedEmbeddings.add(name);
17
+ }
18
+ export function requestedEmbeddingModelNames() {
19
+ return [...requestedEmbeddings];
20
+ }
@@ -0,0 +1,30 @@
1
+ import type { UIMessageChunk } from 'ai';
2
+ import type { AiTextResult } from '../interfaces/ai-result.interface.js';
3
+ import type { AiUsage, AiUsageEvent } from '../interfaces/ai-usage.interface.js';
4
+ import { type StepAttempt } from './model-policy.js';
5
+ import type { CallPlan } from './call-plan.js';
6
+ import { type StepOutcome, type StepOutput } from './steps.js';
7
+ export type LoopMode = {
8
+ stream: false;
9
+ output?: StepOutput;
10
+ } | {
11
+ stream: true;
12
+ emit: (chunk: UIMessageChunk) => void;
13
+ };
14
+ export interface LoopResult {
15
+ result: AiTextResult;
16
+ /** The last step's parsed answer (structured output). */
17
+ output?: unknown;
18
+ /** The last step's `finish` chunk (streams). */
19
+ finish?: UIMessageChunk;
20
+ }
21
+ /**
22
+ * The agent loop: a model call, then, while the model asks for tools (which run inside the call), another one with
23
+ * the conversation so far, up to `maxSteps`. Each model call runs under its resilience policies (retry, timeout,
24
+ * fallback); the budget is checked before each. `report` receives a usage event per model call that answered.
25
+ */
26
+ export declare function runLoop(plan: CallPlan, mode: LoopMode, report: (event: AiUsageEvent) => void): Promise<LoopResult>;
27
+ /** The usage event of a model call that answered. */
28
+ export declare function usageEvent(plan: CallPlan, attempt: StepAttempt, outcome: StepOutcome, stream: boolean): AiUsageEvent;
29
+ /** Throws when the call has spent its budget. */
30
+ export declare function assertWithinBudget(plan: Pick<CallPlan, 'budget'>, usage: AiUsage, cost: number | undefined): void;