outlet-orm 7.0.0 → 9.0.1

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 (46) hide show
  1. package/README.md +130 -2
  2. package/docs/skills/outlet-orm/AI.md +452 -102
  3. package/docs/skills/outlet-orm/API.md +108 -0
  4. package/docs/skills/outlet-orm/QUERIES.md +64 -0
  5. package/docs/skills/outlet-orm/SEEDS.md +47 -0
  6. package/docs/skills/outlet-orm/SKILL.md +15 -7
  7. package/package.json +1 -1
  8. package/src/AI/AIPromptEnhancer.js +170 -0
  9. package/src/AI/AIQueryBuilder.js +234 -0
  10. package/src/AI/AIQueryOptimizer.js +185 -0
  11. package/src/AI/AISeeder.js +181 -0
  12. package/src/AI/AiBridgeManager.js +287 -0
  13. package/src/AI/Builders/TextBuilder.js +170 -0
  14. package/src/AI/Contracts/AudioProviderContract.js +29 -0
  15. package/src/AI/Contracts/ChatProviderContract.js +38 -0
  16. package/src/AI/Contracts/EmbeddingsProviderContract.js +19 -0
  17. package/src/AI/Contracts/ImageProviderContract.js +19 -0
  18. package/src/AI/Contracts/ModelsProviderContract.js +26 -0
  19. package/src/AI/Contracts/ToolContract.js +25 -0
  20. package/src/AI/Facades/AiBridge.js +79 -0
  21. package/src/AI/MCPServer.js +113 -0
  22. package/src/AI/Providers/ClaudeProvider.js +64 -0
  23. package/src/AI/Providers/CustomOpenAIProvider.js +238 -0
  24. package/src/AI/Providers/GeminiProvider.js +68 -0
  25. package/src/AI/Providers/GrokProvider.js +46 -0
  26. package/src/AI/Providers/MistralProvider.js +21 -0
  27. package/src/AI/Providers/OllamaProvider.js +249 -0
  28. package/src/AI/Providers/OllamaTurboProvider.js +32 -0
  29. package/src/AI/Providers/OnnProvider.js +46 -0
  30. package/src/AI/Providers/OpenAIProvider.js +471 -0
  31. package/src/AI/Support/AudioNormalizer.js +37 -0
  32. package/src/AI/Support/ChatNormalizer.js +42 -0
  33. package/src/AI/Support/Document.js +77 -0
  34. package/src/AI/Support/DocumentAttachmentMapper.js +101 -0
  35. package/src/AI/Support/EmbeddingsNormalizer.js +30 -0
  36. package/src/AI/Support/Exceptions/ProviderError.js +22 -0
  37. package/src/AI/Support/FileSecurity.js +56 -0
  38. package/src/AI/Support/ImageNormalizer.js +62 -0
  39. package/src/AI/Support/JsonSchemaValidator.js +73 -0
  40. package/src/AI/Support/Message.js +40 -0
  41. package/src/AI/Support/StreamChunk.js +45 -0
  42. package/src/AI/Support/ToolChatRunner.js +160 -0
  43. package/src/AI/Support/ToolRegistry.js +62 -0
  44. package/src/AI/Tools/SystemInfoTool.js +25 -0
  45. package/src/index.js +67 -1
  46. package/types/index.d.ts +326 -0
@@ -0,0 +1,287 @@
1
+ 'use strict';
2
+
3
+ const OpenAIProvider = require('./Providers/OpenAIProvider');
4
+ const OllamaProvider = require('./Providers/OllamaProvider');
5
+ const OllamaTurboProvider = require('./Providers/OllamaTurboProvider');
6
+ const OnnProvider = require('./Providers/OnnProvider');
7
+ const GeminiProvider = require('./Providers/GeminiProvider');
8
+ const GrokProvider = require('./Providers/GrokProvider');
9
+ const ClaudeProvider = require('./Providers/ClaudeProvider');
10
+ const MistralProvider = require('./Providers/MistralProvider');
11
+ const CustomOpenAIProvider = require('./Providers/CustomOpenAIProvider');
12
+ const ProviderError = require('./Support/Exceptions/ProviderError');
13
+ const ToolRegistry = require('./Support/ToolRegistry');
14
+ const ToolChatRunner = require('./Support/ToolChatRunner');
15
+
16
+ const BEARER_PREFIX = 'Bearer ';
17
+
18
+ /**
19
+ * AiBridgeManager
20
+ * Central orchestrator for all AI providers. Supports provider registration,
21
+ * per-call overrides, capability delegation (chat, stream, embeddings, images,
22
+ * audio, models), tool registration, and chatWithTools loop.
23
+ */
24
+ class AiBridgeManager {
25
+ /**
26
+ * @param {Object} config
27
+ */
28
+ constructor(config = {}) {
29
+ this._providers = {};
30
+ this._toolRegistry = new ToolRegistry();
31
+ this._options = config.options || {};
32
+
33
+ // Auto-register providers from config
34
+ if (config.openai?.api_key) {
35
+ this._providers.openai = new OpenAIProvider(config.openai.api_key);
36
+ }
37
+ if (config.ollama?.endpoint) {
38
+ this._providers.ollama = new OllamaProvider(config.ollama.endpoint);
39
+ }
40
+ if (config.ollama_turbo?.api_key) {
41
+ const ep = config.ollama_turbo.endpoint || 'https://ollama.com';
42
+ this._providers.ollama_turbo = new OllamaTurboProvider(config.ollama_turbo.api_key, ep);
43
+ }
44
+ if (config.onn?.api_key) {
45
+ this._providers.onn = new OnnProvider(config.onn.api_key);
46
+ }
47
+ if (config.gemini?.api_key) {
48
+ this._providers.gemini = new GeminiProvider(config.gemini.api_key);
49
+ }
50
+ if (config.grok?.api_key) {
51
+ this._providers.grok = new GrokProvider(config.grok.api_key);
52
+ }
53
+ if (config.claude?.api_key) {
54
+ this._providers.claude = new ClaudeProvider(config.claude.api_key);
55
+ }
56
+ if (config.mistral?.api_key) {
57
+ const ep = config.mistral.endpoint || 'https://api.mistral.ai/v1/chat/completions';
58
+ this._providers.mistral = new MistralProvider(config.mistral.api_key, ep);
59
+ }
60
+ if (config.openai_custom?.api_key && config.openai_custom?.base_url) {
61
+ const c = config.openai_custom;
62
+ this._providers.openai_custom = new CustomOpenAIProvider(
63
+ c.api_key, c.base_url, c.paths || {},
64
+ c.auth_header || 'Authorization', c.auth_prefix || BEARER_PREFIX,
65
+ c.extra_headers || {}
66
+ );
67
+ }
68
+ // OpenRouter (OpenAI-compatible)
69
+ if (config.openrouter?.api_key) {
70
+ const base = config.openrouter.base_url || 'https://openrouter.ai/api/v1';
71
+ const hdrs = {};
72
+ if (config.openrouter.referer) hdrs['HTTP-Referer'] = config.openrouter.referer;
73
+ if (config.openrouter.title) hdrs['X-Title'] = config.openrouter.title;
74
+ this._providers.openrouter = new CustomOpenAIProvider(
75
+ config.openrouter.api_key, base,
76
+ { chat: '/chat/completions', embeddings: '/embeddings', image: '/images/generations', tts: '/audio/speech', stt: '/audio/transcriptions' },
77
+ 'Authorization', BEARER_PREFIX, hdrs
78
+ );
79
+ }
80
+ }
81
+
82
+ // ─── Provider resolution ───
83
+
84
+ /** @private */
85
+ _hasOverrides(options) {
86
+ const keys = ['api_key', 'endpoint', 'base_url', 'chat_endpoint', 'auth_header', 'auth_prefix', 'paths', 'extra_headers'];
87
+ return keys.some(k => options[k] !== undefined);
88
+ }
89
+
90
+ /** @private */
91
+ _buildProviderFromOptions(name, options) {
92
+ switch (name) {
93
+ case 'openai': {
94
+ const api = options.api_key;
95
+ if (api) return new OpenAIProvider(api, options.chat_endpoint || 'https://api.openai.com/v1/chat/completions');
96
+ break;
97
+ }
98
+ case 'ollama': return new OllamaProvider(options.endpoint || 'http://localhost:11434');
99
+ case 'ollama_turbo': {
100
+ const api = options.api_key;
101
+ if (api) return new OllamaTurboProvider(api, options.endpoint || 'https://ollama.com');
102
+ break;
103
+ }
104
+ case 'onn': {
105
+ const api = options.api_key;
106
+ if (api) return new OnnProvider(api, options.endpoint || 'https://api.onn.ai/v1/chat');
107
+ break;
108
+ }
109
+ case 'gemini': {
110
+ const api = options.api_key;
111
+ if (api) return new GeminiProvider(api, options.endpoint);
112
+ break;
113
+ }
114
+ case 'grok': {
115
+ const api = options.api_key;
116
+ if (api) return new GrokProvider(api, options.endpoint);
117
+ break;
118
+ }
119
+ case 'claude': {
120
+ const api = options.api_key;
121
+ if (api) return new ClaudeProvider(api, options.endpoint);
122
+ break;
123
+ }
124
+ case 'mistral': {
125
+ const api = options.api_key;
126
+ if (api) return new MistralProvider(api, options.endpoint || 'https://api.mistral.ai/v1/chat/completions');
127
+ break;
128
+ }
129
+ case 'openai_custom': {
130
+ const api = options.api_key;
131
+ const base = options.base_url;
132
+ if (api && base) {
133
+ return new CustomOpenAIProvider(api, base, options.paths || {},
134
+ options.auth_header || 'Authorization', options.auth_prefix || BEARER_PREFIX,
135
+ options.extra_headers || {});
136
+ }
137
+ break;
138
+ }
139
+ case 'openrouter': {
140
+ const api = options.api_key;
141
+ if (api) {
142
+ const base = options.base_url || 'https://openrouter.ai/api/v1';
143
+ const hdrs = {};
144
+ if (options.referer) hdrs['HTTP-Referer'] = options.referer;
145
+ if (options.title) hdrs['X-Title'] = options.title;
146
+ return new CustomOpenAIProvider(api, base,
147
+ { chat: '/chat/completions', embeddings: '/embeddings', image: '/images/generations', tts: '/audio/speech', stt: '/audio/transcriptions' },
148
+ 'Authorization', BEARER_PREFIX, hdrs
149
+ );
150
+ }
151
+ break;
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+
157
+ /** @private */
158
+ _resolveProvider(name, options = {}) {
159
+ if (this._hasOverrides(options)) {
160
+ const p = this._buildProviderFromOptions(name, options);
161
+ if (p) return p;
162
+ }
163
+ if (this._providers[name]) return this._providers[name];
164
+ const p = this._buildProviderFromOptions(name, options);
165
+ if (p) { this._providers[name] = p; }
166
+ return p || null;
167
+ }
168
+
169
+ // ─── Public API ───
170
+
171
+ /**
172
+ * Get a registered provider by name.
173
+ * @param {string} name
174
+ * @returns {Object|null}
175
+ */
176
+ provider(name) {
177
+ return this._providers[name] || null;
178
+ }
179
+
180
+ /**
181
+ * Register a provider instance.
182
+ * @param {string} name
183
+ * @param {Object} provider
184
+ * @returns {this}
185
+ */
186
+ registerProvider(name, provider) {
187
+ this._providers[name] = provider;
188
+ return this;
189
+ }
190
+
191
+ // ─── Chat ───
192
+ async chat(provider, messages, options = {}) {
193
+ const p = this._resolveProvider(provider, options);
194
+ if (!p || typeof p.chat !== 'function') throw ProviderError.unsupported(provider, 'chat');
195
+ return p.chat(messages, options);
196
+ }
197
+
198
+ // ─── Stream ───
199
+ async *stream(provider, messages, options = {}) {
200
+ const p = this._resolveProvider(provider, options);
201
+ if (!p || typeof p.stream !== 'function') throw ProviderError.unsupported(provider, 'streaming');
202
+ yield* p.stream(messages, options);
203
+ }
204
+
205
+ // ─── Stream Events ───
206
+ async *streamEvents(provider, messages, options = {}) {
207
+ const p = this._resolveProvider(provider, options);
208
+ if (!p || typeof p.supportsStreaming !== 'function' || !p.supportsStreaming()) {
209
+ throw ProviderError.unsupported(provider, 'streaming');
210
+ }
211
+ if (typeof p.streamEvents === 'function') {
212
+ yield* p.streamEvents(messages, options);
213
+ return;
214
+ }
215
+ for await (const chunk of p.stream(messages, options)) {
216
+ yield { type: 'delta', data: chunk };
217
+ }
218
+ yield { type: 'end', data: null };
219
+ }
220
+
221
+ // ─── Embeddings ───
222
+ async embeddings(provider, inputs, options = {}) {
223
+ const p = this._resolveProvider(provider, options);
224
+ if (!p || typeof p.embeddings !== 'function') throw ProviderError.unsupported(provider, 'embeddings');
225
+ return p.embeddings(inputs, options);
226
+ }
227
+
228
+ // ─── Models ───
229
+ async models(provider) {
230
+ const p = this._providers[provider];
231
+ if (!p || typeof p.listModels !== 'function') throw ProviderError.unsupported(provider, 'models');
232
+ return p.listModels();
233
+ }
234
+
235
+ async model(provider, id) {
236
+ const p = this._providers[provider];
237
+ if (!p || typeof p.getModel !== 'function') throw ProviderError.unsupported(provider, 'model');
238
+ return p.getModel(id);
239
+ }
240
+
241
+ // ─── Images ───
242
+ async image(provider, prompt, options = {}) {
243
+ const p = this._resolveProvider(provider, options);
244
+ if (!p || typeof p.generateImage !== 'function') throw ProviderError.unsupported(provider, 'image');
245
+ return p.generateImage(prompt, options);
246
+ }
247
+
248
+ // ─── Audio ───
249
+ async tts(provider, text, options = {}) {
250
+ const p = this._resolveProvider(provider, options);
251
+ if (!p || typeof p.textToSpeech !== 'function') throw ProviderError.unsupported(provider, 'tts');
252
+ return p.textToSpeech(text, options);
253
+ }
254
+
255
+ async stt(provider, filePath, options = {}) {
256
+ const p = this._resolveProvider(provider, options);
257
+ if (!p || typeof p.speechToText !== 'function') throw ProviderError.unsupported(provider, 'stt');
258
+ return p.speechToText(filePath, options);
259
+ }
260
+
261
+ // ─── Fluent builder ───
262
+ text() {
263
+ const TextBuilder = require('./Builders/TextBuilder');
264
+ return new TextBuilder(this);
265
+ }
266
+
267
+ // ─── Tools API ───
268
+ registerTool(tool) {
269
+ this._toolRegistry.register(tool);
270
+ return this;
271
+ }
272
+
273
+ tool(name) {
274
+ return this._toolRegistry.get(name);
275
+ }
276
+
277
+ tools() {
278
+ return this._toolRegistry.all();
279
+ }
280
+
281
+ async chatWithTools(provider, messages, options = {}) {
282
+ const runner = new ToolChatRunner(this);
283
+ return runner.run(provider, messages, options);
284
+ }
285
+ }
286
+
287
+ module.exports = AiBridgeManager;
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ const ChatNormalizer = require('../Support/ChatNormalizer');
4
+ const StreamChunk = require('../Support/StreamChunk');
5
+
6
+ /**
7
+ * TextBuilder
8
+ * Fluent builder for text generation over AiBridge providers.
9
+ * Keeps method names short and explicit, reducing array option errors.
10
+ *
11
+ * @example
12
+ * const result = await manager.text()
13
+ * .using('openai', 'gpt-4o-mini')
14
+ * .withPrompt('Explain quantum computing')
15
+ * .withMaxTokens(200)
16
+ * .asText();
17
+ */
18
+ class TextBuilder {
19
+ static ERR_MISSING_USING = 'Provider and model must be set via using().';
20
+
21
+ /**
22
+ * @param {import('../AiBridgeManager')} manager
23
+ */
24
+ constructor(manager) {
25
+ this._manager = manager;
26
+ this._provider = null;
27
+ this._model = null;
28
+ this._providerConfig = {};
29
+ this._messages = [];
30
+ this._systemPrompt = null;
31
+ this._maxTokens = null;
32
+ this._temperature = null;
33
+ this._topP = null;
34
+ }
35
+
36
+ /**
37
+ * Set the provider and model (required).
38
+ * @param {string} provider
39
+ * @param {string} model
40
+ * @param {Object} [providerConfig={}]
41
+ * @returns {this}
42
+ */
43
+ using(provider, model, providerConfig = {}) {
44
+ this._provider = provider;
45
+ this._model = model;
46
+ this._providerConfig = providerConfig;
47
+ return this;
48
+ }
49
+
50
+ /**
51
+ * Add a user prompt message.
52
+ * @param {string} text
53
+ * @param {Array} [attachments=[]]
54
+ * @returns {this}
55
+ */
56
+ withPrompt(text, attachments = []) {
57
+ const msg = { role: 'user', content: text };
58
+ if (attachments.length > 0) msg.attachments = attachments;
59
+ this._messages.push(msg);
60
+ return this;
61
+ }
62
+
63
+ /** Alias for withPrompt */
64
+ prompt(text) { return this.withPrompt(text); }
65
+
66
+ /**
67
+ * Set the system prompt.
68
+ * @param {string} text
69
+ * @returns {this}
70
+ */
71
+ withSystemPrompt(text) {
72
+ this._systemPrompt = text;
73
+ return this;
74
+ }
75
+
76
+ /** @param {number} tokens @returns {this} */
77
+ withMaxTokens(tokens) { this._maxTokens = tokens; return this; }
78
+
79
+ /** @param {number} t @returns {this} */
80
+ usingTemperature(t) { this._temperature = t; return this; }
81
+
82
+ /** @param {number} p @returns {this} */
83
+ usingTopP(p) { this._topP = p; return this; }
84
+
85
+ // ─── Override helpers ───
86
+
87
+ /** @param {string} key @returns {this} */
88
+ withApiKey(key) { this._providerConfig.api_key = key; return this; }
89
+ /** @param {string} ep @returns {this} */
90
+ withEndpoint(ep) { this._providerConfig.endpoint = ep; return this; }
91
+ /** @param {string} url @returns {this} */
92
+ withBaseUrl(url) { this._providerConfig.base_url = url; return this; }
93
+ /** @param {string} url @returns {this} */
94
+ withChatEndpoint(url) { this._providerConfig.chat_endpoint = url; return this; }
95
+ /** @param {string} header @param {string} [prefix='Bearer '] @returns {this} */
96
+ withAuthHeader(header, prefix = 'Bearer ') { this._providerConfig.auth_header = header; this._providerConfig.auth_prefix = prefix; return this; }
97
+ /** @param {Object} headers @returns {this} */
98
+ withExtraHeaders(headers) { this._providerConfig.extra_headers = headers; return this; }
99
+ /** @param {Object} paths @returns {this} */
100
+ withPaths(paths) { this._providerConfig.paths = paths; return this; }
101
+
102
+ // ─── Private helpers ───
103
+
104
+ /** @private */
105
+ _buildMessages() {
106
+ const msgs = [...this._messages];
107
+ if (this._systemPrompt) {
108
+ msgs.unshift({ role: 'system', content: this._systemPrompt });
109
+ }
110
+ return msgs;
111
+ }
112
+
113
+ /** @private */
114
+ _callOptions() {
115
+ const opts = { ...this._providerConfig };
116
+ if (this._model) opts.model = this._model;
117
+ if (this._maxTokens !== null) opts.max_tokens = this._maxTokens;
118
+ if (this._temperature !== null) opts.temperature = this._temperature;
119
+ if (this._topP !== null) opts.top_p = this._topP;
120
+ return opts;
121
+ }
122
+
123
+ // ─── Terminal methods ───
124
+
125
+ /**
126
+ * Execute and return normalized text response.
127
+ * @returns {Promise<{text: string, raw: Object, usage: Object|null, finish_reason: string|null}>}
128
+ */
129
+ async asText() {
130
+ if (!this._provider || !this._model) throw new Error(TextBuilder.ERR_MISSING_USING);
131
+ const res = await this._manager.chat(this._provider, this._buildMessages(), this._callOptions());
132
+ const norm = ChatNormalizer.normalize(res);
133
+ return {
134
+ text: norm.text || '',
135
+ raw: res,
136
+ usage: norm.usage || null,
137
+ finish_reason: norm.finish_reason || null,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Execute and return raw provider response.
143
+ * @returns {Promise<Object>}
144
+ */
145
+ async asRaw() {
146
+ if (!this._provider || !this._model) throw new Error(TextBuilder.ERR_MISSING_USING);
147
+ return this._manager.chat(this._provider, this._buildMessages(), this._callOptions());
148
+ }
149
+
150
+ /**
151
+ * Execute as a streaming generator of StreamChunk objects.
152
+ * @returns {AsyncGenerator<StreamChunk>}
153
+ */
154
+ async *asStream() {
155
+ if (!this._provider || !this._model) throw new Error(TextBuilder.ERR_MISSING_USING);
156
+ for await (const chunk of this._manager.stream(this._provider, this._buildMessages(), this._callOptions())) {
157
+ if (typeof chunk === 'string') {
158
+ yield StreamChunk.delta(chunk);
159
+ } else if (chunk && typeof chunk === 'object') {
160
+ const text = String(chunk.delta || chunk.text || '');
161
+ yield new StreamChunk(text, chunk.usage || null, chunk.finish_reason || null,
162
+ chunk.type || 'delta', chunk.tool_calls || [], chunk.tool_results || []);
163
+ } else {
164
+ yield StreamChunk.delta(String(chunk));
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ module.exports = TextBuilder;
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * AudioProviderContract
5
+ * Base class for providers that support text-to-speech and speech-to-text.
6
+ */
7
+ class AudioProviderContract {
8
+ /**
9
+ * Text to speech.
10
+ * @param {string} text
11
+ * @param {Object} [options={}]
12
+ * @returns {Promise<{audio: string, mime: string}>} audio is base64-encoded
13
+ */
14
+ async textToSpeech(text, options = {}) {
15
+ throw new Error('Not implemented: textToSpeech()');
16
+ }
17
+
18
+ /**
19
+ * Speech to text.
20
+ * @param {string} filePath
21
+ * @param {Object} [options={}]
22
+ * @returns {Promise<{text: string, raw?: Object}>}
23
+ */
24
+ async speechToText(filePath, options = {}) {
25
+ throw new Error('Not implemented: speechToText()');
26
+ }
27
+ }
28
+
29
+ module.exports = AudioProviderContract;
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ChatProviderContract
5
+ * Base class defining the chat provider interface.
6
+ * All chat providers must extend this and implement its methods.
7
+ */
8
+ class ChatProviderContract {
9
+ /**
10
+ * Send a chat message and return raw provider response object.
11
+ * @param {Array<{role: string, content: string}>} messages
12
+ * @param {Object} [options={}]
13
+ * @returns {Promise<Object>}
14
+ */
15
+ async chat(messages, options = {}) {
16
+ throw new Error('Not implemented: chat()');
17
+ }
18
+
19
+ /**
20
+ * Stream a chat completion (async generator yielding chunks of text or objects).
21
+ * @param {Array<{role: string, content: string}>} messages
22
+ * @param {Object} [options={}]
23
+ * @yields {string|Object}
24
+ */
25
+ async *stream(messages, options = {}) {
26
+ throw new Error('Not implemented: stream()');
27
+ }
28
+
29
+ /**
30
+ * Whether this provider supports streaming.
31
+ * @returns {boolean}
32
+ */
33
+ supportsStreaming() {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ module.exports = ChatProviderContract;
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * EmbeddingsProviderContract
5
+ * Base class for providers that support embeddings generation.
6
+ */
7
+ class EmbeddingsProviderContract {
8
+ /**
9
+ * Generate embeddings for one or multiple inputs.
10
+ * @param {string[]} inputs
11
+ * @param {Object} [options={}]
12
+ * @returns {Promise<{embeddings: number[][], usage?: Object, raw?: Object}>}
13
+ */
14
+ async embeddings(inputs, options = {}) {
15
+ throw new Error('Not implemented: embeddings()');
16
+ }
17
+ }
18
+
19
+ module.exports = EmbeddingsProviderContract;
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ImageProviderContract
5
+ * Base class for providers that support image generation.
6
+ */
7
+ class ImageProviderContract {
8
+ /**
9
+ * Generate images from a prompt.
10
+ * @param {string} prompt
11
+ * @param {Object} [options={}]
12
+ * @returns {Promise<{images: Array, meta?: Object, raw?: Object}>}
13
+ */
14
+ async generateImage(prompt, options = {}) {
15
+ throw new Error('Not implemented: generateImage()');
16
+ }
17
+ }
18
+
19
+ module.exports = ImageProviderContract;
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ModelsProviderContract
5
+ * Base class for providers that support listing models.
6
+ */
7
+ class ModelsProviderContract {
8
+ /**
9
+ * List models metadata as returned by the provider.
10
+ * @returns {Promise<Array>}
11
+ */
12
+ async listModels() {
13
+ throw new Error('Not implemented: listModels()');
14
+ }
15
+
16
+ /**
17
+ * Retrieve a single model metadata by id/name.
18
+ * @param {string} id
19
+ * @returns {Promise<Object>}
20
+ */
21
+ async getModel(id) {
22
+ throw new Error('Not implemented: getModel()');
23
+ }
24
+ }
25
+
26
+ module.exports = ModelsProviderContract;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ToolContract
5
+ * Base class for tools usable by AI providers (function calling).
6
+ */
7
+ class ToolContract {
8
+ /** @returns {string} */
9
+ name() { throw new Error('Not implemented: name()'); }
10
+
11
+ /** @returns {string} */
12
+ description() { throw new Error('Not implemented: description()'); }
13
+
14
+ /** @returns {Object} JSON Schema of parameters */
15
+ schema() { throw new Error('Not implemented: schema()'); }
16
+
17
+ /**
18
+ * Execute the tool with given arguments. Must return a string result.
19
+ * @param {Object} args
20
+ * @returns {Promise<string>|string}
21
+ */
22
+ execute(args) { throw new Error('Not implemented: execute()'); }
23
+ }
24
+
25
+ module.exports = ToolContract;
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * AiBridge Facade
5
+ *
6
+ * Convenience entry-point mirroring AiBridge\Facades\AiBridge in PHP.
7
+ * Provides static-like helpers that delegate to an AiBridgeManager instance.
8
+ */
9
+
10
+ const ImageNormalizer = require('../Support/ImageNormalizer');
11
+ const AudioNormalizer = require('../Support/AudioNormalizer');
12
+ const EmbeddingsNormalizer = require('../Support/EmbeddingsNormalizer');
13
+
14
+ let _manager = null;
15
+
16
+ const AiBridge = {
17
+ /**
18
+ * Bind an AiBridgeManager instance so all helpers delegate to it.
19
+ * @param {import('../AiBridgeManager')} manager
20
+ */
21
+ setManager(manager) {
22
+ _manager = manager;
23
+ },
24
+
25
+ /**
26
+ * Return the bound manager (or null).
27
+ * @returns {import('../AiBridgeManager')|null}
28
+ */
29
+ getManager() {
30
+ return _manager;
31
+ },
32
+
33
+ /* ─── Normalization helpers (static, no manager needed) ────── */
34
+
35
+ normalizeImages(raw) {
36
+ return ImageNormalizer.normalize(raw);
37
+ },
38
+
39
+ normalizeTTSAudio(raw) {
40
+ return AudioNormalizer.normalizeTTS(raw);
41
+ },
42
+
43
+ normalizeSTTAudio(raw) {
44
+ return AudioNormalizer.normalizeSTT(raw);
45
+ },
46
+
47
+ normalizeEmbeddings(raw) {
48
+ return EmbeddingsNormalizer.normalize(raw);
49
+ },
50
+
51
+ /* ─── Delegated helpers (require bound manager) ────────────── */
52
+
53
+ /**
54
+ * Create a new TextBuilder via the bound manager.
55
+ * @returns {import('../Builders/TextBuilder')}
56
+ */
57
+ text() {
58
+ if (!_manager) throw new Error('AiBridge facade: no manager bound. Call AiBridge.setManager(manager) first.');
59
+ return _manager.text();
60
+ },
61
+
62
+ /**
63
+ * Shorthand for manager.chat()
64
+ */
65
+ async chat(messages, opts) {
66
+ if (!_manager) throw new Error('AiBridge facade: no manager bound.');
67
+ return _manager.chat(messages, opts);
68
+ },
69
+
70
+ /**
71
+ * Shorthand for manager.provider()
72
+ */
73
+ provider(name) {
74
+ if (!_manager) throw new Error('AiBridge facade: no manager bound.');
75
+ return _manager.provider(name);
76
+ },
77
+ };
78
+
79
+ module.exports = AiBridge;