@ugm/desiagent 0.1.21
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/README.md +251 -0
- package/dist/__tests__/test-utils.d.ts +24 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/__tests__/test-utils.js +32 -0
- package/dist/__tests__/test-utils.js.map +1 -0
- package/dist/core/execution/agents.d.ts +60 -0
- package/dist/core/execution/agents.d.ts.map +1 -0
- package/dist/core/execution/agents.js +249 -0
- package/dist/core/execution/agents.js.map +1 -0
- package/dist/core/execution/artifacts.d.ts +27 -0
- package/dist/core/execution/artifacts.d.ts.map +1 -0
- package/dist/core/execution/artifacts.js +93 -0
- package/dist/core/execution/artifacts.js.map +1 -0
- package/dist/core/execution/costs.d.ts +160 -0
- package/dist/core/execution/costs.d.ts.map +1 -0
- package/dist/core/execution/costs.js +196 -0
- package/dist/core/execution/costs.js.map +1 -0
- package/dist/core/execution/dagExecutor.d.ts +112 -0
- package/dist/core/execution/dagExecutor.d.ts.map +1 -0
- package/dist/core/execution/dagExecutor.js +647 -0
- package/dist/core/execution/dagExecutor.js.map +1 -0
- package/dist/core/execution/dags.d.ts +167 -0
- package/dist/core/execution/dags.d.ts.map +1 -0
- package/dist/core/execution/dags.js +713 -0
- package/dist/core/execution/dags.js.map +1 -0
- package/dist/core/execution/executions.d.ts +158 -0
- package/dist/core/execution/executions.d.ts.map +1 -0
- package/dist/core/execution/executions.js +258 -0
- package/dist/core/execution/executions.js.map +1 -0
- package/dist/core/execution/goals.d.ts +62 -0
- package/dist/core/execution/goals.d.ts.map +1 -0
- package/dist/core/execution/goals.js +245 -0
- package/dist/core/execution/goals.js.map +1 -0
- package/dist/core/execution/runs.d.ts +65 -0
- package/dist/core/execution/runs.d.ts.map +1 -0
- package/dist/core/execution/runs.js +219 -0
- package/dist/core/execution/runs.js.map +1 -0
- package/dist/core/execution/tools.d.ts +24 -0
- package/dist/core/execution/tools.d.ts.map +1 -0
- package/dist/core/execution/tools.js +33 -0
- package/dist/core/execution/tools.js.map +1 -0
- package/dist/core/orchestration/index.d.ts +7 -0
- package/dist/core/orchestration/index.d.ts.map +1 -0
- package/dist/core/orchestration/index.js +7 -0
- package/dist/core/orchestration/index.js.map +1 -0
- package/dist/core/orchestration/orchestrator.d.ts +39 -0
- package/dist/core/orchestration/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestration/orchestrator.js +141 -0
- package/dist/core/orchestration/orchestrator.js.map +1 -0
- package/dist/core/orchestration/planner.d.ts +63 -0
- package/dist/core/orchestration/planner.d.ts.map +1 -0
- package/dist/core/orchestration/planner.js +99 -0
- package/dist/core/orchestration/planner.js.map +1 -0
- package/dist/core/providers/factory.d.ts +25 -0
- package/dist/core/providers/factory.d.ts.map +1 -0
- package/dist/core/providers/factory.js +86 -0
- package/dist/core/providers/factory.js.map +1 -0
- package/dist/core/providers/index.d.ts +11 -0
- package/dist/core/providers/index.d.ts.map +1 -0
- package/dist/core/providers/index.js +10 -0
- package/dist/core/providers/index.js.map +1 -0
- package/dist/core/providers/ollama.d.ts +36 -0
- package/dist/core/providers/ollama.d.ts.map +1 -0
- package/dist/core/providers/ollama.js +123 -0
- package/dist/core/providers/ollama.js.map +1 -0
- package/dist/core/providers/openai.d.ts +33 -0
- package/dist/core/providers/openai.d.ts.map +1 -0
- package/dist/core/providers/openai.js +114 -0
- package/dist/core/providers/openai.js.map +1 -0
- package/dist/core/providers/openrouter.d.ts +38 -0
- package/dist/core/providers/openrouter.d.ts.map +1 -0
- package/dist/core/providers/openrouter.js +269 -0
- package/dist/core/providers/openrouter.js.map +1 -0
- package/dist/core/providers/types.d.ts +101 -0
- package/dist/core/providers/types.d.ts.map +1 -0
- package/dist/core/providers/types.js +7 -0
- package/dist/core/providers/types.js.map +1 -0
- package/dist/core/tools/base.d.ts +56 -0
- package/dist/core/tools/base.d.ts.map +1 -0
- package/dist/core/tools/base.js +60 -0
- package/dist/core/tools/base.js.map +1 -0
- package/dist/core/tools/bash.d.ts +50 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +179 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/edit.d.ts +39 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +67 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/executor.d.ts +33 -0
- package/dist/core/tools/executor.d.ts.map +1 -0
- package/dist/core/tools/executor.js +105 -0
- package/dist/core/tools/executor.js.map +1 -0
- package/dist/core/tools/fetchPage.d.ts +46 -0
- package/dist/core/tools/fetchPage.d.ts.map +1 -0
- package/dist/core/tools/fetchPage.js +87 -0
- package/dist/core/tools/fetchPage.js.map +1 -0
- package/dist/core/tools/fetchURLs.d.ts +39 -0
- package/dist/core/tools/fetchURLs.d.ts.map +1 -0
- package/dist/core/tools/fetchURLs.js +67 -0
- package/dist/core/tools/fetchURLs.js.map +1 -0
- package/dist/core/tools/glob.d.ts +36 -0
- package/dist/core/tools/glob.d.ts.map +1 -0
- package/dist/core/tools/glob.js +78 -0
- package/dist/core/tools/glob.js.map +1 -0
- package/dist/core/tools/grep.d.ts +51 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +152 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/index.d.ts +22 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +22 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/llmExecute.d.ts +153 -0
- package/dist/core/tools/llmExecute.d.ts.map +1 -0
- package/dist/core/tools/llmExecute.js +105 -0
- package/dist/core/tools/llmExecute.js.map +1 -0
- package/dist/core/tools/readEmail.d.ts +68 -0
- package/dist/core/tools/readEmail.d.ts.map +1 -0
- package/dist/core/tools/readEmail.js +182 -0
- package/dist/core/tools/readEmail.js.map +1 -0
- package/dist/core/tools/readFile.d.ts +42 -0
- package/dist/core/tools/readFile.d.ts.map +1 -0
- package/dist/core/tools/readFile.js +79 -0
- package/dist/core/tools/readFile.js.map +1 -0
- package/dist/core/tools/registry.d.ts +53 -0
- package/dist/core/tools/registry.d.ts.map +1 -0
- package/dist/core/tools/registry.js +112 -0
- package/dist/core/tools/registry.js.map +1 -0
- package/dist/core/tools/sendEmail.d.ts +83 -0
- package/dist/core/tools/sendEmail.d.ts.map +1 -0
- package/dist/core/tools/sendEmail.js +132 -0
- package/dist/core/tools/sendEmail.js.map +1 -0
- package/dist/core/tools/sendWebhook.d.ts +48 -0
- package/dist/core/tools/sendWebhook.d.ts.map +1 -0
- package/dist/core/tools/sendWebhook.js +61 -0
- package/dist/core/tools/sendWebhook.js.map +1 -0
- package/dist/core/tools/webSearch.d.ts +34 -0
- package/dist/core/tools/webSearch.d.ts.map +1 -0
- package/dist/core/tools/webSearch.js +104 -0
- package/dist/core/tools/webSearch.js.map +1 -0
- package/dist/core/tools/writeFile.d.ts +49 -0
- package/dist/core/tools/writeFile.d.ts.map +1 -0
- package/dist/core/tools/writeFile.js +120 -0
- package/dist/core/tools/writeFile.js.map +1 -0
- package/dist/db/client.d.ts +22 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +178 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/schema.d.ts +1846 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +175 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/errors/index.d.ts +80 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +135 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/dist/services/initDB.d.ts +17 -0
- package/dist/services/initDB.d.ts.map +1 -0
- package/dist/services/initDB.js +212 -0
- package/dist/services/initDB.js.map +1 -0
- package/dist/types/agent.d.ts +354 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +63 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/client.d.ts +309 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +2 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/config.d.ts +96 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +33 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/dag.d.ts +253 -0
- package/dist/types/dag.d.ts.map +1 -0
- package/dist/types/dag.js +46 -0
- package/dist/types/dag.js.map +1 -0
- package/dist/types/execution.d.ts +171 -0
- package/dist/types/execution.d.ts.map +1 -0
- package/dist/types/execution.js +41 -0
- package/dist/types/execution.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/util/cron-validator.d.ts +16 -0
- package/dist/util/cron-validator.d.ts.map +1 -0
- package/dist/util/cron-validator.js +31 -0
- package/dist/util/cron-validator.js.map +1 -0
- package/dist/util/dag-utils.d.ts +54 -0
- package/dist/util/dag-utils.d.ts.map +1 -0
- package/dist/util/dag-utils.js +167 -0
- package/dist/util/dag-utils.js.map +1 -0
- package/dist/util/logger.d.ts +21 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +87 -0
- package/dist/util/logger.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAGs Service
|
|
3
|
+
*
|
|
4
|
+
* Manages DAG (Directed Acyclic Graph) creation, execution, and scheduling.
|
|
5
|
+
* DAGs represent decomposed workflows for complex objectives.
|
|
6
|
+
*/
|
|
7
|
+
import { eq, desc, isNotNull, sql } from 'drizzle-orm';
|
|
8
|
+
import { nanoid } from 'nanoid';
|
|
9
|
+
import cronstrue from 'cronstrue';
|
|
10
|
+
import { dags, dagExecutions, dagSubSteps } from '../../db/schema.js';
|
|
11
|
+
import { NotFoundError, ValidationError } from '../../errors/index.js';
|
|
12
|
+
import { getLogger } from '../../util/logger.js';
|
|
13
|
+
import { validateCronExpression } from '../../util/cron-validator.js';
|
|
14
|
+
import { extractCodeBlock, renumberSubTasks, truncate, truncateForLog, } from '../../util/dag-utils.js';
|
|
15
|
+
import { DecomposerJobSchema } from '../../types/dag.js';
|
|
16
|
+
import { createLLMProvider } from '../providers/factory.js';
|
|
17
|
+
import { DAGExecutor } from './dagExecutor.js';
|
|
18
|
+
export function generateDAGId() {
|
|
19
|
+
return `dag_${nanoid(21)}`;
|
|
20
|
+
}
|
|
21
|
+
export function generateDAGExecutionId() {
|
|
22
|
+
return `exec_${nanoid(21)}`;
|
|
23
|
+
}
|
|
24
|
+
export function generateSubStepId() {
|
|
25
|
+
return `substep_${nanoid(21)}`;
|
|
26
|
+
}
|
|
27
|
+
export class DAGsService {
|
|
28
|
+
db;
|
|
29
|
+
llmProvider;
|
|
30
|
+
toolRegistry;
|
|
31
|
+
agentsService;
|
|
32
|
+
scheduler;
|
|
33
|
+
artifactsDir;
|
|
34
|
+
logger = getLogger();
|
|
35
|
+
constructor(deps) {
|
|
36
|
+
this.db = deps.db;
|
|
37
|
+
this.llmProvider = deps.llmProvider;
|
|
38
|
+
this.toolRegistry = deps.toolRegistry;
|
|
39
|
+
this.agentsService = deps.agentsService;
|
|
40
|
+
this.scheduler = deps.scheduler;
|
|
41
|
+
this.artifactsDir = deps.artifactsDir || process.env.ARTIFACTS_DIR || './artifacts';
|
|
42
|
+
}
|
|
43
|
+
async createFromGoal(options) {
|
|
44
|
+
const { goalText, agentName, provider, model, temperature = 0.7, maxTokens = 10000, seed, cronSchedule, scheduleActive: inputScheduleActive, timezone = 'UTC', } = options;
|
|
45
|
+
const scheduleActive = inputScheduleActive ?? !!cronSchedule;
|
|
46
|
+
const showGoalText = truncateForLog(goalText);
|
|
47
|
+
this.logger.info({ agentName, goalText: showGoalText }, 'Create DAG from goal');
|
|
48
|
+
if (cronSchedule) {
|
|
49
|
+
const validation = validateCronExpression(cronSchedule);
|
|
50
|
+
if (!validation.valid) {
|
|
51
|
+
throw new ValidationError(`Invalid cron expression: ${validation.error}`, 'cronSchedule', cronSchedule);
|
|
52
|
+
}
|
|
53
|
+
this.logger.debug({ cronSchedule, nextRuns: validation.nextRuns }, '╰─Valid cron schedule provided');
|
|
54
|
+
}
|
|
55
|
+
const agent = await this.agentsService.resolve(agentName);
|
|
56
|
+
if (!agent) {
|
|
57
|
+
throw new NotFoundError('Agent', agentName);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.logger.debug({ agentName, provider, model }, '╰─Agent resolved');
|
|
61
|
+
this.logger.debug({ agent }, '╰─Agent details');
|
|
62
|
+
}
|
|
63
|
+
// Determine model/provider with precedence: options → agent → defaults
|
|
64
|
+
const activeProvider = provider || agent.provider;
|
|
65
|
+
const activeModel = model || agent.model;
|
|
66
|
+
let activeLLMProvider;
|
|
67
|
+
if (activeProvider && activeModel) {
|
|
68
|
+
this.logger.info({ requestedProvider: activeProvider, requestedModel: activeModel }, 'Creating custom LLM provider');
|
|
69
|
+
activeLLMProvider = createLLMProvider({ provider: activeProvider, model: activeModel });
|
|
70
|
+
const validationResult = await activeLLMProvider.validateToolCallSupport(activeModel);
|
|
71
|
+
if (!validationResult.supported) {
|
|
72
|
+
this.logger.warn({ model: activeModel, reason: validationResult.message }, 'Model does not support tool calling');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
activeLLMProvider = this.llmProvider;
|
|
77
|
+
this.logger.debug('Using default LLM provider');
|
|
78
|
+
}
|
|
79
|
+
const toolDefinitions = this.toolRegistry.getAllDefinitions();
|
|
80
|
+
const systemPrompt = agent.systemPrompt
|
|
81
|
+
.replace(/\{\{tools\}\}/g, JSON.stringify(toolDefinitions))
|
|
82
|
+
.replace(/\{\{currentDate\}\}/g, new Date().toLocaleString());
|
|
83
|
+
if (systemPrompt.length < 100) {
|
|
84
|
+
this.logger.warn('System prompt is empty after replacement');
|
|
85
|
+
throw new ValidationError('System prompt seems short! ', 'systemPrompt', systemPrompt);
|
|
86
|
+
}
|
|
87
|
+
let currentGoalText = goalText.replace(/\{\{currentDate\}\}/g, new Date().toLocaleString());
|
|
88
|
+
let attempt = 0;
|
|
89
|
+
const maxAttempts = 3;
|
|
90
|
+
const planningAttempts = [];
|
|
91
|
+
const planningUsageTotal = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
92
|
+
let planningCostTotal = 0;
|
|
93
|
+
let retryReason = 'initial';
|
|
94
|
+
while (attempt < maxAttempts) {
|
|
95
|
+
attempt++;
|
|
96
|
+
this.logger.info(`attempt number ${attempt}`);
|
|
97
|
+
//this.logger.info({ attempt, agentName, goalText: showGoalText }, 'Creating DAG with LLM inference');
|
|
98
|
+
//this.logger.info({ temperature,maxTokens, systemPromptPreview: systemPrompt.slice(0,80) }, 'System prompt preview');
|
|
99
|
+
const response = await activeLLMProvider.chat({
|
|
100
|
+
messages: [
|
|
101
|
+
{ role: 'system', content: systemPrompt },
|
|
102
|
+
{ role: 'user', content: currentGoalText },
|
|
103
|
+
],
|
|
104
|
+
temperature,
|
|
105
|
+
maxTokens,
|
|
106
|
+
});
|
|
107
|
+
const attemptUsage = response.usage;
|
|
108
|
+
const attemptCost = response.costUsd;
|
|
109
|
+
const attemptGenStats = response.generationStats;
|
|
110
|
+
if (attemptUsage) {
|
|
111
|
+
planningUsageTotal.promptTokens += attemptUsage.promptTokens ?? 0;
|
|
112
|
+
planningUsageTotal.completionTokens += attemptUsage.completionTokens ?? 0;
|
|
113
|
+
planningUsageTotal.totalTokens += attemptUsage.totalTokens ?? 0;
|
|
114
|
+
}
|
|
115
|
+
if (attemptCost != null) {
|
|
116
|
+
planningCostTotal += attemptCost;
|
|
117
|
+
}
|
|
118
|
+
const MAX_RESPONSE_SIZE = 100_000;
|
|
119
|
+
if (response.content.length > MAX_RESPONSE_SIZE) {
|
|
120
|
+
this.logger.error({ responseSize: response.content.length }, 'LLM response exceeds size limit');
|
|
121
|
+
throw new ValidationError(`Response too large: ${response.content.length} bytes (max: ${MAX_RESPONSE_SIZE})`, 'response', response.content.length);
|
|
122
|
+
}
|
|
123
|
+
let result;
|
|
124
|
+
try {
|
|
125
|
+
result = extractCodeBlock(response.content);
|
|
126
|
+
}
|
|
127
|
+
catch (parseError) {
|
|
128
|
+
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
|
|
129
|
+
planningAttempts.push({
|
|
130
|
+
attempt,
|
|
131
|
+
reason: retryReason,
|
|
132
|
+
usage: attemptUsage,
|
|
133
|
+
costUsd: attemptCost,
|
|
134
|
+
errorMessage,
|
|
135
|
+
generationStats: attemptGenStats,
|
|
136
|
+
});
|
|
137
|
+
this.logger.error({ err: parseError, attempt, responsePreview: response.content.slice(0, 80) }, 'Failed to parse LLM response as JSON');
|
|
138
|
+
if (attempt >= maxAttempts) {
|
|
139
|
+
throw new ValidationError(`LLM response is not valid JSON after ${attempt} attempts`, 'response', response.content.slice(0, 500));
|
|
140
|
+
}
|
|
141
|
+
retryReason = 'retry_parse_error';
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const usage = response.usage ?? null;
|
|
145
|
+
const generationStats = response.generationStats ?? null;
|
|
146
|
+
const validatedResult = DecomposerJobSchema.safeParse(result);
|
|
147
|
+
if (!validatedResult.success) {
|
|
148
|
+
planningAttempts.push({
|
|
149
|
+
attempt,
|
|
150
|
+
reason: retryReason,
|
|
151
|
+
usage: attemptUsage,
|
|
152
|
+
costUsd: attemptCost,
|
|
153
|
+
errorMessage: JSON.stringify(validatedResult.error.issues),
|
|
154
|
+
generationStats: attemptGenStats,
|
|
155
|
+
});
|
|
156
|
+
this.logger.error({ errors: validatedResult.error.issues, attempt }, 'DAG validation failed');
|
|
157
|
+
if (attempt >= maxAttempts) {
|
|
158
|
+
throw new ValidationError(`Invalid DAG structure after ${attempt} attempts`, 'result', JSON.stringify(validatedResult.error.issues));
|
|
159
|
+
}
|
|
160
|
+
retryReason = 'retry_validation';
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
planningAttempts.push({
|
|
164
|
+
attempt,
|
|
165
|
+
reason: retryReason,
|
|
166
|
+
usage: attemptUsage,
|
|
167
|
+
costUsd: attemptCost,
|
|
168
|
+
generationStats: attemptGenStats,
|
|
169
|
+
});
|
|
170
|
+
let dag = validatedResult.data;
|
|
171
|
+
if (dag.clarification_needed) {
|
|
172
|
+
this.logger.info({ clarificationQuery: dag.clarification_query }, 'Clarification required');
|
|
173
|
+
return {
|
|
174
|
+
status: 'clarification_required',
|
|
175
|
+
clarificationQuery: dag.clarification_query || '',
|
|
176
|
+
result: dag,
|
|
177
|
+
usage,
|
|
178
|
+
generationStats,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (dag.validation.coverage === 'high') {
|
|
182
|
+
dag = renumberSubTasks(dag);
|
|
183
|
+
dag.original_request = goalText;
|
|
184
|
+
const dagId = generateDAGId();
|
|
185
|
+
const now = new Date();
|
|
186
|
+
this.logger.info({ dagId }, "DagID Generated");
|
|
187
|
+
// Run TitleMaster generation in parallel with preparing insert data
|
|
188
|
+
const titleMasterPromise = this.generateTitleAsync(activeLLMProvider, goalText);
|
|
189
|
+
// Prepare base insert data (doesn't need title yet)
|
|
190
|
+
const baseInsertData = {
|
|
191
|
+
id: dagId,
|
|
192
|
+
status: 'success',
|
|
193
|
+
result: dag,
|
|
194
|
+
usage: usage,
|
|
195
|
+
generationStats: generationStats,
|
|
196
|
+
attempts: attempt,
|
|
197
|
+
agentName,
|
|
198
|
+
dagTitle: null,
|
|
199
|
+
cronSchedule: cronSchedule || null,
|
|
200
|
+
scheduleActive,
|
|
201
|
+
timezone,
|
|
202
|
+
params: {
|
|
203
|
+
goalText,
|
|
204
|
+
agentName,
|
|
205
|
+
provider,
|
|
206
|
+
model,
|
|
207
|
+
temperature,
|
|
208
|
+
max_tokens: maxTokens,
|
|
209
|
+
seed,
|
|
210
|
+
},
|
|
211
|
+
planningTotalUsage: planningUsageTotal,
|
|
212
|
+
planningTotalCostUsd: planningCostTotal.toString(),
|
|
213
|
+
planningAttempts,
|
|
214
|
+
createdAt: now,
|
|
215
|
+
updatedAt: now,
|
|
216
|
+
};
|
|
217
|
+
// Wait for title generation (runs in parallel with data prep above)
|
|
218
|
+
const titleResult = await titleMasterPromise;
|
|
219
|
+
if (titleResult) {
|
|
220
|
+
this.logger.info('TitleMaster generated Result');
|
|
221
|
+
baseInsertData.dagTitle = titleResult.title;
|
|
222
|
+
planningAttempts.push({
|
|
223
|
+
attempt,
|
|
224
|
+
reason: 'title_master',
|
|
225
|
+
usage: titleResult.usage,
|
|
226
|
+
costUsd: titleResult.costUsd,
|
|
227
|
+
generationStats: titleResult.generationStats,
|
|
228
|
+
});
|
|
229
|
+
if (titleResult.usage) {
|
|
230
|
+
planningUsageTotal.promptTokens += titleResult.usage.promptTokens ?? 0;
|
|
231
|
+
planningUsageTotal.completionTokens += titleResult.usage.completionTokens ?? 0;
|
|
232
|
+
planningUsageTotal.totalTokens += titleResult.usage.totalTokens ?? 0;
|
|
233
|
+
}
|
|
234
|
+
if (titleResult.costUsd != null) {
|
|
235
|
+
planningCostTotal += titleResult.costUsd;
|
|
236
|
+
}
|
|
237
|
+
// Update the totals in insert data
|
|
238
|
+
baseInsertData.planningTotalUsage = planningUsageTotal;
|
|
239
|
+
baseInsertData.planningTotalCostUsd = planningCostTotal.toString();
|
|
240
|
+
}
|
|
241
|
+
this.logger.info('Base insert data prepared');
|
|
242
|
+
try {
|
|
243
|
+
await this.db.insert(dags).values(baseInsertData);
|
|
244
|
+
}
|
|
245
|
+
catch (dbError) {
|
|
246
|
+
const errorMessage = dbError instanceof Error ? dbError.message : String(dbError);
|
|
247
|
+
this.logger.error({ err: dbError, dagId, goalText: showGoalText }, 'Failed to insert DAG into database');
|
|
248
|
+
throw new Error(`Database insert failed for DAG ${dagId}: ${errorMessage}`);
|
|
249
|
+
}
|
|
250
|
+
this.logger.info({
|
|
251
|
+
dagId,
|
|
252
|
+
agentName,
|
|
253
|
+
goalText: showGoalText,
|
|
254
|
+
cronSchedule,
|
|
255
|
+
scheduleActive,
|
|
256
|
+
planningCost: planningCostTotal,
|
|
257
|
+
}, 'DAG saved to database');
|
|
258
|
+
if (this.scheduler && cronSchedule && scheduleActive) {
|
|
259
|
+
this.scheduler.registerDAGSchedule({
|
|
260
|
+
id: dagId,
|
|
261
|
+
cronSchedule,
|
|
262
|
+
scheduleActive,
|
|
263
|
+
timezone,
|
|
264
|
+
});
|
|
265
|
+
this.logger.info({ dagId, cronSchedule, timezone }, 'DAG schedule registered');
|
|
266
|
+
}
|
|
267
|
+
return { status: 'success', dagId };
|
|
268
|
+
}
|
|
269
|
+
if (dag.validation.gaps && dag.validation.gaps.length > 0) {
|
|
270
|
+
const gapsText = dag.validation.gaps.map((gap, idx) => `${idx + 1}. ${gap}`).join('\n');
|
|
271
|
+
currentGoalText = `${goalText}\n\nEnsure following gaps are covered:\n${gapsText}`;
|
|
272
|
+
this.logger.info({ gaps: dag.validation.gaps, attempt }, 'Retrying with gaps addressed');
|
|
273
|
+
retryReason = 'retry_gaps';
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
status: 'success',
|
|
278
|
+
result: dag,
|
|
279
|
+
usage,
|
|
280
|
+
generationStats,
|
|
281
|
+
attempts: attempt,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
throw new ValidationError(`Failed to create DAG after ${maxAttempts} attempts`, 'attempts', maxAttempts);
|
|
285
|
+
}
|
|
286
|
+
async createAndExecuteFromGoal(options) {
|
|
287
|
+
const planningResult = await this.createFromGoal(options);
|
|
288
|
+
if (planningResult.status === 'clarification_required') {
|
|
289
|
+
throw new ValidationError(`Clarification required: ${planningResult.clarificationQuery}`, 'clarification', planningResult.clarificationQuery);
|
|
290
|
+
}
|
|
291
|
+
if (planningResult.status === 'success' && 'dagId' in planningResult) {
|
|
292
|
+
const executionResult = await this.execute(planningResult.dagId);
|
|
293
|
+
return {
|
|
294
|
+
dagId: planningResult.dagId,
|
|
295
|
+
executionId: executionResult.id,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const result = planningResult.result;
|
|
299
|
+
const executionResult = await this.executeDefinition({
|
|
300
|
+
definition: result,
|
|
301
|
+
originalGoalText: options.goalText,
|
|
302
|
+
});
|
|
303
|
+
return { executionId: executionResult.id };
|
|
304
|
+
}
|
|
305
|
+
async execute(dagId, _options) {
|
|
306
|
+
const [dagRecord] = await this.db.select().from(dags).where(eq(dags.id, dagId)).limit(1);
|
|
307
|
+
if (!dagRecord) {
|
|
308
|
+
throw new NotFoundError('DAG', dagId);
|
|
309
|
+
}
|
|
310
|
+
this.logger.info({ dagId }, 'Retrieved DAG for execution');
|
|
311
|
+
let resultStr = JSON.stringify(dagRecord.result)
|
|
312
|
+
.replace(/\{\{currentDate\}\}/g, new Date().toLocaleString())
|
|
313
|
+
.replace(/\{\{Today\}\}/gi, new Date().toLocaleString());
|
|
314
|
+
const job = DecomposerJobSchema.parse(JSON.parse(resultStr));
|
|
315
|
+
if (job.clarification_needed) {
|
|
316
|
+
throw new ValidationError(`Clarification required: ${job.clarification_query}`, 'clarification', job.clarification_query);
|
|
317
|
+
}
|
|
318
|
+
const executionId = generateDAGExecutionId();
|
|
319
|
+
const originalGoalText = dagRecord.params?.goalText || job.original_request;
|
|
320
|
+
const now = new Date();
|
|
321
|
+
await this.db.insert(dagExecutions).values({
|
|
322
|
+
id: executionId,
|
|
323
|
+
dagId: dagId,
|
|
324
|
+
originalRequest: originalGoalText,
|
|
325
|
+
primaryIntent: job.intent.primary,
|
|
326
|
+
status: 'pending',
|
|
327
|
+
totalTasks: job.sub_tasks.length,
|
|
328
|
+
completedTasks: 0,
|
|
329
|
+
failedTasks: 0,
|
|
330
|
+
waitingTasks: 0,
|
|
331
|
+
retryCount: 0,
|
|
332
|
+
createdAt: now,
|
|
333
|
+
updatedAt: now,
|
|
334
|
+
});
|
|
335
|
+
await this.db.insert(dagSubSteps).values(job.sub_tasks.map((task) => ({
|
|
336
|
+
id: generateSubStepId(),
|
|
337
|
+
executionId: executionId,
|
|
338
|
+
taskId: task.id,
|
|
339
|
+
description: task.description,
|
|
340
|
+
thought: task.thought,
|
|
341
|
+
actionType: task.action_type,
|
|
342
|
+
toolOrPromptName: task.tool_or_prompt.name,
|
|
343
|
+
toolOrPromptParams: task.tool_or_prompt.params || {},
|
|
344
|
+
dependencies: task.dependencies,
|
|
345
|
+
status: 'pending',
|
|
346
|
+
createdAt: now,
|
|
347
|
+
updatedAt: now,
|
|
348
|
+
})));
|
|
349
|
+
this.logger.info({
|
|
350
|
+
executionId,
|
|
351
|
+
dagId,
|
|
352
|
+
primaryIntent: job.intent.primary,
|
|
353
|
+
totalTasks: job.sub_tasks.length,
|
|
354
|
+
}, 'DAG execution records created');
|
|
355
|
+
// Execute the DAG asynchronously (fire and forget)
|
|
356
|
+
const dagExecutor = new DAGExecutor({
|
|
357
|
+
db: this.db,
|
|
358
|
+
llmProvider: this.llmProvider,
|
|
359
|
+
toolRegistry: this.toolRegistry,
|
|
360
|
+
artifactsDir: this.artifactsDir,
|
|
361
|
+
});
|
|
362
|
+
// Start execution in background - don't await
|
|
363
|
+
dagExecutor.execute(job, executionId, dagId, originalGoalText, _options?.executionConfig).catch((error) => {
|
|
364
|
+
this.logger.error({ err: error, executionId }, 'DAG execution failed');
|
|
365
|
+
});
|
|
366
|
+
return { id: executionId, status: 'pending' };
|
|
367
|
+
}
|
|
368
|
+
// @TODO ask Oracle why is this function required
|
|
369
|
+
async executeDefinition(options) {
|
|
370
|
+
const { definition: job, originalGoalText, executionConfig } = options;
|
|
371
|
+
if (job.clarification_needed) {
|
|
372
|
+
throw new ValidationError(`Clarification required: ${job.clarification_query}`, 'clarification', job.clarification_query);
|
|
373
|
+
}
|
|
374
|
+
const executionId = generateDAGExecutionId();
|
|
375
|
+
const now = new Date();
|
|
376
|
+
await this.db.insert(dagExecutions).values({
|
|
377
|
+
id: executionId,
|
|
378
|
+
dagId: null,
|
|
379
|
+
originalRequest: originalGoalText,
|
|
380
|
+
primaryIntent: job.intent.primary,
|
|
381
|
+
status: 'pending',
|
|
382
|
+
totalTasks: job.sub_tasks.length,
|
|
383
|
+
completedTasks: 0,
|
|
384
|
+
failedTasks: 0,
|
|
385
|
+
waitingTasks: 0,
|
|
386
|
+
retryCount: 0,
|
|
387
|
+
createdAt: now,
|
|
388
|
+
updatedAt: now,
|
|
389
|
+
});
|
|
390
|
+
await this.db.insert(dagSubSteps).values(job.sub_tasks.map((task) => ({
|
|
391
|
+
id: generateSubStepId(),
|
|
392
|
+
executionId: executionId,
|
|
393
|
+
taskId: task.id,
|
|
394
|
+
description: task.description,
|
|
395
|
+
thought: task.thought,
|
|
396
|
+
actionType: task.action_type,
|
|
397
|
+
toolOrPromptName: task.tool_or_prompt.name,
|
|
398
|
+
toolOrPromptParams: task.tool_or_prompt.params || {},
|
|
399
|
+
dependencies: task.dependencies,
|
|
400
|
+
status: 'pending',
|
|
401
|
+
createdAt: now,
|
|
402
|
+
updatedAt: now,
|
|
403
|
+
})));
|
|
404
|
+
this.logger.info({
|
|
405
|
+
executionId,
|
|
406
|
+
primaryIntent: job.intent.primary,
|
|
407
|
+
totalTasks: job.sub_tasks.length,
|
|
408
|
+
}, 'Ad-hoc DAG execution records created');
|
|
409
|
+
// Execute the DAG asynchronously (fire and forget)
|
|
410
|
+
const dagExecutor = new DAGExecutor({
|
|
411
|
+
db: this.db,
|
|
412
|
+
llmProvider: this.llmProvider,
|
|
413
|
+
toolRegistry: this.toolRegistry,
|
|
414
|
+
artifactsDir: this.artifactsDir,
|
|
415
|
+
});
|
|
416
|
+
// Start execution in background - don't await
|
|
417
|
+
dagExecutor.execute(job, executionId, undefined, originalGoalText, executionConfig).catch((error) => {
|
|
418
|
+
this.logger.error({ err: error, executionId }, 'DAG execution failed');
|
|
419
|
+
});
|
|
420
|
+
return { id: executionId, status: 'pending' };
|
|
421
|
+
}
|
|
422
|
+
async resume(executionId, executionConfig) {
|
|
423
|
+
const [execution] = await this.db.select().from(dagExecutions).where(eq(dagExecutions.id, executionId)).limit(1);
|
|
424
|
+
if (!execution) {
|
|
425
|
+
throw new NotFoundError('DAG Execution', executionId);
|
|
426
|
+
}
|
|
427
|
+
if (!['suspended', 'failed'].includes(execution.status)) {
|
|
428
|
+
throw new ValidationError(`Cannot resume execution with status '${execution.status}'. Only 'suspended' or 'failed' executions can be resumed.`, 'status', execution.status);
|
|
429
|
+
}
|
|
430
|
+
if (!execution.dagId) {
|
|
431
|
+
throw new ValidationError('Execution has no associated DAG. Cannot resume.', 'dagId', null);
|
|
432
|
+
}
|
|
433
|
+
const [dagRecord] = await this.db.select().from(dags).where(eq(dags.id, execution.dagId)).limit(1);
|
|
434
|
+
if (!dagRecord) {
|
|
435
|
+
throw new NotFoundError('DAG', execution.dagId);
|
|
436
|
+
}
|
|
437
|
+
const newRetryCount = (execution.retryCount || 0) + 1;
|
|
438
|
+
const now = new Date();
|
|
439
|
+
await this.db
|
|
440
|
+
.update(dagExecutions)
|
|
441
|
+
.set({
|
|
442
|
+
lastRetryAt: now,
|
|
443
|
+
retryCount: sql `${dagExecutions.retryCount} + 1`,
|
|
444
|
+
status: 'running',
|
|
445
|
+
updatedAt: now,
|
|
446
|
+
})
|
|
447
|
+
.where(eq(dagExecutions.id, executionId));
|
|
448
|
+
this.logger.info({
|
|
449
|
+
executionId,
|
|
450
|
+
dagId: execution.dagId,
|
|
451
|
+
retryCount: newRetryCount,
|
|
452
|
+
previousStatus: execution.status,
|
|
453
|
+
}, 'Resuming DAG execution');
|
|
454
|
+
// Parse job and execute
|
|
455
|
+
const job = DecomposerJobSchema.parse(dagRecord.result);
|
|
456
|
+
const originalGoalText = dagRecord.params?.goalText || job.original_request;
|
|
457
|
+
const dagExecutor = new DAGExecutor({
|
|
458
|
+
db: this.db,
|
|
459
|
+
llmProvider: this.llmProvider,
|
|
460
|
+
toolRegistry: this.toolRegistry,
|
|
461
|
+
artifactsDir: this.artifactsDir,
|
|
462
|
+
});
|
|
463
|
+
// Start execution in background - don't await
|
|
464
|
+
dagExecutor.execute(job, executionId, execution.dagId, originalGoalText, executionConfig).catch((error) => {
|
|
465
|
+
this.logger.error({ err: error, executionId }, 'DAG resume execution failed');
|
|
466
|
+
});
|
|
467
|
+
return { id: executionId, status: 'running', retryCount: newRetryCount };
|
|
468
|
+
}
|
|
469
|
+
async get(id) {
|
|
470
|
+
const [dag] = await this.db.select().from(dags).where(eq(dags.id, id)).limit(1);
|
|
471
|
+
if (!dag) {
|
|
472
|
+
throw new NotFoundError('DAG', id);
|
|
473
|
+
}
|
|
474
|
+
return this.mapDAG(dag);
|
|
475
|
+
}
|
|
476
|
+
async list(filter) {
|
|
477
|
+
let query = this.db.select().from(dags).orderBy(desc(dags.createdAt));
|
|
478
|
+
if (filter?.status) {
|
|
479
|
+
query = query.where(eq(dags.status, filter.status));
|
|
480
|
+
}
|
|
481
|
+
const allDAGs = await query.limit(filter?.limit || 100).offset(filter?.offset || 0);
|
|
482
|
+
return allDAGs.map((d) => this.mapDAG(d));
|
|
483
|
+
}
|
|
484
|
+
async listScheduled() {
|
|
485
|
+
const scheduledDags = await this.db
|
|
486
|
+
.select()
|
|
487
|
+
.from(dags)
|
|
488
|
+
.where(isNotNull(dags.cronSchedule))
|
|
489
|
+
.orderBy(desc(dags.updatedAt));
|
|
490
|
+
return scheduledDags.map((dag) => {
|
|
491
|
+
let scheduleDescription = 'Invalid schedule';
|
|
492
|
+
if (dag.cronSchedule) {
|
|
493
|
+
try {
|
|
494
|
+
scheduleDescription = cronstrue.toString(dag.cronSchedule);
|
|
495
|
+
}
|
|
496
|
+
catch (e) {
|
|
497
|
+
this.logger.warn({ dagId: dag.id, schedule: dag.cronSchedule, err: e }, 'Failed to parse cron schedule');
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
id: dag.id,
|
|
502
|
+
dagTitle: dag.dagTitle,
|
|
503
|
+
cronSchedule: dag.cronSchedule,
|
|
504
|
+
scheduleDescription,
|
|
505
|
+
scheduleActive: dag.scheduleActive,
|
|
506
|
+
};
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
async update(id, updates) {
|
|
510
|
+
const [existing] = await this.db.select().from(dags).where(eq(dags.id, id)).limit(1);
|
|
511
|
+
if (!existing) {
|
|
512
|
+
throw new NotFoundError('DAG', id);
|
|
513
|
+
}
|
|
514
|
+
if (updates.cronSchedule) {
|
|
515
|
+
const validation = validateCronExpression(updates.cronSchedule);
|
|
516
|
+
if (!validation.valid) {
|
|
517
|
+
throw new ValidationError(`Invalid cron expression: ${validation.error}`, 'cronSchedule', updates.cronSchedule);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const updateData = {
|
|
521
|
+
updatedAt: new Date(),
|
|
522
|
+
};
|
|
523
|
+
if (updates.status !== undefined)
|
|
524
|
+
updateData.status = updates.status;
|
|
525
|
+
if (updates.result !== undefined)
|
|
526
|
+
updateData.result = updates.result;
|
|
527
|
+
if (updates.params !== undefined)
|
|
528
|
+
updateData.params = updates.params;
|
|
529
|
+
if (updates.cronSchedule !== undefined)
|
|
530
|
+
updateData.cronSchedule = updates.cronSchedule;
|
|
531
|
+
if (updates.scheduleActive !== undefined)
|
|
532
|
+
updateData.scheduleActive = updates.scheduleActive;
|
|
533
|
+
if (updates.timezone !== undefined)
|
|
534
|
+
updateData.timezone = updates.timezone;
|
|
535
|
+
if (updates.dagTitle !== undefined)
|
|
536
|
+
updateData.dagTitle = updates.dagTitle;
|
|
537
|
+
await this.db.update(dags).set(updateData).where(eq(dags.id, id));
|
|
538
|
+
const [updated] = await this.db.select().from(dags).where(eq(dags.id, id)).limit(1);
|
|
539
|
+
if (!updated) {
|
|
540
|
+
throw new Error('Failed to update DAG');
|
|
541
|
+
}
|
|
542
|
+
if (this.scheduler && (updates.cronSchedule !== undefined || updates.scheduleActive !== undefined)) {
|
|
543
|
+
const finalSchedule = updates.cronSchedule ?? updated.cronSchedule;
|
|
544
|
+
const finalActive = updates.scheduleActive ?? updated.scheduleActive;
|
|
545
|
+
if (finalSchedule && finalActive) {
|
|
546
|
+
this.scheduler.updateDAGSchedule(id, finalSchedule, finalActive);
|
|
547
|
+
this.logger.info({ dagId: id, cronSchedule: finalSchedule, scheduleActive: finalActive }, 'DAG schedule updated');
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
this.scheduler.unregisterDAGSchedule(id);
|
|
551
|
+
this.logger.info({ dagId: id }, 'DAG schedule unregistered');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
this.logger.info({ dagId: id, updates: Object.keys(updates) }, 'DAG updated successfully');
|
|
555
|
+
return this.mapDAG(updated);
|
|
556
|
+
}
|
|
557
|
+
async safeDelete(id) {
|
|
558
|
+
const [existing] = await this.db.select().from(dags).where(eq(dags.id, id)).limit(1);
|
|
559
|
+
if (!existing) {
|
|
560
|
+
throw new NotFoundError('DAG', id);
|
|
561
|
+
}
|
|
562
|
+
const relatedExecutions = await this.db
|
|
563
|
+
.select()
|
|
564
|
+
.from(dagExecutions)
|
|
565
|
+
.where(eq(dagExecutions.dagId, id));
|
|
566
|
+
if (relatedExecutions.length > 0) {
|
|
567
|
+
throw new ValidationError(`Cannot delete DAG: ${relatedExecutions.length} execution(s) exist for this DAG`, 'executions', relatedExecutions.length);
|
|
568
|
+
}
|
|
569
|
+
await this.db.delete(dags).where(eq(dags.id, id));
|
|
570
|
+
if (this.scheduler) {
|
|
571
|
+
this.scheduler.unregisterDAGSchedule(id);
|
|
572
|
+
}
|
|
573
|
+
this.logger.info({ dagId: id }, 'DAG deleted successfully');
|
|
574
|
+
}
|
|
575
|
+
async runExperiments(input) {
|
|
576
|
+
const { goalText, agentName, provider, models, temperatures, seed } = input;
|
|
577
|
+
this.logger.info({
|
|
578
|
+
goalText: truncateForLog(goalText),
|
|
579
|
+
modelsCount: models.length,
|
|
580
|
+
temperaturesCount: temperatures.length,
|
|
581
|
+
totalExperiments: models.length * temperatures.length,
|
|
582
|
+
}, 'Starting DAG experiments');
|
|
583
|
+
const experimentResults = [];
|
|
584
|
+
for (const model of models) {
|
|
585
|
+
for (const temperature of temperatures) {
|
|
586
|
+
let dagId = null;
|
|
587
|
+
let success = false;
|
|
588
|
+
let error;
|
|
589
|
+
try {
|
|
590
|
+
const result = await this.createFromGoal({
|
|
591
|
+
goalText,
|
|
592
|
+
agentName,
|
|
593
|
+
provider,
|
|
594
|
+
model,
|
|
595
|
+
temperature,
|
|
596
|
+
seed,
|
|
597
|
+
});
|
|
598
|
+
if (result.status === 'success' && 'dagId' in result) {
|
|
599
|
+
dagId = result.dagId;
|
|
600
|
+
success = true;
|
|
601
|
+
}
|
|
602
|
+
else if (result.status === 'success' && 'result' in result) {
|
|
603
|
+
const persistedDagId = generateDAGId();
|
|
604
|
+
const unpersistedResult = result;
|
|
605
|
+
const now = new Date();
|
|
606
|
+
await this.db.insert(dags).values({
|
|
607
|
+
id: persistedDagId,
|
|
608
|
+
status: 'success',
|
|
609
|
+
result: unpersistedResult.result,
|
|
610
|
+
usage: unpersistedResult.usage,
|
|
611
|
+
generationStats: unpersistedResult.generationStats,
|
|
612
|
+
attempts: unpersistedResult.attempts,
|
|
613
|
+
params: { goalText, agentName, provider, model, temperature, seed },
|
|
614
|
+
createdAt: now,
|
|
615
|
+
updatedAt: now,
|
|
616
|
+
});
|
|
617
|
+
dagId = persistedDagId;
|
|
618
|
+
success = true;
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
error = 'Clarification required';
|
|
622
|
+
}
|
|
623
|
+
this.logger.info({ dagId, model, temperature }, 'DAG experiment completed');
|
|
624
|
+
}
|
|
625
|
+
catch (experimentError) {
|
|
626
|
+
error = experimentError instanceof Error ? experimentError.message : String(experimentError);
|
|
627
|
+
this.logger.error({ err: experimentError, model, temperature }, 'DAG experiment failed');
|
|
628
|
+
}
|
|
629
|
+
experimentResults.push({ model, temperature, dagId, success, error });
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
const successCount = experimentResults.filter((r) => r.success).length;
|
|
633
|
+
const failureCount = experimentResults.filter((r) => !r.success).length;
|
|
634
|
+
return {
|
|
635
|
+
status: 'completed',
|
|
636
|
+
totalExperiments: experimentResults.length,
|
|
637
|
+
successCount,
|
|
638
|
+
failureCount,
|
|
639
|
+
results: experimentResults,
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
async getSubSteps(executionId) {
|
|
643
|
+
const [execution] = await this.db.select().from(dagExecutions).where(eq(dagExecutions.id, executionId)).limit(1);
|
|
644
|
+
if (!execution) {
|
|
645
|
+
throw new NotFoundError('DAG Execution', executionId);
|
|
646
|
+
}
|
|
647
|
+
const subSteps = await this.db
|
|
648
|
+
.select()
|
|
649
|
+
.from(dagSubSteps)
|
|
650
|
+
.where(eq(dagSubSteps.executionId, executionId))
|
|
651
|
+
.orderBy(dagSubSteps.taskId);
|
|
652
|
+
return subSteps;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Generate DAG title asynchronously using TitleMaster agent
|
|
656
|
+
* Returns null if TitleMaster is not available or fails
|
|
657
|
+
*/
|
|
658
|
+
async generateTitleAsync(llmProvider, goalText) {
|
|
659
|
+
try {
|
|
660
|
+
const titleMasterAgent = await this.agentsService.resolve('TitleMaster');
|
|
661
|
+
if (!titleMasterAgent) {
|
|
662
|
+
this.logger.warn('TitleMaster agent not found or inactive');
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
const titleResponse = await llmProvider.chat({
|
|
666
|
+
messages: [
|
|
667
|
+
{ role: 'system', content: titleMasterAgent.systemPrompt },
|
|
668
|
+
{ role: 'user', content: truncate(goalText) },
|
|
669
|
+
],
|
|
670
|
+
temperature: 0.7,
|
|
671
|
+
maxTokens: 100,
|
|
672
|
+
});
|
|
673
|
+
const title = titleResponse.content.trim();
|
|
674
|
+
this.logger.info({ dagTitle: title }, 'Generated DAG title from TitleMaster');
|
|
675
|
+
return {
|
|
676
|
+
title,
|
|
677
|
+
usage: titleResponse.usage,
|
|
678
|
+
costUsd: titleResponse.costUsd,
|
|
679
|
+
generationStats: titleResponse.generationStats,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
catch (titleError) {
|
|
683
|
+
this.logger.error({ err: titleError }, 'Error calling TitleMaster');
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
mapDAG(record) {
|
|
688
|
+
return {
|
|
689
|
+
id: record.id,
|
|
690
|
+
objective: record.dagTitle || record.params?.goalText || record.params?.objective || '',
|
|
691
|
+
nodes: [],
|
|
692
|
+
edges: [],
|
|
693
|
+
status: record.status,
|
|
694
|
+
createdAt: record.createdAt,
|
|
695
|
+
updatedAt: record.updatedAt,
|
|
696
|
+
metadata: {
|
|
697
|
+
...record.params,
|
|
698
|
+
result: record.result,
|
|
699
|
+
usage: record.usage,
|
|
700
|
+
generationStats: record.generationStats,
|
|
701
|
+
attempts: record.attempts,
|
|
702
|
+
agentName: record.agentName,
|
|
703
|
+
cronSchedule: record.cronSchedule,
|
|
704
|
+
scheduleActive: record.scheduleActive,
|
|
705
|
+
timezone: record.timezone,
|
|
706
|
+
planningTotalUsage: record.planningTotalUsage,
|
|
707
|
+
planningTotalCostUsd: record.planningTotalCostUsd,
|
|
708
|
+
planningAttempts: record.planningAttempts,
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
//# sourceMappingURL=dags.js.map
|