@bpinhosilva/agent-orchestrator 1.0.0-alpha.39 → 1.0.0-alpha.40

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.0.0-alpha.40](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.0.0-alpha.39...v1.0.0-alpha.40) (2026-04-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * add Ollama provider and model support ([7c24c38](https://github.com/bpinhosilva/agent-orchestrator/commit/7c24c38704975c0083485e1e2a4552997cd17e34))
7
+
1
8
  # [1.0.0-alpha.39](https://github.com/bpinhosilva/agent-orchestrator/compare/v1.0.0-alpha.38...v1.0.0-alpha.39) (2026-04-17)
2
9
 
3
10
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ Agent Orchestrator is an open-source platform for managing AI agents, tasks, and
21
21
 
22
22
  ## Current capabilities
23
23
 
24
- - Multi-provider agent execution with Google Gemini and Anthropic Claude
24
+ - Multi-provider agent execution with Google Gemini, Anthropic Claude, and Ollama (local or cloud)
25
25
  - Agent profiles with provider/model selection
26
26
  - Project management with project membership and RBAC
27
27
  - Task execution plus recurring scheduling
@@ -51,9 +51,10 @@ Agent Orchestrator is an open-source platform for managing AI agents, tasks, and
51
51
  - [Node.js](https://nodejs.org/) 24 or newer
52
52
  - npm
53
53
  - [Docker](https://www.docker.com/) and Docker Compose (optional)
54
- - At least one provider API key to execute agents:
54
+ - At least one provider API key or local model server to execute agents:
55
55
  - [Google Gemini API key](https://aistudio.google.com/)
56
56
  - [Anthropic API key](https://console.anthropic.com/)
57
+ - [Ollama](https://ollama.com/) running locally (no key required) or a cloud Ollama endpoint
57
58
 
58
59
  ## Quick start
59
60
 
@@ -112,6 +113,10 @@ JWT_REFRESH_SECRET="replace-with-another-secret-at-least-32-characters-long"
112
113
  GEMINI_API_KEY=""
113
114
  ANTHROPIC_API_KEY=""
114
115
 
116
+ # Ollama (local by default, fill in OLLAMA_HOST and OLLAMA_API_KEY for cloud usage)
117
+ OLLAMA_HOST=http://127.0.0.1:11434
118
+ OLLAMA_API_KEY=""
119
+
115
120
  # Database
116
121
  DB_TYPE=sqlite
117
122
  DATABASE_URL=
@@ -282,7 +287,7 @@ Endpoints:
282
287
 
283
288
  - **Native module errors after install**: run `npm rebuild`
284
289
  - **`JWT_SECRET` rejected**: it must be at least 32 characters
285
- - **Agent execution fails immediately**: confirm `GEMINI_API_KEY` and/or `ANTHROPIC_API_KEY` are set
290
+ - **Agent execution fails immediately**: confirm `GEMINI_API_KEY`, `ANTHROPIC_API_KEY`, or Ollama (`OLLAMA_HOST`) are set correctly for the provider in use
286
291
  - **Schema/startup issues**: run `npm run migration:run`
287
292
  - **Need to undo the latest migration**: run `npm run migration:revert`
288
293
 
@@ -14,6 +14,7 @@ const agents_service_1 = require("./agents.service");
14
14
  const agent_entity_1 = require("./entities/agent.entity");
15
15
  const gemini_agent_1 = require("./implementations/gemini.agent");
16
16
  const claude_agent_1 = require("./implementations/claude.agent");
17
+ const ollama_agent_1 = require("./implementations/ollama.agent");
17
18
  let AgentsModule = class AgentsModule {
18
19
  };
19
20
  exports.AgentsModule = AgentsModule;
@@ -21,7 +22,7 @@ exports.AgentsModule = AgentsModule = __decorate([
21
22
  (0, common_1.Module)({
22
23
  imports: [typeorm_1.TypeOrmModule.forFeature([agent_entity_1.AgentEntity])],
23
24
  controllers: [agents_controller_1.AgentsController],
24
- providers: [agents_service_1.AgentsService, gemini_agent_1.GeminiAgent, claude_agent_1.ClaudeAgent],
25
+ providers: [agents_service_1.AgentsService, gemini_agent_1.GeminiAgent, claude_agent_1.ClaudeAgent, ollama_agent_1.OllamaAgent],
25
26
  exports: [agents_service_1.AgentsService],
26
27
  })
27
28
  ], AgentsModule);
@@ -89,19 +89,25 @@ let AgentsService = AgentsService_1 = class AgentsService {
89
89
  if (modelId) {
90
90
  agentData.model = { id: modelId };
91
91
  }
92
- return await this.agentRepository.manager.transaction(async (manager) => {
93
- const agent = manager.create(agent_entity_1.AgentEntity, agentData);
94
- const savedAgent = await manager.save(agent);
95
- const fullyLoadedAgent = await manager.findOne(agent_entity_1.AgentEntity, {
96
- where: { id: savedAgent.id },
97
- relations: AGENT_RELATIONS,
92
+ try {
93
+ return await this.agentRepository.manager.transaction(async (manager) => {
94
+ const agent = manager.create(agent_entity_1.AgentEntity, agentData);
95
+ const savedAgent = await manager.save(agent);
96
+ const fullyLoadedAgent = await manager.findOne(agent_entity_1.AgentEntity, {
97
+ where: { id: savedAgent.id },
98
+ relations: AGENT_RELATIONS,
99
+ });
100
+ if (!fullyLoadedAgent) {
101
+ throw new Error(`Agent #${savedAgent.id} not found immediately after save`);
102
+ }
103
+ await this.syncAgentInstance(fullyLoadedAgent);
104
+ return fullyLoadedAgent;
98
105
  });
99
- if (!fullyLoadedAgent) {
100
- throw new Error(`Agent #${savedAgent.id} not found immediately after save`);
101
- }
102
- await this.syncAgentInstance(fullyLoadedAgent);
103
- return fullyLoadedAgent;
104
- });
106
+ }
107
+ catch (error) {
108
+ this.rethrowDatabaseError(error);
109
+ throw error;
110
+ }
105
111
  }
106
112
  async findAll() {
107
113
  return this.agentRepository.find({ relations: AGENT_RELATIONS });
@@ -122,18 +128,35 @@ let AgentsService = AgentsService_1 = class AgentsService {
122
128
  if (modelId) {
123
129
  updateData.model = { id: modelId };
124
130
  }
125
- return await this.agentRepository.manager.transaction(async (manager) => {
126
- await manager.update(agent_entity_1.AgentEntity, id, updateData);
127
- const updatedAgent = await manager.findOne(agent_entity_1.AgentEntity, {
128
- where: { id },
129
- relations: AGENT_RELATIONS,
131
+ try {
132
+ return await this.agentRepository.manager.transaction(async (manager) => {
133
+ await manager.update(agent_entity_1.AgentEntity, id, updateData);
134
+ const updatedAgent = await manager.findOne(agent_entity_1.AgentEntity, {
135
+ where: { id },
136
+ relations: AGENT_RELATIONS,
137
+ });
138
+ if (!updatedAgent) {
139
+ throw new common_1.NotFoundException(`Agent #${id} not found`);
140
+ }
141
+ await this.syncAgentInstance(updatedAgent);
142
+ return updatedAgent;
130
143
  });
131
- if (!updatedAgent) {
132
- throw new common_1.NotFoundException(`Agent #${id} not found`);
144
+ }
145
+ catch (error) {
146
+ this.rethrowDatabaseError(error);
147
+ throw error;
148
+ }
149
+ }
150
+ rethrowDatabaseError(error) {
151
+ if (error instanceof typeorm_2.QueryFailedError) {
152
+ const msg = error.message ?? '';
153
+ const isUniqueViolation = msg.toLowerCase().includes('unique') ||
154
+ msg.toLowerCase().includes('duplicate');
155
+ if (isUniqueViolation) {
156
+ throw new common_1.ConflictException('An agent with that name already exists');
133
157
  }
134
- await this.syncAgentInstance(updatedAgent);
135
- return updatedAgent;
136
- });
158
+ throw new common_1.BadRequestException(`Database error: ${msg}`);
159
+ }
137
160
  }
138
161
  async remove(id) {
139
162
  const agent = await this.findOne(id);
@@ -10,8 +10,13 @@ exports.DEFAULT_PROVIDER_MODELS = [
10
10
  providerName: 'anthropic',
11
11
  models: ['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5'],
12
12
  },
13
+ {
14
+ providerName: 'ollama',
15
+ models: ['gemma4'],
16
+ },
13
17
  ];
14
18
  exports.DEFAULT_MODEL_BY_PROVIDER = {
15
19
  google: exports.DEFAULT_PROVIDER_MODELS[0].models[0],
16
20
  anthropic: exports.DEFAULT_PROVIDER_MODELS[1].models[0],
21
+ ollama: exports.DEFAULT_PROVIDER_MODELS[2].models[0],
17
22
  };
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var OllamaAgent_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.OllamaAgent = void 0;
17
+ const ollama_1 = require("ollama");
18
+ const common_1 = require("@nestjs/common");
19
+ const config_1 = require("@nestjs/config");
20
+ const default_provider_models_1 = require("../default-provider-models");
21
+ const agent_registry_1 = require("../registry/agent.registry");
22
+ const personality_util_1 = require("../personality.util");
23
+ let OllamaAgent = OllamaAgent_1 = class OllamaAgent {
24
+ configService;
25
+ logger = new common_1.Logger(OllamaAgent_1.name);
26
+ ollama;
27
+ name = 'OllamaAgent';
28
+ description;
29
+ systemInstructions;
30
+ role;
31
+ provider = 'ollama';
32
+ model;
33
+ attributes;
34
+ constructor(configService, model = default_provider_models_1.DEFAULT_MODEL_BY_PROVIDER.ollama) {
35
+ this.configService = configService;
36
+ this.model = model;
37
+ const host = this.configService.get('OLLAMA_HOST') ?? 'http://127.0.0.1:11434';
38
+ const apiKey = this.configService.get('OLLAMA_API_KEY') ?? '';
39
+ const config = { host };
40
+ if (apiKey) {
41
+ config.headers = { Authorization: `Bearer ${apiKey}` };
42
+ }
43
+ this.ollama = new ollama_1.Ollama(config);
44
+ }
45
+ getName() {
46
+ return this.name;
47
+ }
48
+ getDescription() {
49
+ return this.description || '';
50
+ }
51
+ getSystemInstructions() {
52
+ return this.systemInstructions || '';
53
+ }
54
+ getRole() {
55
+ return this.role || '';
56
+ }
57
+ getProvider() {
58
+ return this.provider;
59
+ }
60
+ getModel() {
61
+ return this.model;
62
+ }
63
+ updateConfig(config) {
64
+ if (config['name'])
65
+ this.name = config['name'];
66
+ if (config['description'])
67
+ this.description = config['description'];
68
+ if (config['systemInstructions'])
69
+ this.systemInstructions = config['systemInstructions'];
70
+ if (config['role'])
71
+ this.role = config['role'];
72
+ if (config['provider'])
73
+ this.provider = config['provider'];
74
+ if (config['model'])
75
+ this.model = config['model'];
76
+ if ('attributes' in config)
77
+ this.attributes = config['attributes'];
78
+ }
79
+ buildSystemPrompt() {
80
+ return [
81
+ `Your name is ${this.name}.`,
82
+ this.role ? `Your role is: ${this.role}.` : '',
83
+ this.description ? `Description of yourself: ${this.description}.` : '',
84
+ this.systemInstructions
85
+ ? `Instructions:\n${this.systemInstructions}`
86
+ : '',
87
+ (0, personality_util_1.buildPersonalitySection)(this.attributes),
88
+ ]
89
+ .filter(Boolean)
90
+ .join('\n');
91
+ }
92
+ async processText(input) {
93
+ this.logger.debug(`Processing input with OllamaAgent. Model: ${this.model}`);
94
+ try {
95
+ const systemPrompt = this.buildSystemPrompt();
96
+ const response = await this.ollama.chat({
97
+ model: this.model,
98
+ messages: [
99
+ { role: 'system', content: systemPrompt },
100
+ { role: 'user', content: input },
101
+ ],
102
+ stream: false,
103
+ });
104
+ return {
105
+ content: response.message.content,
106
+ metadata: {
107
+ model: this.model,
108
+ usage: {
109
+ eval_count: response.eval_count,
110
+ prompt_eval_count: response.prompt_eval_count,
111
+ },
112
+ },
113
+ };
114
+ }
115
+ catch (error) {
116
+ this.logger.error(`Ollama Error: ${error}`);
117
+ throw error;
118
+ }
119
+ }
120
+ isFeatureSupported(_feature) {
121
+ void _feature;
122
+ return false;
123
+ }
124
+ };
125
+ exports.OllamaAgent = OllamaAgent;
126
+ exports.OllamaAgent = OllamaAgent = OllamaAgent_1 = __decorate([
127
+ (0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
128
+ (0, agent_registry_1.RegisterAgent)('ollama'),
129
+ __param(1, (0, common_1.Optional)()),
130
+ __metadata("design:paramtypes", [config_1.ConfigService, String])
131
+ ], OllamaAgent);
@@ -13,12 +13,14 @@ function registerSetupCommand(program) {
13
13
  .option('--db-type <type>', 'Database type: sqlite or postgres')
14
14
  .option('--database-url <url>', 'PostgreSQL connection string')
15
15
  .option('--db-logging', 'Enable database query logging')
16
- .option('--provider <provider>', 'Configure a provider (gemini, anthropic)', (value, previous = []) => previous.concat(value
16
+ .option('--provider <provider>', 'Configure a provider (gemini, anthropic, ollama)', (value, previous = []) => previous.concat(value
17
17
  .split(',')
18
18
  .map((item) => item.trim().toLowerCase())
19
19
  .filter(Boolean)), [])
20
20
  .option('--gemini-key <key>', 'Google Gemini API key')
21
21
  .option('--anthropic-key <key>', 'Anthropic Claude API key')
22
+ .option('--ollama-key <key>', 'Ollama API key (leave blank for local installations)')
23
+ .option('--ollama-host <host>', 'Ollama host URL (default: http://127.0.0.1:11434)')
22
24
  .option('-y, --yes', 'Disable prompts and use supplied flags/defaults')
23
25
  .option('--skip-admin-setup', 'Skip creating or updating the admin user')
24
26
  .option('--admin-name <name>', 'Admin user name for non-interactive setup')
package/dist/cli/env.js CHANGED
@@ -73,7 +73,7 @@ function writePrivateFile(filePath, content, fsDep = realFs) {
73
73
  fsDep.writeFileSync(filePath, content, { mode: 0o600 });
74
74
  fsDep.chmodSync(filePath, 0o600);
75
75
  }
76
- function buildEnvContent(currentEnv, basicConfig, databaseUrl, geminiKey, anthropicKey, jwtSecret, jwtRefreshSecret) {
76
+ function buildEnvContent(currentEnv, basicConfig, databaseUrl, geminiKey, anthropicKey, jwtSecret, jwtRefreshSecret, ollamaApiKey = '', ollamaHost = '') {
77
77
  const envValues = {
78
78
  ...currentEnv,
79
79
  NODE_ENV: 'production',
@@ -97,6 +97,12 @@ function buildEnvContent(currentEnv, basicConfig, databaseUrl, geminiKey, anthro
97
97
  if (anthropicKey) {
98
98
  envValues.ANTHROPIC_API_KEY = anthropicKey;
99
99
  }
100
+ if (ollamaHost) {
101
+ envValues.OLLAMA_HOST = ollamaHost;
102
+ }
103
+ if (ollamaApiKey) {
104
+ envValues.OLLAMA_API_KEY = ollamaApiKey;
105
+ }
100
106
  const orderedKeys = [
101
107
  'NODE_ENV',
102
108
  'HOST',
@@ -107,6 +113,8 @@ function buildEnvContent(currentEnv, basicConfig, databaseUrl, geminiKey, anthro
107
113
  'DATABASE_URL',
108
114
  'GEMINI_API_KEY',
109
115
  'ANTHROPIC_API_KEY',
116
+ 'OLLAMA_HOST',
117
+ 'OLLAMA_API_KEY',
110
118
  'JWT_SECRET',
111
119
  'JWT_REFRESH_SECRET',
112
120
  ];
@@ -61,6 +61,8 @@ async function handleSetup(opts, fsDep, prompter, dsFactory) {
61
61
  jwtRefreshSecret,
62
62
  geminiApiKey: opts.geminiKey ?? existingEnv.GEMINI_API_KEY ?? '',
63
63
  anthropicApiKey: opts.anthropicKey ?? existingEnv.ANTHROPIC_API_KEY ?? '',
64
+ ollamaApiKey: opts.ollamaKey ?? existingEnv.OLLAMA_API_KEY ?? '',
65
+ ollamaHost: opts.ollamaHost ?? existingEnv.OLLAMA_HOST ?? '',
64
66
  };
65
67
  }
66
68
  else {
@@ -74,7 +76,7 @@ async function handleSetup(opts, fsDep, prompter, dsFactory) {
74
76
  port: answers.port,
75
77
  dbType: answers.dbType,
76
78
  dbLogging: existingEnv.DB_LOGGING === 'true',
77
- }, answers.databaseUrl, answers.geminiApiKey, answers.anthropicApiKey, answers.jwtSecret, answers.jwtRefreshSecret);
79
+ }, answers.databaseUrl, answers.geminiApiKey, answers.anthropicApiKey, answers.jwtSecret, answers.jwtRefreshSecret, answers.ollamaApiKey, answers.ollamaHost);
78
80
  (0, env_1.writePrivateFile)(constants_1.ENV_PATH, envContent, fsDep);
79
81
  console.log(`Configuration saved to ${constants_1.ENV_PATH} with mode 600.`);
80
82
  if (dsFactory !== undefined) {
@@ -43,6 +43,8 @@ exports.promptJwtSecret = promptJwtSecret;
43
43
  exports.promptJwtRefreshSecret = promptJwtRefreshSecret;
44
44
  exports.promptGeminiKey = promptGeminiKey;
45
45
  exports.promptAnthropicKey = promptAnthropicKey;
46
+ exports.promptOllamaHost = promptOllamaHost;
47
+ exports.promptOllamaKey = promptOllamaKey;
46
48
  exports.runSetupPrompts = runSetupPrompts;
47
49
  const crypto = __importStar(require("crypto"));
48
50
  const constants_1 = require("../constants");
@@ -191,9 +193,27 @@ async function promptAnthropicKey(prompter, required = false, initial) {
191
193
  });
192
194
  return answer.key ?? '';
193
195
  }
196
+ async function promptOllamaHost(prompter, initial = 'http://127.0.0.1:11434') {
197
+ const answer = await prompter.prompt({
198
+ type: 'input',
199
+ name: 'ollamaHost',
200
+ message: 'Enter your Ollama host URL (leave as default for local installations):',
201
+ initial,
202
+ validate: (value) => value.trim().length > 0 || 'Ollama host URL cannot be empty',
203
+ });
204
+ return (answer.ollamaHost ?? '').trim() || 'http://127.0.0.1:11434';
205
+ }
206
+ async function promptOllamaKey(prompter, initial) {
207
+ const answer = await prompter.prompt({
208
+ type: 'password',
209
+ name: 'key',
210
+ message: 'Enter your Ollama API Key (leave blank for local installations):',
211
+ initial: initial ?? '',
212
+ });
213
+ return answer.key ?? '';
214
+ }
194
215
  /**
195
216
  * Orchestrates all interactive setup prompts and returns a `SetupAnswers`
196
- * object ready for env-file generation.
197
217
  *
198
218
  * @param existingEnv - Current env values used as prompt defaults.
199
219
  * @param prompter - Optional injected prompter; defaults to a lazy-loaded
@@ -221,6 +241,12 @@ async function runSetupPrompts(existingEnv = {}, prompter) {
221
241
  const anthropicApiKey = providers.includes('anthropic')
222
242
  ? await promptAnthropicKey(p, true, existingEnv.ANTHROPIC_API_KEY)
223
243
  : (existingEnv.ANTHROPIC_API_KEY ?? '');
244
+ const ollamaHost = providers.includes('ollama')
245
+ ? await promptOllamaHost(p, existingEnv.OLLAMA_HOST)
246
+ : (existingEnv.OLLAMA_HOST ?? '');
247
+ const ollamaApiKey = providers.includes('ollama')
248
+ ? await promptOllamaKey(p, existingEnv.OLLAMA_API_KEY)
249
+ : (existingEnv.OLLAMA_API_KEY ?? '');
224
250
  return {
225
251
  host,
226
252
  port,
@@ -232,5 +258,7 @@ async function runSetupPrompts(existingEnv = {}, prompter) {
232
258
  jwtRefreshSecret,
233
259
  geminiApiKey,
234
260
  anthropicApiKey,
261
+ ollamaApiKey,
262
+ ollamaHost,
235
263
  };
236
264
  }
package/dist/cli/types.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SUPPORTED_PROVIDERS = void 0;
4
- exports.SUPPORTED_PROVIDERS = ['gemini', 'anthropic'];
4
+ exports.SUPPORTED_PROVIDERS = ['gemini', 'anthropic', 'ollama'];
@@ -5,10 +5,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  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;
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
+ var HttpExceptionFilter_1;
8
9
  Object.defineProperty(exports, "__esModule", { value: true });
9
10
  exports.HttpExceptionFilter = void 0;
10
11
  const common_1 = require("@nestjs/common");
11
- let HttpExceptionFilter = class HttpExceptionFilter {
12
+ let HttpExceptionFilter = HttpExceptionFilter_1 = class HttpExceptionFilter {
13
+ logger = new common_1.Logger(HttpExceptionFilter_1.name);
12
14
  catch(exception, host) {
13
15
  const ctx = host.switchToHttp();
14
16
  const response = ctx.getResponse();
@@ -33,6 +35,10 @@ let HttpExceptionFilter = class HttpExceptionFilter {
33
35
  }
34
36
  }
35
37
  }
38
+ else {
39
+ const err = exception instanceof Error ? exception : new Error(String(exception));
40
+ this.logger.error(`Unhandled exception on ${request.method} ${request.url}: ${err.message}`, err.stack);
41
+ }
36
42
  response.status(status).json({
37
43
  statusCode: status,
38
44
  message,
@@ -43,6 +49,6 @@ let HttpExceptionFilter = class HttpExceptionFilter {
43
49
  }
44
50
  };
45
51
  exports.HttpExceptionFilter = HttpExceptionFilter;
46
- exports.HttpExceptionFilter = HttpExceptionFilter = __decorate([
52
+ exports.HttpExceptionFilter = HttpExceptionFilter = HttpExceptionFilter_1 = __decorate([
47
53
  (0, common_1.Catch)()
48
54
  ], HttpExceptionFilter);
@@ -43,6 +43,8 @@ exports.envValidationSchema = Joi.object({
43
43
  JWT_REFRESH_SECRET: Joi.string().min(32).required(),
44
44
  GEMINI_API_KEY: Joi.string().optional(),
45
45
  ANTHROPIC_API_KEY: Joi.string().optional(),
46
+ OLLAMA_HOST: Joi.string().optional(),
47
+ OLLAMA_API_KEY: Joi.string().optional(),
46
48
  DB_LOGGING: Joi.boolean().default(false),
47
49
  SERVE_STATIC_UI: Joi.boolean().default(true),
48
50
  CHECK_PENDING_MIGRATIONS_ON_STARTUP: Joi.boolean().default(false),
package/dist/main.js CHANGED
@@ -41,8 +41,8 @@ async function bootstrap() {
41
41
  await ensureDatabaseIsReadyForStartup();
42
42
  const nodeEnv = process.env.NODE_ENV || 'development';
43
43
  const defaultLevels = nodeEnv === 'production'
44
- ? ['error']
45
- : ['log', 'error', 'warn', 'debug', 'verbose', 'fatal'];
44
+ ? ['fatal', 'error']
45
+ : ['fatal', 'error', 'warn', 'log', 'debug', 'verbose'];
46
46
  let logLevels = defaultLevels;
47
47
  const envLogLevel = process.env.LOG_LEVEL;
48
48
  if (envLogLevel) {
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddOllamaProviderAndModel1775270000000 = void 0;
4
+ const crypto_1 = require("crypto");
5
+ class AddOllamaProviderAndModel1775270000000 {
6
+ name = 'AddOllamaProviderAndModel1775270000000';
7
+ escapeSqlLiteral(value) {
8
+ return `'${value.replace(/'/g, "''")}'`;
9
+ }
10
+ async getIdRow(queryRunner, sql) {
11
+ const rows = (await queryRunner.query(sql));
12
+ return rows[0] ?? null;
13
+ }
14
+ async up(queryRunner) {
15
+ const existingProvider = await this.getIdRow(queryRunner, `SELECT "id" FROM "providers" WHERE "name" = 'ollama'`);
16
+ const providerId = existingProvider?.id ?? (0, crypto_1.randomUUID)();
17
+ if (!existingProvider) {
18
+ await queryRunner.query(`INSERT INTO "providers" ("id", "name") VALUES (${this.escapeSqlLiteral(providerId)}, 'ollama')`);
19
+ }
20
+ const existingModel = await this.getIdRow(queryRunner, `SELECT "id" FROM "models" WHERE "name" = 'gemma4' AND "providerId" = ${this.escapeSqlLiteral(providerId)}`);
21
+ if (!existingModel) {
22
+ await queryRunner.query(`INSERT INTO "models" ("id", "name", "providerId") VALUES (${this.escapeSqlLiteral((0, crypto_1.randomUUID)())}, 'gemma4', ${this.escapeSqlLiteral(providerId)})`);
23
+ }
24
+ }
25
+ async down(queryRunner) {
26
+ const provider = await this.getIdRow(queryRunner, `SELECT "id" FROM "providers" WHERE "name" = 'ollama'`);
27
+ if (provider) {
28
+ await queryRunner.query(`DELETE FROM "models" WHERE "name" = 'gemma4' AND "providerId" = ${this.escapeSqlLiteral(provider.id)}`);
29
+ }
30
+ await queryRunner.query(`DELETE FROM "providers" WHERE "name" = 'ollama'`);
31
+ }
32
+ }
33
+ exports.AddOllamaProviderAndModel1775270000000 = AddOllamaProviderAndModel1775270000000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpinhosilva/agent-orchestrator",
3
- "version": "1.0.0-alpha.39",
3
+ "version": "1.0.0-alpha.40",
4
4
  "description": "An open-source AI agent orchestrator platform built with NestJS.",
5
5
  "author": "bpinhosilva",
6
6
  "license": "MIT",
@@ -97,6 +97,7 @@
97
97
  "enquirer": "^2.4.1",
98
98
  "helmet": "^8.1.0",
99
99
  "joi": "^18.1.1",
100
+ "ollama": "^0.6.3",
100
101
  "passport": "^0.7.0",
101
102
  "passport-jwt": "^4.0.1",
102
103
  "passport-local": "^1.0.0",