scai 0.1.119 โ 0.1.120
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 +2 -2
- package/dist/agents/MainAgent.js +121 -169
- package/dist/agents/infoPlanGenStep.js +0 -1
- package/dist/agents/planResolverStep.js +0 -1
- package/dist/agents/selectRelevantSourcesStep.js +0 -2
- package/dist/agents/transformPlanGenStep.js +0 -1
- package/dist/lib/generate.js +4 -16
- package/dist/pipeline/modules/writeFileModule.js +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Once in the REPL, you can:
|
|
|
41
41
|
* Ask questions freely about your codebase:
|
|
42
42
|
|
|
43
43
|
```text
|
|
44
|
-
scai> How many functions in
|
|
44
|
+
scai> How many functions in config.js are missing tests?
|
|
45
45
|
scai> Summarize utils/helpers.ts
|
|
46
46
|
scai> Where are all the database queries defined?
|
|
47
47
|
scai> List files involved in authentication
|
|
@@ -161,7 +161,7 @@ scai auth reset
|
|
|
161
161
|
|
|
162
162
|
## ๐ง Example Queries in REPL
|
|
163
163
|
|
|
164
|
-
* `Summarize
|
|
164
|
+
* `Summarize codeTransform.js`
|
|
165
165
|
* `Explain utils/helpers.ts architecture`
|
|
166
166
|
* `List all functions without tests in services/`
|
|
167
167
|
* `Show where database queries are defined`
|
package/dist/agents/MainAgent.js
CHANGED
|
@@ -12,49 +12,69 @@ import { semanticAnalysisStep } from "./semanticAnalysisStep.js";
|
|
|
12
12
|
import { selectRelevantSourcesStep } from "./selectRelevantSourcesStep.js";
|
|
13
13
|
import { transformPlanGenStep } from "./transformPlanGenStep.js";
|
|
14
14
|
import { finalPlanGenStep } from "./finalPlanGenStep.js";
|
|
15
|
-
|
|
15
|
+
import { Spinner } from "../lib/spinner.js";
|
|
16
|
+
/* โโโโโโโโโโโโโโโโโโโโโโโโโ helpers โโโโโโโโโโโโโโโโโโโโโโโโโ */
|
|
17
|
+
function startTimer() {
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
return () => Date.now() - start;
|
|
20
|
+
}
|
|
21
|
+
function logLine(phase, step, ms, desc) {
|
|
22
|
+
// Clear current line (removes leftover spinner)
|
|
23
|
+
process.stdout.write('\r\x1b[K');
|
|
24
|
+
const suffix = desc ? ` โ ${desc}` : "";
|
|
25
|
+
const timing = typeof ms === "number" ? ` (${ms}ms)` : "";
|
|
26
|
+
console.log(`[AGENT] ${phase} :: ${step}${suffix}${timing}`);
|
|
27
|
+
}
|
|
28
|
+
/** Helper to display messages to the user from the agent */
|
|
29
|
+
function userOutput(message) {
|
|
30
|
+
console.log(`[USER OUTPUT] ${message}`);
|
|
31
|
+
}
|
|
32
|
+
/** Helper to display messages with phase context */
|
|
33
|
+
function userPhaseOutput(phase, message) {
|
|
34
|
+
console.log(`[USER OUTPUT] [${phase}] ${message}`);
|
|
35
|
+
}
|
|
36
|
+
/* โโโโโโโโโโโโโโโโโโโโโโโโโ registry โโโโโโโโโโโโโโโโโโโโโโโโโ */
|
|
16
37
|
const MODULE_REGISTRY = Object.fromEntries(Object.entries(builtInModules).map(([name, mod]) => [name, mod]));
|
|
17
|
-
/** Resolve a module from the registry */
|
|
18
38
|
function resolveModuleForAction(action) {
|
|
19
|
-
|
|
20
|
-
if (!mod)
|
|
21
|
-
console.warn(`โ ๏ธ Missing module for action "${action}" โ skipping`);
|
|
22
|
-
return mod;
|
|
39
|
+
return MODULE_REGISTRY[action];
|
|
23
40
|
}
|
|
41
|
+
/* โโโโโโโโโโโโโโโโโโโโโโโโโ agent โโโโโโโโโโโโโโโโโโโโโโโโโ */
|
|
24
42
|
export class MainAgent {
|
|
25
43
|
constructor(context) {
|
|
44
|
+
this.spinner = new Spinner(); // spinner for user feedback
|
|
26
45
|
this.runCount = 0;
|
|
27
46
|
this.maxRuns = 2;
|
|
28
47
|
this.context = context;
|
|
29
|
-
this.query = context.initContext?.userQuery ??
|
|
48
|
+
this.query = context.initContext?.userQuery ?? "";
|
|
30
49
|
}
|
|
31
|
-
|
|
50
|
+
/* โโโโโโโโโโโโโ step executor โโโโโโโโโโโโโ */
|
|
32
51
|
async executeStep(step, input) {
|
|
33
|
-
|
|
34
|
-
console.log(`\n =====================================================================================`);
|
|
35
|
-
console.log(`\nโก Executing step: ${step.action}`);
|
|
36
|
-
if (step.description)
|
|
37
|
-
console.log(`\n --> Description: ${step.description}`);
|
|
38
|
-
console.log(`\n =====================================================================================`);
|
|
39
|
-
// attach the current step to the context
|
|
52
|
+
const stop = startTimer();
|
|
40
53
|
if (input.context) {
|
|
41
|
-
input.context.currentStep = step;
|
|
54
|
+
input.context.currentStep = step;
|
|
42
55
|
}
|
|
43
56
|
const mod = resolveModuleForAction(step.action);
|
|
44
57
|
if (!mod) {
|
|
45
|
-
|
|
58
|
+
logLine("EXECUTE", step.action, stop(), "skipped (missing module)");
|
|
59
|
+
return {
|
|
60
|
+
query: input.query,
|
|
61
|
+
content: input.content,
|
|
62
|
+
data: { skipped: true },
|
|
63
|
+
context: input.context
|
|
64
|
+
};
|
|
46
65
|
}
|
|
47
|
-
const moduleInput = {
|
|
48
|
-
query: step.description ?? input.query,
|
|
49
|
-
content: input.data ?? input.content,
|
|
50
|
-
context: input.context // shared reference
|
|
51
|
-
};
|
|
52
66
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
// Update spinner text for user
|
|
68
|
+
this.spinner.update(`Running step: ${step.action}`);
|
|
69
|
+
const output = await mod.run({
|
|
70
|
+
query: step.description ?? input.query,
|
|
71
|
+
content: input.data ?? input.content,
|
|
72
|
+
context: input.context
|
|
73
|
+
});
|
|
74
|
+
if (!output) {
|
|
75
|
+
throw new Error(`Module "${mod.name}" returned empty output`);
|
|
76
|
+
}
|
|
77
|
+
logLine("EXECUTE", step.action, stop(), step.description);
|
|
58
78
|
return {
|
|
59
79
|
query: step.description ?? input.query,
|
|
60
80
|
data: output.data,
|
|
@@ -62,194 +82,126 @@ export class MainAgent {
|
|
|
62
82
|
};
|
|
63
83
|
}
|
|
64
84
|
catch (err) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
query: input.query,
|
|
68
|
-
content: input.content,
|
|
69
|
-
data: { error: String(err) },
|
|
70
|
-
context: input.context
|
|
71
|
-
};
|
|
85
|
+
logLine("EXECUTE", step.action, stop(), "failed");
|
|
86
|
+
throw err;
|
|
72
87
|
}
|
|
73
88
|
}
|
|
74
|
-
|
|
89
|
+
/* โโโโโโโโโโโโโ main run โโโโโโโโโโโโโ */
|
|
75
90
|
async run() {
|
|
76
|
-
var _a;
|
|
77
91
|
this.runCount++;
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
const stopRun = startTimer();
|
|
93
|
+
logLine("RUN", `start #${this.runCount}`);
|
|
80
94
|
logInputOutput("GlobalContext (structured)", "input", this.context);
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
(_a = this.context).analysis ?? (_a.analysis = {});
|
|
95
|
-
this.context.analysis.folderCapsulesSummary = capsulesSummary;
|
|
96
|
-
const humanReadable = capsulesSummary.map(fc => `- ${fc.path}: ${fc.fileCount} files, depth ${fc.depth}, confidence ${fc.confidence}`).join('\n');
|
|
97
|
-
this.context.analysis.folderCapsulesHuman = humanReadable;
|
|
98
|
-
console.log('\n๐ Folder Capsules Summary (for agent steps):\n', humanReadable);
|
|
99
|
-
}
|
|
100
|
-
// ---------------------------------------------------------------------
|
|
101
|
-
// 0๏ธโฃ UNDERSTAND INTENT โ First-pass interpretation of query
|
|
102
|
-
// ---------------------------------------------------------------------
|
|
103
|
-
console.log("\n๐ง Running understandIntentStep (pre-analysis fixture)\n");
|
|
104
|
-
await understandIntentStep.run({
|
|
105
|
-
context: this.context
|
|
106
|
-
});
|
|
107
|
-
// ---------------------------------------------------------------------
|
|
108
|
-
// 1๏ธโฃ PLAN RESOLVER โ Fast path (context-driven routing)
|
|
109
|
-
// ---------------------------------------------------------------------
|
|
110
|
-
await planResolverStep.run(this.context);
|
|
111
|
-
// --------------------------------------------------
|
|
112
|
-
// ๐ฆ ROUTING DECISION
|
|
113
|
-
// --------------------------------------------------
|
|
95
|
+
this.spinner.start(); // start spinner at beginning
|
|
96
|
+
/* BOOT */
|
|
97
|
+
{
|
|
98
|
+
const t = startTimer();
|
|
99
|
+
await understandIntentStep.run({ context: this.context });
|
|
100
|
+
logLine("BOOT", "understandIntent", t());
|
|
101
|
+
}
|
|
102
|
+
/* ROUTE */
|
|
103
|
+
{
|
|
104
|
+
const t = startTimer();
|
|
105
|
+
await planResolverStep.run(this.context);
|
|
106
|
+
logLine("ROUTE", "planResolver", t());
|
|
107
|
+
}
|
|
114
108
|
const routing = this.context.analysis?.routingDecision;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
if (routing?.decision === "final-answer" && routing.answer) {
|
|
110
|
+
this.spinner.stop();
|
|
111
|
+
logLine("RUN", "complete", stopRun());
|
|
112
|
+
userOutput("All input/output logs can be found at ~/.scai/input_output.log");
|
|
118
113
|
return {
|
|
119
114
|
query: this.query,
|
|
120
115
|
data: {
|
|
121
116
|
finalAnswer: routing.answer,
|
|
122
|
-
source: "planResolver"
|
|
117
|
+
source: "planResolver"
|
|
123
118
|
},
|
|
124
119
|
context: this.context
|
|
125
120
|
};
|
|
126
121
|
}
|
|
127
|
-
// --------------------------------------------------
|
|
128
|
-
// โฉ FALL THROUGH: continue with plan generation
|
|
129
|
-
// --------------------------------------------------
|
|
130
|
-
let textPlan;
|
|
131
|
-
// Use rationale from routing if available (temporary compatibility)
|
|
132
|
-
if (typeof routing?.rationale === 'string' && routing.rationale.trim()) {
|
|
133
|
-
textPlan = routing.rationale;
|
|
134
|
-
}
|
|
135
|
-
// Fallback: use the original query if no plan/rationale is available
|
|
136
|
-
if (!textPlan || textPlan.trim().length === 0) {
|
|
137
|
-
textPlan = this.query;
|
|
138
|
-
}
|
|
139
|
-
// textPlan can now be passed to planGeneratorStep
|
|
140
|
-
// ---------------------------------------------------------------------
|
|
141
|
-
// PRE-FILE-SEARCH CHECK (do we have the files required to continue )
|
|
142
|
-
// ---------------------------------------------------------------------
|
|
143
|
-
await preFileSearchCheckStep(this.context);
|
|
144
|
-
// ---------------------------------------------------------------------
|
|
145
|
-
// 2๏ธโฃ INFO PLAN GENERATION (information-only steps)
|
|
146
|
-
// ---------------------------------------------------------------------
|
|
147
|
-
await infoPlanGen.run(this.context);
|
|
148
|
-
const infoPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
149
|
-
const infoSteps = infoPlan.steps.filter(s => s.groups?.includes("info"));
|
|
150
|
-
console.log("information gathering steps:\n");
|
|
151
|
-
console.dir(infoSteps);
|
|
152
|
-
let stepIO = {
|
|
153
|
-
query: this.query,
|
|
154
|
-
context: this.context
|
|
155
|
-
};
|
|
156
122
|
// =====================================================
|
|
157
123
|
// INFORMATION ACQUISITION PHASE
|
|
158
124
|
// Purpose: gather raw information, no interpretation
|
|
159
125
|
// =====================================================
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
126
|
+
{
|
|
127
|
+
const t = startTimer();
|
|
128
|
+
await preFileSearchCheckStep(this.context);
|
|
129
|
+
logLine("PRECHECK", "preFileSearch", t());
|
|
130
|
+
}
|
|
131
|
+
{
|
|
132
|
+
const t = startTimer();
|
|
133
|
+
await infoPlanGen.run(this.context);
|
|
134
|
+
logLine("PLAN", "infoPlanGen", t());
|
|
135
|
+
}
|
|
136
|
+
const infoPlan = this.context.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
137
|
+
let stepIO = {
|
|
138
|
+
query: this.query,
|
|
139
|
+
context: this.context
|
|
140
|
+
};
|
|
141
|
+
for (const step of infoPlan.steps.filter(s => s.groups?.includes("info"))) {
|
|
163
142
|
stepIO = await this.executeStep(step, stepIO);
|
|
164
|
-
/* if (count === 4) {
|
|
165
|
-
debugContext(this.context, {
|
|
166
|
-
step: "first step",
|
|
167
|
-
note: "searchFiles?"
|
|
168
|
-
});
|
|
169
|
-
} */
|
|
170
143
|
}
|
|
171
144
|
// =====================================================
|
|
172
145
|
// ANALYSIS PHASE
|
|
173
146
|
// Purpose: understand what we have and what is being asked
|
|
174
147
|
// =====================================================
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
console.log("\n๐ง Running validation analysis\n");
|
|
193
|
-
await validationAnalysisStep.run({ query: this.query, context: this.context });
|
|
194
|
-
// =====================================================
|
|
195
|
-
// ROUTING / PLANNING PHASE
|
|
196
|
-
// =====================================================
|
|
148
|
+
{
|
|
149
|
+
let t = startTimer();
|
|
150
|
+
await selectRelevantSourcesStep.run({ query: this.query, context: this.context });
|
|
151
|
+
logLine("ANALYSIS", "selectRelevantSources", t());
|
|
152
|
+
t = startTimer();
|
|
153
|
+
await structuralAnalysisStep.run({ query: this.query, context: this.context });
|
|
154
|
+
logLine("ANALYSIS", "structuralAnalysis", t());
|
|
155
|
+
t = startTimer();
|
|
156
|
+
await semanticAnalysisStep.run({ query: this.query, context: this.context });
|
|
157
|
+
logLine("ANALYSIS", "semanticAnalysis", t());
|
|
158
|
+
t = startTimer();
|
|
159
|
+
await planTargetFilesStep.run({ query: this.query, context: this.context });
|
|
160
|
+
logLine("ANALYSIS", "planTargetFiles", t());
|
|
161
|
+
t = startTimer();
|
|
162
|
+
await validationAnalysisStep.run({ query: this.query, context: this.context });
|
|
163
|
+
logLine("VALIDATE", "validationAnalysis", t());
|
|
164
|
+
}
|
|
197
165
|
const review = await contextReviewStep(this.context);
|
|
198
166
|
if (review.decision === "loopAgain" && this.runCount < this.maxRuns) {
|
|
199
|
-
console.log("๐ Looping for additional context");
|
|
200
|
-
// Clear noisy bootstrap artifacts before looping
|
|
201
167
|
this.resetInitContextForLoop();
|
|
202
168
|
return this.run();
|
|
203
169
|
}
|
|
204
170
|
// =====================================================
|
|
205
|
-
// TRANSFORM PLAN GENERATION (after context review)
|
|
206
|
-
// Purpose: produce actionable transform steps
|
|
207
|
-
// =====================================================
|
|
208
|
-
console.log("\n๐ ๏ธ Generating transform plan\n");
|
|
209
|
-
await transformPlanGenStep.run(this.context);
|
|
210
|
-
// Filter transform steps
|
|
211
|
-
const transformPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
212
|
-
const transformSteps = transformPlan.steps.filter(s => s.groups?.includes("transform"));
|
|
213
|
-
console.log("transform steps:\n");
|
|
214
|
-
console.dir(transformSteps);
|
|
215
|
-
// =====================================================
|
|
216
171
|
// TRANSFORM PHASE
|
|
217
172
|
// Purpose: produce concrete changes or artifacts
|
|
218
173
|
// =====================================================
|
|
219
|
-
|
|
220
|
-
|
|
174
|
+
{
|
|
175
|
+
const t = startTimer();
|
|
176
|
+
await transformPlanGenStep.run(this.context);
|
|
177
|
+
logLine("PLAN", "transformPlanGen", t());
|
|
178
|
+
}
|
|
179
|
+
const transformPlan = this.context.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
180
|
+
for (const step of transformPlan.steps.filter(s => s.groups?.includes("transform"))) {
|
|
221
181
|
stepIO = await this.executeStep(step, stepIO);
|
|
222
182
|
}
|
|
223
183
|
// =====================================================
|
|
224
|
-
// FINAL PLAN GENERATION
|
|
225
|
-
// Purpose: produce finalize / commit steps
|
|
226
|
-
// =====================================================
|
|
227
|
-
console.log("\nโ
Generating final plan\n");
|
|
228
|
-
await finalPlanGenStep.run(this.context);
|
|
229
|
-
// Filter finalize steps
|
|
230
|
-
const finalPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
231
|
-
const finalizeSteps = finalPlan.steps.filter(s => s.groups?.includes("finalize"));
|
|
232
|
-
console.log("finalize steps:\n");
|
|
233
|
-
console.dir(finalizeSteps);
|
|
234
|
-
// =====================================================
|
|
235
184
|
// FINALIZE PHASE
|
|
236
185
|
// Purpose: commit results and respond to the user
|
|
237
186
|
// =====================================================
|
|
238
|
-
|
|
187
|
+
{
|
|
188
|
+
const t = startTimer();
|
|
189
|
+
await finalPlanGenStep.run(this.context);
|
|
190
|
+
logLine("PLAN", "finalPlanGen", t());
|
|
191
|
+
}
|
|
192
|
+
const finalPlan = this.context.analysis?.planSuggestion?.plan ?? { steps: [] };
|
|
193
|
+
for (const step of finalPlan.steps.filter(s => s.groups?.includes("finalize"))) {
|
|
239
194
|
stepIO = await this.executeStep(step, stepIO);
|
|
240
195
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
196
|
+
this.spinner.stop(); // stop spinner at the end
|
|
197
|
+
userOutput("All input/output logs can be found at ~/.scai/input_output.log");
|
|
198
|
+
logLine("RUN", "complete", stopRun());
|
|
244
199
|
return stepIO;
|
|
245
200
|
}
|
|
246
|
-
|
|
247
|
-
// Helper: reset initContext for clean loop
|
|
248
|
-
// =====================================================
|
|
201
|
+
/* โโโโโโโโโโโโโ helpers โโโโโโโโโโโโโ */
|
|
249
202
|
resetInitContextForLoop() {
|
|
250
203
|
if (this.context.initContext) {
|
|
251
204
|
this.context.initContext.relatedFiles = [];
|
|
252
|
-
console.log(" ๐งน Cleared initContext.relatedFiles for clean loop");
|
|
253
205
|
}
|
|
254
206
|
}
|
|
255
207
|
}
|
|
@@ -36,7 +36,6 @@ export const infoPlanGen = {
|
|
|
36
36
|
// --------------------------------------------------
|
|
37
37
|
const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('info'));
|
|
38
38
|
const actionsJson = JSON.stringify(effectiveActions, null, 2);
|
|
39
|
-
console.log('Actions: ', actionsJson);
|
|
40
39
|
const intentText = context.analysis.intent?.normalizedQuery ??
|
|
41
40
|
context.initContext?.userQuery ??
|
|
42
41
|
'';
|
|
@@ -45,7 +45,6 @@ PLAN:
|
|
|
45
45
|
content: prompt,
|
|
46
46
|
};
|
|
47
47
|
const genOutput = await generate(genInput);
|
|
48
|
-
console.log('PlanResolverOutput: ', genInput.data);
|
|
49
48
|
const raw = String(genOutput.data ?? '').trim();
|
|
50
49
|
logInputOutput('planResolver raw', 'output', raw);
|
|
51
50
|
const isResolved = raw.startsWith('RESOLVED:');
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// File: src/modules/selectRelevantSourcesStep.ts
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import chalk from "chalk";
|
|
4
3
|
import { generate } from "../lib/generate.js";
|
|
5
4
|
import { cleanupModule } from "../pipeline/modules/cleanupModule.js";
|
|
6
5
|
import { logInputOutput } from "../utils/promptLogHelper.js";
|
|
@@ -14,7 +13,6 @@ export const selectRelevantSourcesStep = {
|
|
|
14
13
|
if (!context) {
|
|
15
14
|
throw new Error("[selectRelevantSources] StructuredContext is required.");
|
|
16
15
|
}
|
|
17
|
-
console.log(chalk.blueBright(`๐ [selectRelevantSources] Selecting relevant files for query "${query}"...`));
|
|
18
16
|
// Merge candidate paths (relatedFiles + existing workingFiles)
|
|
19
17
|
// At this point in-depth context build or searchfiles may have placed files in
|
|
20
18
|
// working files.
|
|
@@ -66,7 +66,6 @@ Return a strictly valid JSON plan:
|
|
|
66
66
|
}
|
|
67
67
|
`.trim();
|
|
68
68
|
try {
|
|
69
|
-
console.log('TransformPlanGenStep prompt', prompt);
|
|
70
69
|
const genInput = { query: intentText, content: prompt };
|
|
71
70
|
const genOutput = await generate(genInput);
|
|
72
71
|
const raw = typeof genOutput.data === 'string'
|
package/dist/lib/generate.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// File: lib/generate.ts
|
|
2
|
-
import { Spinner } from './spinner.js';
|
|
3
2
|
import { Config, readConfig } from '../config.js';
|
|
4
3
|
import { startModelProcess } from '../utils/checkModel.js';
|
|
5
4
|
/**
|
|
@@ -18,20 +17,18 @@ export async function generate(input) {
|
|
|
18
17
|
: JSON.stringify(input.content, null, 2))
|
|
19
18
|
: "";
|
|
20
19
|
const prompt = `${queryPart}${contentPart}`.trim();
|
|
21
|
-
const spinner = new Spinner();
|
|
22
|
-
spinner.start();
|
|
23
20
|
try {
|
|
24
|
-
const data = await doGenerate(prompt, model, contextLength,
|
|
21
|
+
const data = await doGenerate(prompt, model, contextLength, false);
|
|
25
22
|
return { query: input.query, data };
|
|
26
23
|
}
|
|
27
24
|
catch {
|
|
28
|
-
|
|
25
|
+
// Attempt model restart if first generation fails
|
|
29
26
|
await startModelProcess();
|
|
30
|
-
const data = await doGenerate(prompt, model, contextLength,
|
|
27
|
+
const data = await doGenerate(prompt, model, contextLength, true);
|
|
31
28
|
return { query: input.query, data };
|
|
32
29
|
}
|
|
33
30
|
}
|
|
34
|
-
async function doGenerate(prompt, model, contextLength,
|
|
31
|
+
async function doGenerate(prompt, model, contextLength, retrying) {
|
|
35
32
|
const res = await fetch('http://localhost:11434/api/generate', {
|
|
36
33
|
method: 'POST',
|
|
37
34
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -48,14 +45,5 @@ async function doGenerate(prompt, model, contextLength, spinner, retrying) {
|
|
|
48
45
|
throw new Error(`Model request failed with status ${res.status}`);
|
|
49
46
|
}
|
|
50
47
|
const data = await res.json();
|
|
51
|
-
if (retrying) {
|
|
52
|
-
spinner.succeed('Model response received after restart.');
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
spinner.succeed();
|
|
56
|
-
}
|
|
57
|
-
process.stdout.write('\n');
|
|
58
|
-
return data.response?.trim() ?? '';
|
|
59
|
-
process.stdout.write('\n');
|
|
60
48
|
return data.response?.trim() ?? '';
|
|
61
49
|
}
|