@mgvdev/nestjs-ai 0.1.0
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/LICENSE +21 -0
- package/README.md +655 -0
- package/dist/ai-module-options.interface-B5X4AFLC.d.cts +192 -0
- package/dist/ai-module-options.interface-Ca6y_MY2.d.ts +192 -0
- package/dist/conversation-store.interface-CtQY-qcc.d.cts +30 -0
- package/dist/conversation-store.interface-CtQY-qcc.d.ts +30 -0
- package/dist/index.cjs +3209 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1441 -0
- package/dist/index.d.ts +1441 -0
- package/dist/index.js +3163 -0
- package/dist/index.js.map +1 -0
- package/dist/stream-to-socket-fJphU0WN.d.cts +47 -0
- package/dist/stream-to-socket-fJphU0WN.d.ts +47 -0
- package/dist/testing.cjs +2395 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +46 -0
- package/dist/testing.d.ts +46 -0
- package/dist/testing.js +2391 -0
- package/dist/testing.js.map +1 -0
- package/dist/typeorm.cjs +115 -0
- package/dist/typeorm.cjs.map +1 -0
- package/dist/typeorm.d.cts +44 -0
- package/dist/typeorm.d.ts +44 -0
- package/dist/typeorm.js +113 -0
- package/dist/typeorm.js.map +1 -0
- package/dist/websocket.cjs +154 -0
- package/dist/websocket.cjs.map +1 -0
- package/dist/websocket.d.cts +25 -0
- package/dist/websocket.d.ts +25 -0
- package/dist/websocket.js +152 -0
- package/dist/websocket.js.map +1 -0
- package/documentation/README.md +44 -0
- package/documentation/agents-and-tools.md +102 -0
- package/documentation/api-reference.md +117 -0
- package/documentation/configuration.md +87 -0
- package/documentation/content-safety.md +51 -0
- package/documentation/embeddings-and-rag.md +105 -0
- package/documentation/evals-and-testing.md +56 -0
- package/documentation/getting-started.md +96 -0
- package/documentation/guardrails-events-telemetry.md +72 -0
- package/documentation/jobs-and-realtime.md +69 -0
- package/documentation/memory.md +101 -0
- package/documentation/multimodal.md +49 -0
- package/documentation/orchestration-and-mcp.md +68 -0
- package/documentation/prompts.md +51 -0
- package/documentation/reliability.md +90 -0
- package/documentation/structured-output-and-streaming.md +81 -0
- package/package.json +146 -0
- package/skill/nestjs-ai/SKILL.md +154 -0
package/dist/typeorm.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { PrimaryGeneratedColumn, Index, Column, Entity } from 'typeorm';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
6
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
7
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
8
|
+
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;
|
|
9
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
10
|
+
}
|
|
11
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
12
|
+
function _ts_metadata(k, v) {
|
|
13
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
14
|
+
}
|
|
15
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
16
|
+
var ConversationMessageEntity = class {
|
|
17
|
+
static {
|
|
18
|
+
__name(this, "ConversationMessageEntity");
|
|
19
|
+
}
|
|
20
|
+
id;
|
|
21
|
+
conversationId;
|
|
22
|
+
role;
|
|
23
|
+
/** Message content stored as JSON to preserve structured parts. */
|
|
24
|
+
content;
|
|
25
|
+
position;
|
|
26
|
+
createdAt;
|
|
27
|
+
};
|
|
28
|
+
_ts_decorate([
|
|
29
|
+
PrimaryGeneratedColumn("uuid"),
|
|
30
|
+
_ts_metadata("design:type", String)
|
|
31
|
+
], ConversationMessageEntity.prototype, "id", void 0);
|
|
32
|
+
_ts_decorate([
|
|
33
|
+
Index(),
|
|
34
|
+
Column(),
|
|
35
|
+
_ts_metadata("design:type", String)
|
|
36
|
+
], ConversationMessageEntity.prototype, "conversationId", void 0);
|
|
37
|
+
_ts_decorate([
|
|
38
|
+
Column(),
|
|
39
|
+
_ts_metadata("design:type", String)
|
|
40
|
+
], ConversationMessageEntity.prototype, "role", void 0);
|
|
41
|
+
_ts_decorate([
|
|
42
|
+
Column({
|
|
43
|
+
type: "simple-json"
|
|
44
|
+
}),
|
|
45
|
+
_ts_metadata("design:type", Object)
|
|
46
|
+
], ConversationMessageEntity.prototype, "content", void 0);
|
|
47
|
+
_ts_decorate([
|
|
48
|
+
Column({
|
|
49
|
+
type: "bigint"
|
|
50
|
+
}),
|
|
51
|
+
_ts_metadata("design:type", Number)
|
|
52
|
+
], ConversationMessageEntity.prototype, "position", void 0);
|
|
53
|
+
_ts_decorate([
|
|
54
|
+
Column({
|
|
55
|
+
type: "datetime",
|
|
56
|
+
default: /* @__PURE__ */ __name(() => "CURRENT_TIMESTAMP", "default")
|
|
57
|
+
}),
|
|
58
|
+
_ts_metadata("design:type", typeof Date === "undefined" ? Object : Date)
|
|
59
|
+
], ConversationMessageEntity.prototype, "createdAt", void 0);
|
|
60
|
+
ConversationMessageEntity = _ts_decorate([
|
|
61
|
+
Entity("ai_conversation_messages")
|
|
62
|
+
], ConversationMessageEntity);
|
|
63
|
+
|
|
64
|
+
// src/memory/adapters/typeorm/typeorm-conversation.store.ts
|
|
65
|
+
var TypeOrmConversationStore = class {
|
|
66
|
+
static {
|
|
67
|
+
__name(this, "TypeOrmConversationStore");
|
|
68
|
+
}
|
|
69
|
+
repository;
|
|
70
|
+
constructor(repository) {
|
|
71
|
+
this.repository = repository;
|
|
72
|
+
}
|
|
73
|
+
async load(conversationId) {
|
|
74
|
+
const rows = await this.repository.find({
|
|
75
|
+
where: {
|
|
76
|
+
conversationId
|
|
77
|
+
},
|
|
78
|
+
order: {
|
|
79
|
+
position: "ASC"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return rows.map((row) => ({
|
|
83
|
+
role: row.role,
|
|
84
|
+
content: row.content
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
async append(conversationId, messages) {
|
|
88
|
+
if (messages.length === 0) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const base = await this.repository.count({
|
|
92
|
+
where: {
|
|
93
|
+
conversationId
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const entities = messages.map((message, index) => this.repository.create({
|
|
97
|
+
conversationId,
|
|
98
|
+
role: message.role,
|
|
99
|
+
content: message.content,
|
|
100
|
+
position: base + index
|
|
101
|
+
}));
|
|
102
|
+
await this.repository.save(entities);
|
|
103
|
+
}
|
|
104
|
+
async clear(conversationId) {
|
|
105
|
+
await this.repository.delete({
|
|
106
|
+
conversationId
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export { ConversationMessageEntity, TypeOrmConversationStore };
|
|
112
|
+
//# sourceMappingURL=typeorm.js.map
|
|
113
|
+
//# sourceMappingURL=typeorm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/memory/adapters/typeorm/conversation-message.entity.ts","../src/memory/adapters/typeorm/typeorm-conversation.store.ts"],"names":["ConversationMessageEntity","id","conversationId","role","content","position","createdAt","type","default","TypeOrmConversationStore","repository","load","rows","find","where","order","map","row","append","messages","length","base","count","entities","message","index","create","save","clear","delete"],"mappings":";;;;;;;;;;;;;;;AAQO,IAAMA,4BAAN,MAAMA;AAAAA,EAAAA;;;AAEXC,EAAAA,EAAAA;AAIAC,EAAAA,cAAAA;AAGAC,EAAAA,IAAAA;;AAIAC,EAAAA,OAAAA;AAGAC,EAAAA,QAAAA;AAGAC,EAAAA,SAAAA;AACF;;;;;;;;;;;;;;;;IARYC,IAAAA,EAAM;;;;;;IAGNA,IAAAA,EAAM;;;;;;IAGNA,IAAAA,EAAM,UAAA;AAAYC,IAAAA,OAAAA,+BAAe,mBAAA,EAAN,SAAA;;;;;;;;;ACLhC,IAAMC,2BAAN,MAAMA;EAhBb;;;;AAiBE,EAAA,WAAA,CACmBC,UAAAA,EACjB;SADiBA,UAAAA,GAAAA,UAAAA;AAChB,EAAA;AAEH,EAAA,MAAMC,KAAKT,cAAAA,EAA8C;AACvD,IAAA,MAAMU,IAAAA,GAAO,MAAM,IAAA,CAAKF,UAAAA,CAAWG,IAAAA,CAAK;MACtCC,KAAAA,EAAO;AAAEZ,QAAAA;AAAe,OAAA;MACxBa,KAAAA,EAAO;QAAEV,QAAAA,EAAU;AAAM;KAC3B,CAAA;AACA,IAAA,OAAOO,IAAAA,CAAKI,GAAAA,CAAI,CAACC,GAAAA,MAAS;AACxBd,MAAAA,IAAAA,EAAMc,GAAAA,CAAId,IAAAA;AACVC,MAAAA,OAAAA,EAASa,GAAAA,CAAIb;KACf,CAAA,CAAA;AACF,EAAA;EAEA,MAAMc,MAAAA,CACJhB,gBACAiB,QAAAA,EACe;AACf,IAAA,IAAIA,QAAAA,CAASC,WAAW,CAAA,EAAG;AACzB,MAAA;AACF,IAAA;AACA,IAAA,MAAMC,IAAAA,GAAO,MAAM,IAAA,CAAKX,UAAAA,CAAWY,KAAAA,CAAM;MAAER,KAAAA,EAAO;AAAEZ,QAAAA;AAAe;KAAE,CAAA;AACrE,IAAA,MAAMqB,QAAAA,GAAWJ,SAASH,GAAAA,CAAI,CAACQ,SAASC,KAAAA,KACtC,IAAA,CAAKf,WAAWgB,MAAAA,CAAO;AACrBxB,MAAAA,cAAAA;AACAC,MAAAA,IAAAA,EAAMqB,OAAAA,CAAQrB,IAAAA;AACdC,MAAAA,OAAAA,EAASoB,OAAAA,CAAQpB,OAAAA;AACjBC,MAAAA,QAAAA,EAAUgB,IAAAA,GAAOI;AACnB,KAAA,CAAA,CAAA;AAEF,IAAA,MAAM,IAAA,CAAKf,UAAAA,CAAWiB,IAAAA,CAAKJ,QAAAA,CAAAA;AAC7B,EAAA;AAEA,EAAA,MAAMK,MAAM1B,cAAAA,EAAuC;AACjD,IAAA,MAAM,IAAA,CAAKQ,WAAWmB,MAAAA,CAAO;AAAE3B,MAAAA;KAAe,CAAA;AAChD,EAAA;AACF","file":"typeorm.js","sourcesContent":["import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';\n\n/**\n * Persisted conversation message. Register this entity in your TypeORM\n * `DataSource` (`entities: [ConversationMessageEntity]`) and wire\n * `TypeOrmConversationStore` as the module's `conversationStore`.\n */\n@Entity('ai_conversation_messages')\nexport class ConversationMessageEntity {\n @PrimaryGeneratedColumn('uuid')\n id!: string;\n\n @Index()\n @Column()\n conversationId!: string;\n\n @Column()\n role!: string;\n\n /** Message content stored as JSON to preserve structured parts. */\n @Column({ type: 'simple-json' })\n content!: unknown;\n\n @Column({ type: 'bigint' })\n position!: number;\n\n @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })\n createdAt!: Date;\n}\n","import type { Repository } from 'typeorm';\nimport type { AiMessage } from '../../../messages/message.types.js';\nimport type { ConversationStore } from '../../conversation-store.interface.js';\nimport { ConversationMessageEntity } from './conversation-message.entity.js';\n\n/**\n * TypeORM-backed conversation store. Framework-agnostic: pass a\n * `Repository<ConversationMessageEntity>` (obtain it via `@nestjs/typeorm`'s\n * `getRepositoryToken` in a factory provider). Messages are ordered by an\n * append-only `position` column.\n *\n * @example\n * ```ts\n * AiModule.forRoot({\n * conversationStore: {\n * useFactory: (repo) => new TypeOrmConversationStore(repo),\n * inject: [getRepositoryToken(ConversationMessageEntity)],\n * },\n * })\n * ```\n */\nexport class TypeOrmConversationStore implements ConversationStore {\n constructor(\n private readonly repository: Repository<ConversationMessageEntity>,\n ) {}\n\n async load(conversationId: string): Promise<AiMessage[]> {\n const rows = await this.repository.find({\n where: { conversationId },\n order: { position: 'ASC' },\n });\n return rows.map((row) => ({\n role: row.role,\n content: row.content,\n })) as AiMessage[];\n }\n\n async append(\n conversationId: string,\n messages: AiMessage[],\n ): Promise<void> {\n if (messages.length === 0) {\n return;\n }\n const base = await this.repository.count({ where: { conversationId } });\n const entities = messages.map((message, index) =>\n this.repository.create({\n conversationId,\n role: message.role,\n content: message.content,\n position: base + index,\n }),\n );\n await this.repository.save(entities);\n }\n\n async clear(conversationId: string): Promise<void> {\n await this.repository.delete({ conversationId });\n }\n}\n"]}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var websockets = require('@nestjs/websockets');
|
|
4
|
+
var common = require('@nestjs/common');
|
|
5
|
+
var core = require('@nestjs/core');
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
|
|
10
|
+
// src/ai.constants.ts
|
|
11
|
+
var AGENT_METADATA = /* @__PURE__ */ Symbol("nestjs-ai:agent");
|
|
12
|
+
|
|
13
|
+
// src/agent/orchestration/agent-registry.ts
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
21
|
+
function _ts_metadata(k, v) {
|
|
22
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
23
|
+
}
|
|
24
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
25
|
+
var AgentRegistry = class {
|
|
26
|
+
static {
|
|
27
|
+
__name(this, "AgentRegistry");
|
|
28
|
+
}
|
|
29
|
+
discovery;
|
|
30
|
+
agents = /* @__PURE__ */ new Map();
|
|
31
|
+
constructor(discovery) {
|
|
32
|
+
this.discovery = discovery;
|
|
33
|
+
}
|
|
34
|
+
onModuleInit() {
|
|
35
|
+
for (const wrapper of this.discovery.getProviders()) {
|
|
36
|
+
const instance = wrapper.instance;
|
|
37
|
+
const ctor = instance?.constructor;
|
|
38
|
+
if (instance && ctor && Reflect.getMetadata(AGENT_METADATA, ctor)) {
|
|
39
|
+
this.agents.set(ctor.name, {
|
|
40
|
+
name: ctor.name,
|
|
41
|
+
instance,
|
|
42
|
+
target: ctor
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Returns an agent instance by class name. */
|
|
48
|
+
get(name) {
|
|
49
|
+
return this.agents.get(name)?.instance;
|
|
50
|
+
}
|
|
51
|
+
/** Returns an agent instance by its class. */
|
|
52
|
+
getByClass(target) {
|
|
53
|
+
return this.agents.get(target.name)?.instance;
|
|
54
|
+
}
|
|
55
|
+
/** All discovered agents. */
|
|
56
|
+
all() {
|
|
57
|
+
return [
|
|
58
|
+
...this.agents.values()
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
AgentRegistry = _ts_decorate([
|
|
63
|
+
common.Injectable(),
|
|
64
|
+
_ts_metadata("design:type", Function),
|
|
65
|
+
_ts_metadata("design:paramtypes", [
|
|
66
|
+
typeof core.DiscoveryService === "undefined" ? Object : core.DiscoveryService
|
|
67
|
+
])
|
|
68
|
+
], AgentRegistry);
|
|
69
|
+
|
|
70
|
+
// src/websocket/stream-to-socket.ts
|
|
71
|
+
async function streamAgentToSocket(stream, socket, options = {}) {
|
|
72
|
+
const chunkEvent = options.chunkEvent ?? "agent:chunk";
|
|
73
|
+
const doneEvent = options.doneEvent ?? "agent:done";
|
|
74
|
+
const errorEvent = options.errorEvent ?? "agent:error";
|
|
75
|
+
try {
|
|
76
|
+
let text = "";
|
|
77
|
+
for await (const delta of stream.textStream) {
|
|
78
|
+
text += delta;
|
|
79
|
+
socket.emit(chunkEvent, {
|
|
80
|
+
delta
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
socket.emit(doneEvent, {
|
|
84
|
+
text
|
|
85
|
+
});
|
|
86
|
+
return text;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
socket.emit(errorEvent, {
|
|
89
|
+
message: error instanceof Error ? error.message : String(error)
|
|
90
|
+
});
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
__name(streamAgentToSocket, "streamAgentToSocket");
|
|
95
|
+
|
|
96
|
+
// src/websocket/agent.gateway.ts
|
|
97
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
98
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
99
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
100
|
+
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;
|
|
101
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
102
|
+
}
|
|
103
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
104
|
+
function _ts_metadata2(k, v) {
|
|
105
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
106
|
+
}
|
|
107
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
108
|
+
function _ts_param(paramIndex, decorator) {
|
|
109
|
+
return function(target, key) {
|
|
110
|
+
decorator(target, key, paramIndex);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
__name(_ts_param, "_ts_param");
|
|
114
|
+
exports.AgentGateway = class AgentGateway {
|
|
115
|
+
static {
|
|
116
|
+
__name(this, "AgentGateway");
|
|
117
|
+
}
|
|
118
|
+
agents;
|
|
119
|
+
constructor(agents) {
|
|
120
|
+
this.agents = agents;
|
|
121
|
+
}
|
|
122
|
+
async run(message, client) {
|
|
123
|
+
const agent = this.agents.get(message.agent);
|
|
124
|
+
if (!agent) {
|
|
125
|
+
client.emit("agent:error", {
|
|
126
|
+
message: `Unknown agent "${message.agent}".`
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
await streamAgentToSocket(agent.stream(message.input), client);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
_ts_decorate2([
|
|
134
|
+
websockets.SubscribeMessage("agent:run"),
|
|
135
|
+
_ts_param(0, websockets.MessageBody()),
|
|
136
|
+
_ts_param(1, websockets.ConnectedSocket()),
|
|
137
|
+
_ts_metadata2("design:type", Function),
|
|
138
|
+
_ts_metadata2("design:paramtypes", [
|
|
139
|
+
typeof AgentRunMessage === "undefined" ? Object : AgentRunMessage,
|
|
140
|
+
typeof SocketLike === "undefined" ? Object : SocketLike
|
|
141
|
+
]),
|
|
142
|
+
_ts_metadata2("design:returntype", Promise)
|
|
143
|
+
], exports.AgentGateway.prototype, "run", null);
|
|
144
|
+
exports.AgentGateway = _ts_decorate2([
|
|
145
|
+
websockets.WebSocketGateway(),
|
|
146
|
+
_ts_metadata2("design:type", Function),
|
|
147
|
+
_ts_metadata2("design:paramtypes", [
|
|
148
|
+
typeof AgentRegistry === "undefined" ? Object : AgentRegistry
|
|
149
|
+
])
|
|
150
|
+
], exports.AgentGateway);
|
|
151
|
+
|
|
152
|
+
exports.streamAgentToSocket = streamAgentToSocket;
|
|
153
|
+
//# sourceMappingURL=websocket.cjs.map
|
|
154
|
+
//# sourceMappingURL=websocket.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai.constants.ts","../src/agent/orchestration/agent-registry.ts","../src/websocket/stream-to-socket.ts","../src/websocket/agent.gateway.ts"],"names":["AGENT_METADATA","AgentRegistry","agents","Map","discovery","onModuleInit","wrapper","getProviders","instance","ctor","Reflect","getMetadata","set","name","target","get","getByClass","all","values","streamAgentToSocket","stream","socket","options","chunkEvent","doneEvent","errorEvent","text","delta","textStream","emit","error","message","Error","String","AgentGateway","run","client","agent","input"],"mappings":";;;;;;;;;;AAgBO,IAAMA,cAAAA,0BAAwB,iBAAA,CAAA;;;;;;;;;;;;;;ACA9B,IAAMC,gBAAN,MAAMA;AAAAA,EAAAA;;;;AACMC,EAAAA,MAAAA,uBAAaC,GAAAA,EAAAA;AAE9B,EAAA,WAAA,CAA6BC,SAAAA,EAA6B;SAA7BA,SAAAA,GAAAA,SAAAA;AAA8B,EAAA;EAE3DC,YAAAA,GAAqB;AACnB,IAAA,KAAA,MAAWC,OAAAA,IAAW,IAAA,CAAKF,SAAAA,CAAUG,YAAAA,EAAY,EAAI;AACnD,MAAA,MAAMC,WAAWF,OAAAA,CAAQE,QAAAA;AACzB,MAAA,MAAMC,OAAOD,QAAAA,EAAU,WAAA;AACvB,MAAA,IAAIA,YAAYC,IAAAA,IAAQC,OAAAA,CAAQC,WAAAA,CAAYX,cAAAA,EAAgBS,IAAAA,CAAAA,EAAO;AACjE,QAAA,IAAA,CAAKP,MAAAA,CAAOU,GAAAA,CAAIH,IAAAA,CAAKI,IAAAA,EAAM;AAAEA,UAAAA,IAAAA,EAAMJ,IAAAA,CAAKI,IAAAA;AAAML,UAAAA,QAAAA;UAAUM,MAAAA,EAAQL;SAAK,CAAA;AACvE,MAAA;AACF,IAAA;AACF,EAAA;;AAGAM,EAAAA,GAAAA,CAAIF,IAAAA,EAA+B;AACjC,IAAA,OAAO,IAAA,CAAKX,MAAAA,CAAOa,GAAAA,CAAIF,IAAAA,CAAAA,EAAOL,QAAAA;AAChC,EAAA;;AAGAQ,EAAAA,UAAAA,CAAWF,MAAAA,EAAoC;AAC7C,IAAA,OAAO,IAAA,CAAKZ,MAAAA,CAAOa,GAAAA,CAAID,MAAAA,CAAOD,IAAI,CAAA,EAAGL,QAAAA;AACvC,EAAA;;EAGAS,GAAAA,GAAoB;AAClB,IAAA,OAAO;AAAI,MAAA,GAAA,IAAA,CAAKf,OAAOgB,MAAAA;;AACzB,EAAA;AACF,CAAA;;;;;;;;;;ACxBA,eAAsBC,mBAAAA,CACpBC,MAAAA,EACAC,MAAAA,EACAC,OAAAA,GAAiC,EAAC,EAAC;AAEnC,EAAA,MAAMC,UAAAA,GAAaD,QAAQC,UAAAA,IAAc,aAAA;AACzC,EAAA,MAAMC,SAAAA,GAAYF,QAAQE,SAAAA,IAAa,YAAA;AACvC,EAAA,MAAMC,UAAAA,GAAaH,QAAQG,UAAAA,IAAc,aAAA;AAEzC,EAAA,IAAI;AACF,IAAA,IAAIC,IAAAA,GAAO,EAAA;AACX,IAAA,WAAA,MAAiBC,KAAAA,IAASP,OAAOQ,UAAAA,EAAY;AAC3CF,MAAAA,IAAAA,IAAQC,KAAAA;AACRN,MAAAA,MAAAA,CAAOQ,KAAKN,UAAAA,EAAY;AAAEI,QAAAA;OAAM,CAAA;AAClC,IAAA;AACAN,IAAAA,MAAAA,CAAOQ,KAAKL,SAAAA,EAAW;AAAEE,MAAAA;KAAK,CAAA;AAC9B,IAAA,OAAOA,IAAAA;AACT,EAAA,CAAA,CAAA,OAASI,KAAAA,EAAO;AACdT,IAAAA,MAAAA,CAAOQ,KAAKJ,UAAAA,EAAY;AACtBM,MAAAA,OAAAA,EAASD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMC,OAAAA,GAAUE,OAAOH,KAAAA;KAC3D,CAAA;AACA,IAAA,MAAMA,KAAAA;AACR,EAAA;AACF;AAvBsBX,MAAAA,CAAAA,mBAAAA,EAAAA,qBAAAA,CAAAA;;;;;;;;;;;;;;;;;;;;ACMTe,uBAAN,kBAAA,CAAMA;AAAAA,EAAAA;;;;AACX,EAAA,WAAA,CAA6BhC,MAAAA,EAAuB;SAAvBA,MAAAA,GAAAA,MAAAA;AAAwB,EAAA;EAErD,MACMiC,GAAAA,CACWJ,SACIK,MAAAA,EACJ;AACf,IAAA,MAAMC,KAAAA,GAAQ,IAAA,CAAKnC,MAAAA,CAAOa,GAAAA,CAAIgB,QAAQM,KAAK,CAAA;AAC3C,IAAA,IAAI,CAACA,KAAAA,EAAO;AACVD,MAAAA,MAAAA,CAAOP,KAAK,aAAA,EAAe;QACzBE,OAAAA,EAAS,CAAA,eAAA,EAAkBA,QAAQM,KAAK,CAAA,EAAA;OAC1C,CAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA,MAAMlB,oBAAoBkB,KAAAA,CAAMjB,MAAAA,CAAOW,OAAAA,CAAQO,KAAK,GAAGF,MAAAA,CAAAA;AACzD,EAAA;AACF","file":"websocket.cjs","sourcesContent":["/**\n * Dependency-injection tokens used across the module.\n */\nexport const AI_MODULE_OPTIONS = Symbol('AI_MODULE_OPTIONS');\nexport const CONVERSATION_STORE = Symbol('CONVERSATION_STORE');\nexport const VECTOR_STORE = Symbol('VECTOR_STORE');\nexport const AI_CACHE = Symbol('AI_CACHE');\nexport const APPROVAL_GATE = Symbol('APPROVAL_GATE');\nexport const AGENT_QUEUE = Symbol('AGENT_QUEUE');\nexport const RATE_LIMITER = Symbol('RATE_LIMITER');\nexport const RERANKER = Symbol('RERANKER');\n\n/**\n * Reflect metadata keys for the `@Tool` and `@Agent` decorators.\n */\nexport const TOOL_METADATA = Symbol('nestjs-ai:tool');\nexport const AGENT_METADATA = Symbol('nestjs-ai:agent');\nexport const GUARDRAIL_METADATA = Symbol('nestjs-ai:guardrail');\n\n/**\n * Default number of tool-calling steps allowed before an agent run stops.\n */\nexport const DEFAULT_MAX_STEPS = 5;\n","import { Injectable, type OnModuleInit, type Type } from '@nestjs/common';\nimport { DiscoveryService } from '@nestjs/core';\nimport { AGENT_METADATA } from '../../ai.constants.js';\n\n/** A discovered agent instance and its class. */\nexport interface AgentEntry {\n name: string;\n instance: any;\n target: Type<any>;\n}\n\n/**\n * Indexes every `@Agent`-decorated provider by class name, enabling\n * multi-agent orchestration (supervisor/handoff) and background-job resolution.\n */\n@Injectable()\nexport class AgentRegistry implements OnModuleInit {\n private readonly agents = new Map<string, AgentEntry>();\n\n constructor(private readonly discovery: DiscoveryService) {}\n\n onModuleInit(): void {\n for (const wrapper of this.discovery.getProviders()) {\n const instance = wrapper.instance;\n const ctor = instance?.constructor as Type<any> | undefined;\n if (instance && ctor && Reflect.getMetadata(AGENT_METADATA, ctor)) {\n this.agents.set(ctor.name, { name: ctor.name, instance, target: ctor });\n }\n }\n }\n\n /** Returns an agent instance by class name. */\n get(name: string): any | undefined {\n return this.agents.get(name)?.instance;\n }\n\n /** Returns an agent instance by its class. */\n getByClass(target: Type<any>): any | undefined {\n return this.agents.get(target.name)?.instance;\n }\n\n /** All discovered agents. */\n all(): AgentEntry[] {\n return [...this.agents.values()];\n }\n}\n","/** Minimal socket shape (compatible with socket.io's `Socket`). */\nexport interface SocketLike {\n emit(event: string, payload?: unknown): void;\n}\n\n/** A stream result exposing a text stream (from agent `.stream()`). */\nexport interface TextStreamLike {\n textStream: AsyncIterable<string>;\n}\n\nexport interface StreamToSocketOptions {\n chunkEvent?: string;\n doneEvent?: string;\n errorEvent?: string;\n}\n\n/**\n * Forwards an agent text stream to a socket: emits `chunkEvent` per delta, then\n * `doneEvent` with the full text; emits `errorEvent` and rethrows on failure.\n * Framework-agnostic — the WebSocket gateway is thin glue over this.\n */\nexport async function streamAgentToSocket(\n stream: TextStreamLike,\n socket: SocketLike,\n options: StreamToSocketOptions = {},\n): Promise<string> {\n const chunkEvent = options.chunkEvent ?? 'agent:chunk';\n const doneEvent = options.doneEvent ?? 'agent:done';\n const errorEvent = options.errorEvent ?? 'agent:error';\n\n try {\n let text = '';\n for await (const delta of stream.textStream) {\n text += delta;\n socket.emit(chunkEvent, { delta });\n }\n socket.emit(doneEvent, { text });\n return text;\n } catch (error) {\n socket.emit(errorEvent, {\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n","import {\n ConnectedSocket,\n MessageBody,\n SubscribeMessage,\n WebSocketGateway,\n} from '@nestjs/websockets';\nimport { AgentRegistry } from '../agent/orchestration/agent-registry.js';\nimport {\n streamAgentToSocket,\n type SocketLike,\n} from './stream-to-socket.js';\n\n/** Payload for the `agent:run` message. */\nexport interface AgentRunMessage {\n agent: string;\n input: string;\n}\n\n/**\n * WebSocket gateway that streams agent responses over socket.io. Client emits\n * `agent:run { agent, input }`; the server streams `agent:chunk { delta }` then\n * `agent:done { text }`. Requires the optional peers `@nestjs/websockets`,\n * `@nestjs/platform-socket.io`, and `socket.io`.\n *\n * Published under `@mgvdev/nestjs-ai/websocket`.\n */\n@WebSocketGateway()\nexport class AgentGateway {\n constructor(private readonly agents: AgentRegistry) {}\n\n @SubscribeMessage('agent:run')\n async run(\n @MessageBody() message: AgentRunMessage,\n @ConnectedSocket() client: SocketLike,\n ): Promise<void> {\n const agent = this.agents.get(message.agent);\n if (!agent) {\n client.emit('agent:error', {\n message: `Unknown agent \"${message.agent}\".`,\n });\n return;\n }\n await streamAgentToSocket(agent.stream(message.input), client);\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { A as AgentRegistry, S as SocketLike } from './stream-to-socket-fJphU0WN.cjs';
|
|
2
|
+
export { b as StreamToSocketOptions, T as TextStreamLike, s as streamAgentToSocket } from './stream-to-socket-fJphU0WN.cjs';
|
|
3
|
+
import '@nestjs/common';
|
|
4
|
+
import '@nestjs/core';
|
|
5
|
+
|
|
6
|
+
/** Payload for the `agent:run` message. */
|
|
7
|
+
interface AgentRunMessage {
|
|
8
|
+
agent: string;
|
|
9
|
+
input: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* WebSocket gateway that streams agent responses over socket.io. Client emits
|
|
13
|
+
* `agent:run { agent, input }`; the server streams `agent:chunk { delta }` then
|
|
14
|
+
* `agent:done { text }`. Requires the optional peers `@nestjs/websockets`,
|
|
15
|
+
* `@nestjs/platform-socket.io`, and `socket.io`.
|
|
16
|
+
*
|
|
17
|
+
* Published under `@mgvdev/nestjs-ai/websocket`.
|
|
18
|
+
*/
|
|
19
|
+
declare class AgentGateway {
|
|
20
|
+
private readonly agents;
|
|
21
|
+
constructor(agents: AgentRegistry);
|
|
22
|
+
run(message: AgentRunMessage, client: SocketLike): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { AgentGateway, type AgentRunMessage, SocketLike };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { A as AgentRegistry, S as SocketLike } from './stream-to-socket-fJphU0WN.js';
|
|
2
|
+
export { b as StreamToSocketOptions, T as TextStreamLike, s as streamAgentToSocket } from './stream-to-socket-fJphU0WN.js';
|
|
3
|
+
import '@nestjs/common';
|
|
4
|
+
import '@nestjs/core';
|
|
5
|
+
|
|
6
|
+
/** Payload for the `agent:run` message. */
|
|
7
|
+
interface AgentRunMessage {
|
|
8
|
+
agent: string;
|
|
9
|
+
input: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* WebSocket gateway that streams agent responses over socket.io. Client emits
|
|
13
|
+
* `agent:run { agent, input }`; the server streams `agent:chunk { delta }` then
|
|
14
|
+
* `agent:done { text }`. Requires the optional peers `@nestjs/websockets`,
|
|
15
|
+
* `@nestjs/platform-socket.io`, and `socket.io`.
|
|
16
|
+
*
|
|
17
|
+
* Published under `@mgvdev/nestjs-ai/websocket`.
|
|
18
|
+
*/
|
|
19
|
+
declare class AgentGateway {
|
|
20
|
+
private readonly agents;
|
|
21
|
+
constructor(agents: AgentRegistry);
|
|
22
|
+
run(message: AgentRunMessage, client: SocketLike): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { AgentGateway, type AgentRunMessage, SocketLike };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { SubscribeMessage, MessageBody, ConnectedSocket, WebSocketGateway } from '@nestjs/websockets';
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { DiscoveryService } from '@nestjs/core';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
|
|
8
|
+
// src/ai.constants.ts
|
|
9
|
+
var AGENT_METADATA = /* @__PURE__ */ Symbol("nestjs-ai:agent");
|
|
10
|
+
|
|
11
|
+
// src/agent/orchestration/agent-registry.ts
|
|
12
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
13
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
14
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
15
|
+
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;
|
|
16
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
17
|
+
}
|
|
18
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
19
|
+
function _ts_metadata(k, v) {
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
21
|
+
}
|
|
22
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
23
|
+
var AgentRegistry = class {
|
|
24
|
+
static {
|
|
25
|
+
__name(this, "AgentRegistry");
|
|
26
|
+
}
|
|
27
|
+
discovery;
|
|
28
|
+
agents = /* @__PURE__ */ new Map();
|
|
29
|
+
constructor(discovery) {
|
|
30
|
+
this.discovery = discovery;
|
|
31
|
+
}
|
|
32
|
+
onModuleInit() {
|
|
33
|
+
for (const wrapper of this.discovery.getProviders()) {
|
|
34
|
+
const instance = wrapper.instance;
|
|
35
|
+
const ctor = instance?.constructor;
|
|
36
|
+
if (instance && ctor && Reflect.getMetadata(AGENT_METADATA, ctor)) {
|
|
37
|
+
this.agents.set(ctor.name, {
|
|
38
|
+
name: ctor.name,
|
|
39
|
+
instance,
|
|
40
|
+
target: ctor
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Returns an agent instance by class name. */
|
|
46
|
+
get(name) {
|
|
47
|
+
return this.agents.get(name)?.instance;
|
|
48
|
+
}
|
|
49
|
+
/** Returns an agent instance by its class. */
|
|
50
|
+
getByClass(target) {
|
|
51
|
+
return this.agents.get(target.name)?.instance;
|
|
52
|
+
}
|
|
53
|
+
/** All discovered agents. */
|
|
54
|
+
all() {
|
|
55
|
+
return [
|
|
56
|
+
...this.agents.values()
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
AgentRegistry = _ts_decorate([
|
|
61
|
+
Injectable(),
|
|
62
|
+
_ts_metadata("design:type", Function),
|
|
63
|
+
_ts_metadata("design:paramtypes", [
|
|
64
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService
|
|
65
|
+
])
|
|
66
|
+
], AgentRegistry);
|
|
67
|
+
|
|
68
|
+
// src/websocket/stream-to-socket.ts
|
|
69
|
+
async function streamAgentToSocket(stream, socket, options = {}) {
|
|
70
|
+
const chunkEvent = options.chunkEvent ?? "agent:chunk";
|
|
71
|
+
const doneEvent = options.doneEvent ?? "agent:done";
|
|
72
|
+
const errorEvent = options.errorEvent ?? "agent:error";
|
|
73
|
+
try {
|
|
74
|
+
let text = "";
|
|
75
|
+
for await (const delta of stream.textStream) {
|
|
76
|
+
text += delta;
|
|
77
|
+
socket.emit(chunkEvent, {
|
|
78
|
+
delta
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
socket.emit(doneEvent, {
|
|
82
|
+
text
|
|
83
|
+
});
|
|
84
|
+
return text;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
socket.emit(errorEvent, {
|
|
87
|
+
message: error instanceof Error ? error.message : String(error)
|
|
88
|
+
});
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
__name(streamAgentToSocket, "streamAgentToSocket");
|
|
93
|
+
|
|
94
|
+
// src/websocket/agent.gateway.ts
|
|
95
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
96
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
97
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
98
|
+
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;
|
|
99
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
100
|
+
}
|
|
101
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
102
|
+
function _ts_metadata2(k, v) {
|
|
103
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
104
|
+
}
|
|
105
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
106
|
+
function _ts_param(paramIndex, decorator) {
|
|
107
|
+
return function(target, key) {
|
|
108
|
+
decorator(target, key, paramIndex);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
__name(_ts_param, "_ts_param");
|
|
112
|
+
var AgentGateway = class {
|
|
113
|
+
static {
|
|
114
|
+
__name(this, "AgentGateway");
|
|
115
|
+
}
|
|
116
|
+
agents;
|
|
117
|
+
constructor(agents) {
|
|
118
|
+
this.agents = agents;
|
|
119
|
+
}
|
|
120
|
+
async run(message, client) {
|
|
121
|
+
const agent = this.agents.get(message.agent);
|
|
122
|
+
if (!agent) {
|
|
123
|
+
client.emit("agent:error", {
|
|
124
|
+
message: `Unknown agent "${message.agent}".`
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await streamAgentToSocket(agent.stream(message.input), client);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
_ts_decorate2([
|
|
132
|
+
SubscribeMessage("agent:run"),
|
|
133
|
+
_ts_param(0, MessageBody()),
|
|
134
|
+
_ts_param(1, ConnectedSocket()),
|
|
135
|
+
_ts_metadata2("design:type", Function),
|
|
136
|
+
_ts_metadata2("design:paramtypes", [
|
|
137
|
+
typeof AgentRunMessage === "undefined" ? Object : AgentRunMessage,
|
|
138
|
+
typeof SocketLike === "undefined" ? Object : SocketLike
|
|
139
|
+
]),
|
|
140
|
+
_ts_metadata2("design:returntype", Promise)
|
|
141
|
+
], AgentGateway.prototype, "run", null);
|
|
142
|
+
AgentGateway = _ts_decorate2([
|
|
143
|
+
WebSocketGateway(),
|
|
144
|
+
_ts_metadata2("design:type", Function),
|
|
145
|
+
_ts_metadata2("design:paramtypes", [
|
|
146
|
+
typeof AgentRegistry === "undefined" ? Object : AgentRegistry
|
|
147
|
+
])
|
|
148
|
+
], AgentGateway);
|
|
149
|
+
|
|
150
|
+
export { AgentGateway, streamAgentToSocket };
|
|
151
|
+
//# sourceMappingURL=websocket.js.map
|
|
152
|
+
//# sourceMappingURL=websocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai.constants.ts","../src/agent/orchestration/agent-registry.ts","../src/websocket/stream-to-socket.ts","../src/websocket/agent.gateway.ts"],"names":["AGENT_METADATA","AgentRegistry","agents","Map","discovery","onModuleInit","wrapper","getProviders","instance","ctor","Reflect","getMetadata","set","name","target","get","getByClass","all","values","streamAgentToSocket","stream","socket","options","chunkEvent","doneEvent","errorEvent","text","delta","textStream","emit","error","message","Error","String","AgentGateway","run","client","agent","input"],"mappings":"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","file":"websocket.js","sourcesContent":["/**\n * Dependency-injection tokens used across the module.\n */\nexport const AI_MODULE_OPTIONS = Symbol('AI_MODULE_OPTIONS');\nexport const CONVERSATION_STORE = Symbol('CONVERSATION_STORE');\nexport const VECTOR_STORE = Symbol('VECTOR_STORE');\nexport const AI_CACHE = Symbol('AI_CACHE');\nexport const APPROVAL_GATE = Symbol('APPROVAL_GATE');\nexport const AGENT_QUEUE = Symbol('AGENT_QUEUE');\nexport const RATE_LIMITER = Symbol('RATE_LIMITER');\nexport const RERANKER = Symbol('RERANKER');\n\n/**\n * Reflect metadata keys for the `@Tool` and `@Agent` decorators.\n */\nexport const TOOL_METADATA = Symbol('nestjs-ai:tool');\nexport const AGENT_METADATA = Symbol('nestjs-ai:agent');\nexport const GUARDRAIL_METADATA = Symbol('nestjs-ai:guardrail');\n\n/**\n * Default number of tool-calling steps allowed before an agent run stops.\n */\nexport const DEFAULT_MAX_STEPS = 5;\n","import { Injectable, type OnModuleInit, type Type } from '@nestjs/common';\nimport { DiscoveryService } from '@nestjs/core';\nimport { AGENT_METADATA } from '../../ai.constants.js';\n\n/** A discovered agent instance and its class. */\nexport interface AgentEntry {\n name: string;\n instance: any;\n target: Type<any>;\n}\n\n/**\n * Indexes every `@Agent`-decorated provider by class name, enabling\n * multi-agent orchestration (supervisor/handoff) and background-job resolution.\n */\n@Injectable()\nexport class AgentRegistry implements OnModuleInit {\n private readonly agents = new Map<string, AgentEntry>();\n\n constructor(private readonly discovery: DiscoveryService) {}\n\n onModuleInit(): void {\n for (const wrapper of this.discovery.getProviders()) {\n const instance = wrapper.instance;\n const ctor = instance?.constructor as Type<any> | undefined;\n if (instance && ctor && Reflect.getMetadata(AGENT_METADATA, ctor)) {\n this.agents.set(ctor.name, { name: ctor.name, instance, target: ctor });\n }\n }\n }\n\n /** Returns an agent instance by class name. */\n get(name: string): any | undefined {\n return this.agents.get(name)?.instance;\n }\n\n /** Returns an agent instance by its class. */\n getByClass(target: Type<any>): any | undefined {\n return this.agents.get(target.name)?.instance;\n }\n\n /** All discovered agents. */\n all(): AgentEntry[] {\n return [...this.agents.values()];\n }\n}\n","/** Minimal socket shape (compatible with socket.io's `Socket`). */\nexport interface SocketLike {\n emit(event: string, payload?: unknown): void;\n}\n\n/** A stream result exposing a text stream (from agent `.stream()`). */\nexport interface TextStreamLike {\n textStream: AsyncIterable<string>;\n}\n\nexport interface StreamToSocketOptions {\n chunkEvent?: string;\n doneEvent?: string;\n errorEvent?: string;\n}\n\n/**\n * Forwards an agent text stream to a socket: emits `chunkEvent` per delta, then\n * `doneEvent` with the full text; emits `errorEvent` and rethrows on failure.\n * Framework-agnostic — the WebSocket gateway is thin glue over this.\n */\nexport async function streamAgentToSocket(\n stream: TextStreamLike,\n socket: SocketLike,\n options: StreamToSocketOptions = {},\n): Promise<string> {\n const chunkEvent = options.chunkEvent ?? 'agent:chunk';\n const doneEvent = options.doneEvent ?? 'agent:done';\n const errorEvent = options.errorEvent ?? 'agent:error';\n\n try {\n let text = '';\n for await (const delta of stream.textStream) {\n text += delta;\n socket.emit(chunkEvent, { delta });\n }\n socket.emit(doneEvent, { text });\n return text;\n } catch (error) {\n socket.emit(errorEvent, {\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n","import {\n ConnectedSocket,\n MessageBody,\n SubscribeMessage,\n WebSocketGateway,\n} from '@nestjs/websockets';\nimport { AgentRegistry } from '../agent/orchestration/agent-registry.js';\nimport {\n streamAgentToSocket,\n type SocketLike,\n} from './stream-to-socket.js';\n\n/** Payload for the `agent:run` message. */\nexport interface AgentRunMessage {\n agent: string;\n input: string;\n}\n\n/**\n * WebSocket gateway that streams agent responses over socket.io. Client emits\n * `agent:run { agent, input }`; the server streams `agent:chunk { delta }` then\n * `agent:done { text }`. Requires the optional peers `@nestjs/websockets`,\n * `@nestjs/platform-socket.io`, and `socket.io`.\n *\n * Published under `@mgvdev/nestjs-ai/websocket`.\n */\n@WebSocketGateway()\nexport class AgentGateway {\n constructor(private readonly agents: AgentRegistry) {}\n\n @SubscribeMessage('agent:run')\n async run(\n @MessageBody() message: AgentRunMessage,\n @ConnectedSocket() client: SocketLike,\n ): Promise<void> {\n const agent = this.agents.get(message.agent);\n if (!agent) {\n client.emit('agent:error', {\n message: `Unknown agent \"${message.agent}\".`,\n });\n return;\n }\n await streamAgentToSocket(agent.stream(message.input), client);\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @mgvdev/nestjs-ai — Documentation
|
|
2
|
+
|
|
3
|
+
A NestJS toolkit over the [Vercel AI SDK](https://ai-sdk.dev) (v7): DI-native
|
|
4
|
+
agents, tools, structured output, streaming, embeddings, RAG, multimodal,
|
|
5
|
+
prompts, guardrails, orchestration, MCP, and production features (cost, rate
|
|
6
|
+
limiting, memory, evals).
|
|
7
|
+
|
|
8
|
+
## Table of contents
|
|
9
|
+
|
|
10
|
+
1. [Getting started](./getting-started.md)
|
|
11
|
+
2. [Configuration](./configuration.md)
|
|
12
|
+
3. [Agents & tools](./agents-and-tools.md)
|
|
13
|
+
4. [Structured output & streaming](./structured-output-and-streaming.md)
|
|
14
|
+
5. [Embeddings & RAG](./embeddings-and-rag.md)
|
|
15
|
+
6. [Multimodal (image / speech / transcription)](./multimodal.md)
|
|
16
|
+
7. [Prompt registry](./prompts.md)
|
|
17
|
+
8. [Memory (conversation & semantic)](./memory.md)
|
|
18
|
+
9. [Guardrails, events & telemetry](./guardrails-events-telemetry.md)
|
|
19
|
+
10. [Content safety (PII & moderation)](./content-safety.md)
|
|
20
|
+
11. [Orchestration & MCP](./orchestration-and-mcp.md)
|
|
21
|
+
12. [Reliability (fallback, retry, cache, rate limit, budgets)](./reliability.md)
|
|
22
|
+
13. [Background jobs & realtime](./jobs-and-realtime.md)
|
|
23
|
+
14. [Evals & testing](./evals-and-testing.md)
|
|
24
|
+
15. [API reference](./api-reference.md)
|
|
25
|
+
|
|
26
|
+
## Entry points
|
|
27
|
+
|
|
28
|
+
| Import | Contents |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `@mgvdev/nestjs-ai` | Everything except the adapters below |
|
|
31
|
+
| `@mgvdev/nestjs-ai/typeorm` | `TypeOrmConversationStore`, `ConversationMessageEntity` |
|
|
32
|
+
| `@mgvdev/nestjs-ai/websocket` | `AgentGateway` |
|
|
33
|
+
| `@mgvdev/nestjs-ai/testing` | `createTestingAiModule`, `createMockModel` |
|
|
34
|
+
|
|
35
|
+
## Peer dependencies
|
|
36
|
+
|
|
37
|
+
**Required:** `@nestjs/common`, `@nestjs/core`, `ai`, `zod`, `reflect-metadata`, `rxjs`.
|
|
38
|
+
|
|
39
|
+
**Optional** (install per feature): `@ai-sdk/openai` · `@ai-sdk/anthropic` ·
|
|
40
|
+
`@ai-sdk/google` · `@ai-sdk/cohere` (reranking) · `@nestjs/event-emitter`
|
|
41
|
+
(events) · `@nestjs/typeorm` + `typeorm` (TypeORM store) · `bullmq` (jobs) ·
|
|
42
|
+
`pg` (pgvector) · `@qdrant/js-client-rest` · `@pinecone-database/pinecone` ·
|
|
43
|
+
`@nestjs/websockets` + `@nestjs/platform-socket.io` + `socket.io` (realtime) ·
|
|
44
|
+
`@modelcontextprotocol/sdk` (MCP) · `msw` (testing).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Agents & tools
|
|
2
|
+
|
|
3
|
+
## Tools
|
|
4
|
+
|
|
5
|
+
A tool is a method on an injectable provider, decorated with `@Tool`. It keeps
|
|
6
|
+
full access to injected dependencies.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class WeatherTools {
|
|
11
|
+
constructor(private readonly api: WeatherApi) {}
|
|
12
|
+
|
|
13
|
+
@Tool({
|
|
14
|
+
name: 'get_weather', // optional; defaults to the method name
|
|
15
|
+
description: 'Get the weather for a city',
|
|
16
|
+
schema: z.object({ city: z.string() }),
|
|
17
|
+
requiresApproval: false, // gate behind an ApprovalGate when true
|
|
18
|
+
})
|
|
19
|
+
getWeather({ city }: { city: string }) {
|
|
20
|
+
return this.api.lookup(city); // return value is fed back to the model
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **`schema`** is a Zod schema for the arguments; the method receives the parsed object.
|
|
26
|
+
- The return value (any JSON-serializable value) is returned to the model.
|
|
27
|
+
- Tools are discovered automatically once the class is a registered provider.
|
|
28
|
+
|
|
29
|
+
### `ToolRegistry`
|
|
30
|
+
|
|
31
|
+
Introspect discovered tools:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
constructor(private readonly tools: ToolRegistry) {}
|
|
35
|
+
this.tools.getAll(); // ToolEntry[]
|
|
36
|
+
this.tools.getByName('get_weather');
|
|
37
|
+
this.tools.buildToolSet([WeatherTools, 'get_weather']); // for AiService
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Agents
|
|
41
|
+
|
|
42
|
+
Extend `AiAgent` and annotate with `@Agent`.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
@Agent({
|
|
46
|
+
model: 'openai:gpt-4o', // string | string[] (fallback)
|
|
47
|
+
system: 'You are a helpful assistant.',
|
|
48
|
+
tools: [WeatherTools], // @Tool providers and/or @Agent sub-agents
|
|
49
|
+
maxSteps: 5, // tool-calling loop bound
|
|
50
|
+
output: z.object({ answer: z.string() }), // optional structured output
|
|
51
|
+
})
|
|
52
|
+
export class SupportAgent extends AiAgent {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Running
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const { text, usage, steps, toolCalls } = await agent.run('Weather in Paris?');
|
|
59
|
+
const { object } = await agent.run<Shape>(input); // when `output`/`schema` set
|
|
60
|
+
const stream = agent.stream('Hi'); // Vercel stream result
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `AgentRunOptions`
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
await agent.run(input, {
|
|
67
|
+
model, // override model / fallback chain
|
|
68
|
+
system, // override system prompt
|
|
69
|
+
systemPrompt: { name, vars, version }, // resolve from PromptRegistry
|
|
70
|
+
conversationId, // load + persist history
|
|
71
|
+
maxSteps,
|
|
72
|
+
schema, // structured output for this call
|
|
73
|
+
temperature,
|
|
74
|
+
maxRetries,
|
|
75
|
+
recall: { query?, topK? }, // prepend semantic-memory context
|
|
76
|
+
abortSignal,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`run()` returns an `AgentResult`: `{ text, object?, steps?, toolCalls?, usage?, finishReason?, messages }`.
|
|
81
|
+
|
|
82
|
+
## The raw facade — `AiService`
|
|
83
|
+
|
|
84
|
+
When you don't want an agent class:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const { text } = await this.ai.generateText({
|
|
88
|
+
model: 'openai:gpt-4o',
|
|
89
|
+
tools: [WeatherTools], // tool provider classes and/or names
|
|
90
|
+
prompt: 'Weather in Paris?',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`AiService` mirrors `generateText` / `streamText` / `generateObject` /
|
|
95
|
+
`streamObject`, resolving `model` strings and `tools` references for you.
|
|
96
|
+
|
|
97
|
+
## How the tool loop works
|
|
98
|
+
|
|
99
|
+
`run()` calls the model with the resolved tools and `stopWhen: stepCountIs(maxSteps)`.
|
|
100
|
+
When the model emits a tool call, the SDK executes the matching `@Tool` method on
|
|
101
|
+
its DI instance, feeds the result back, and continues until the model produces a
|
|
102
|
+
final answer or `maxSteps` is reached.
|