noterai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -0
- package/bin/noter.js +218 -0
- package/bin/noter.js.map +1 -0
- package/package.json +73 -0
- package/packages/client/dist/assets/geist-mono-latin-400-normal-CoULgQGM.woff +0 -0
- package/packages/client/dist/assets/geist-mono-latin-400-normal-LC9RFr9I.woff2 +0 -0
- package/packages/client/dist/assets/index-CBiNAMaA.js +264 -0
- package/packages/client/dist/assets/index-u7i5Fle2.css +1 -0
- package/packages/client/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
- package/packages/client/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
- package/packages/client/dist/assets/space-grotesk-latin-600-normal-BflQw4A9.woff +0 -0
- package/packages/client/dist/assets/space-grotesk-latin-600-normal-DjKNqYRj.woff2 +0 -0
- package/packages/client/dist/assets/vendor-react-DHeZC_T5.js +17 -0
- package/packages/client/dist/index.html +14 -0
- package/packages/server/dist/adapters/claudeAdapter.d.ts +34 -0
- package/packages/server/dist/adapters/claudeAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/claudeAdapter.js +281 -0
- package/packages/server/dist/adapters/claudeAdapter.js.map +1 -0
- package/packages/server/dist/adapters/codexAdapter.d.ts +39 -0
- package/packages/server/dist/adapters/codexAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/codexAdapter.js +347 -0
- package/packages/server/dist/adapters/codexAdapter.js.map +1 -0
- package/packages/server/dist/adapters/droidAdapter.d.ts +34 -0
- package/packages/server/dist/adapters/droidAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/droidAdapter.js +109 -0
- package/packages/server/dist/adapters/droidAdapter.js.map +1 -0
- package/packages/server/dist/adapters/genericAdapter.d.ts +50 -0
- package/packages/server/dist/adapters/genericAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/genericAdapter.js +203 -0
- package/packages/server/dist/adapters/genericAdapter.js.map +1 -0
- package/packages/server/dist/adapters/index.d.ts +7 -0
- package/packages/server/dist/adapters/index.d.ts.map +1 -0
- package/packages/server/dist/adapters/index.js +7 -0
- package/packages/server/dist/adapters/index.js.map +1 -0
- package/packages/server/dist/adapters/opencodeAdapter.d.ts +28 -0
- package/packages/server/dist/adapters/opencodeAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/opencodeAdapter.js +265 -0
- package/packages/server/dist/adapters/opencodeAdapter.js.map +1 -0
- package/packages/server/dist/adapters/opencodeSessionManager.d.ts +13 -0
- package/packages/server/dist/adapters/opencodeSessionManager.d.ts.map +1 -0
- package/packages/server/dist/adapters/opencodeSessionManager.js +230 -0
- package/packages/server/dist/adapters/opencodeSessionManager.js.map +1 -0
- package/packages/server/dist/adapters/pathUtils.d.ts +54 -0
- package/packages/server/dist/adapters/pathUtils.d.ts.map +1 -0
- package/packages/server/dist/adapters/pathUtils.js +86 -0
- package/packages/server/dist/adapters/pathUtils.js.map +1 -0
- package/packages/server/dist/adapters/piAdapter.d.ts +34 -0
- package/packages/server/dist/adapters/piAdapter.d.ts.map +1 -0
- package/packages/server/dist/adapters/piAdapter.js +307 -0
- package/packages/server/dist/adapters/piAdapter.js.map +1 -0
- package/packages/server/dist/autoDetect.d.ts +15 -0
- package/packages/server/dist/autoDetect.d.ts.map +1 -0
- package/packages/server/dist/autoDetect.js +102 -0
- package/packages/server/dist/autoDetect.js.map +1 -0
- package/packages/server/dist/config.d.ts +29 -0
- package/packages/server/dist/config.d.ts.map +1 -0
- package/packages/server/dist/config.js +96 -0
- package/packages/server/dist/config.js.map +1 -0
- package/packages/server/dist/eventStore.d.ts +14 -0
- package/packages/server/dist/eventStore.d.ts.map +1 -0
- package/packages/server/dist/eventStore.js +35 -0
- package/packages/server/dist/eventStore.js.map +1 -0
- package/packages/server/dist/hookWatcher.d.ts +24 -0
- package/packages/server/dist/hookWatcher.d.ts.map +1 -0
- package/packages/server/dist/hookWatcher.js +152 -0
- package/packages/server/dist/hookWatcher.js.map +1 -0
- package/packages/server/dist/index.d.ts +58 -0
- package/packages/server/dist/index.d.ts.map +1 -0
- package/packages/server/dist/index.js +527 -0
- package/packages/server/dist/index.js.map +1 -0
- package/packages/server/dist/llm/jsonExtractor.d.ts +39 -0
- package/packages/server/dist/llm/jsonExtractor.d.ts.map +1 -0
- package/packages/server/dist/llm/jsonExtractor.js +83 -0
- package/packages/server/dist/llm/jsonExtractor.js.map +1 -0
- package/packages/server/dist/llm/ollamaCache.d.ts +19 -0
- package/packages/server/dist/llm/ollamaCache.d.ts.map +1 -0
- package/packages/server/dist/llm/ollamaCache.js +31 -0
- package/packages/server/dist/llm/ollamaCache.js.map +1 -0
- package/packages/server/dist/llm/ollamaClient.d.ts +29 -0
- package/packages/server/dist/llm/ollamaClient.d.ts.map +1 -0
- package/packages/server/dist/llm/ollamaClient.js +117 -0
- package/packages/server/dist/llm/ollamaClient.js.map +1 -0
- package/packages/server/dist/llm/ollamaQueue.d.ts +7 -0
- package/packages/server/dist/llm/ollamaQueue.d.ts.map +1 -0
- package/packages/server/dist/llm/ollamaQueue.js +28 -0
- package/packages/server/dist/llm/ollamaQueue.js.map +1 -0
- package/packages/server/dist/llm/promptBuilder.d.ts +8 -0
- package/packages/server/dist/llm/promptBuilder.d.ts.map +1 -0
- package/packages/server/dist/llm/promptBuilder.js +85 -0
- package/packages/server/dist/llm/promptBuilder.js.map +1 -0
- package/packages/server/dist/llm/promptSections.d.ts +64 -0
- package/packages/server/dist/llm/promptSections.d.ts.map +1 -0
- package/packages/server/dist/llm/promptSections.js +128 -0
- package/packages/server/dist/llm/promptSections.js.map +1 -0
- package/packages/server/dist/llm/providerModels.d.ts +14 -0
- package/packages/server/dist/llm/providerModels.d.ts.map +1 -0
- package/packages/server/dist/llm/providerModels.js +67 -0
- package/packages/server/dist/llm/providerModels.js.map +1 -0
- package/packages/server/dist/llm/suggester.d.ts +12 -0
- package/packages/server/dist/llm/suggester.d.ts.map +1 -0
- package/packages/server/dist/llm/suggester.js +263 -0
- package/packages/server/dist/llm/suggester.js.map +1 -0
- package/packages/server/dist/llm/summarizer.d.ts +53 -0
- package/packages/server/dist/llm/summarizer.d.ts.map +1 -0
- package/packages/server/dist/llm/summarizer.js +174 -0
- package/packages/server/dist/llm/summarizer.js.map +1 -0
- package/packages/server/dist/persistence.d.ts +42 -0
- package/packages/server/dist/persistence.d.ts.map +1 -0
- package/packages/server/dist/persistence.js +103 -0
- package/packages/server/dist/persistence.js.map +1 -0
- package/packages/server/dist/repoContext.d.ts +60 -0
- package/packages/server/dist/repoContext.d.ts.map +1 -0
- package/packages/server/dist/repoContext.js +197 -0
- package/packages/server/dist/repoContext.js.map +1 -0
- package/packages/server/dist/routes/blueprint.d.ts +7 -0
- package/packages/server/dist/routes/blueprint.d.ts.map +1 -0
- package/packages/server/dist/routes/blueprint.js +529 -0
- package/packages/server/dist/routes/blueprint.js.map +1 -0
- package/packages/server/dist/routes/config.d.ts +17 -0
- package/packages/server/dist/routes/config.d.ts.map +1 -0
- package/packages/server/dist/routes/config.js +282 -0
- package/packages/server/dist/routes/config.js.map +1 -0
- package/packages/server/dist/routes/lmstudio.d.ts +4 -0
- package/packages/server/dist/routes/lmstudio.d.ts.map +1 -0
- package/packages/server/dist/routes/lmstudio.js +67 -0
- package/packages/server/dist/routes/lmstudio.js.map +1 -0
- package/packages/server/dist/routes/notes.d.ts +5 -0
- package/packages/server/dist/routes/notes.d.ts.map +1 -0
- package/packages/server/dist/routes/notes.js +60 -0
- package/packages/server/dist/routes/notes.js.map +1 -0
- package/packages/server/dist/routes/ollama.d.ts +4 -0
- package/packages/server/dist/routes/ollama.d.ts.map +1 -0
- package/packages/server/dist/routes/ollama.js +207 -0
- package/packages/server/dist/routes/ollama.js.map +1 -0
- package/packages/server/dist/routes/prompt.d.ts +4 -0
- package/packages/server/dist/routes/prompt.d.ts.map +1 -0
- package/packages/server/dist/routes/prompt.js +75 -0
- package/packages/server/dist/routes/prompt.js.map +1 -0
- package/packages/server/dist/routes/refineNotes.d.ts +4 -0
- package/packages/server/dist/routes/refineNotes.d.ts.map +1 -0
- package/packages/server/dist/routes/refineNotes.js +87 -0
- package/packages/server/dist/routes/refineNotes.js.map +1 -0
- package/packages/server/dist/routes/repoContext.d.ts +3 -0
- package/packages/server/dist/routes/repoContext.d.ts.map +1 -0
- package/packages/server/dist/routes/repoContext.js +15 -0
- package/packages/server/dist/routes/repoContext.js.map +1 -0
- package/packages/server/dist/routes/sessions.d.ts +15 -0
- package/packages/server/dist/routes/sessions.d.ts.map +1 -0
- package/packages/server/dist/routes/sessions.js +420 -0
- package/packages/server/dist/routes/sessions.js.map +1 -0
- package/packages/server/dist/routes/summarize.d.ts +6 -0
- package/packages/server/dist/routes/summarize.d.ts.map +1 -0
- package/packages/server/dist/routes/summarize.js +85 -0
- package/packages/server/dist/routes/summarize.js.map +1 -0
- package/packages/server/dist/server.d.ts +2 -0
- package/packages/server/dist/server.d.ts.map +1 -0
- package/packages/server/dist/server.js +6 -0
- package/packages/server/dist/server.js.map +1 -0
- package/packages/server/dist/sessionManager.d.ts +41 -0
- package/packages/server/dist/sessionManager.d.ts.map +1 -0
- package/packages/server/dist/sessionManager.js +493 -0
- package/packages/server/dist/sessionManager.js.map +1 -0
- package/packages/server/dist/sessionScanner.d.ts +22 -0
- package/packages/server/dist/sessionScanner.d.ts.map +1 -0
- package/packages/server/dist/sessionScanner.js +165 -0
- package/packages/server/dist/sessionScanner.js.map +1 -0
- package/packages/server/dist/sessionTitle.d.ts +17 -0
- package/packages/server/dist/sessionTitle.d.ts.map +1 -0
- package/packages/server/dist/sessionTitle.js +321 -0
- package/packages/server/dist/sessionTitle.js.map +1 -0
- package/packages/server/dist/stateManager.d.ts +38 -0
- package/packages/server/dist/stateManager.d.ts.map +1 -0
- package/packages/server/dist/stateManager.js +83 -0
- package/packages/server/dist/stateManager.js.map +1 -0
- package/packages/server/dist/wsServer.d.ts +27 -0
- package/packages/server/dist/wsServer.d.ts.map +1 -0
- package/packages/server/dist/wsServer.js +144 -0
- package/packages/server/dist/wsServer.js.map +1 -0
- package/packages/shared/dist/index.d.ts +3 -0
- package/packages/shared/dist/index.d.ts.map +1 -0
- package/packages/shared/dist/index.js +3 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/shared/dist/llm/mambaDetect.d.ts +84 -0
- package/packages/shared/dist/llm/mambaDetect.d.ts.map +1 -0
- package/packages/shared/dist/llm/mambaDetect.js +205 -0
- package/packages/shared/dist/llm/mambaDetect.js.map +1 -0
- package/packages/shared/dist/types.d.ts +184 -0
- package/packages/shared/dist/types.d.ts.map +1 -0
- package/packages/shared/dist/types.js +2 -0
- package/packages/shared/dist/types.js.map +1 -0
- package/scripts/postbuild.js +70 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt to extract JSON from raw model output.
|
|
3
|
+
* Tries multiple strategies in order:
|
|
4
|
+
* 1. Direct JSON.parse
|
|
5
|
+
* 2. Strip markdown code fences (```json ... ```) and retry
|
|
6
|
+
* 3. Regex extraction of first JSON object or array
|
|
7
|
+
* Returns null if no JSON found.
|
|
8
|
+
*/
|
|
9
|
+
export function extractJSON(raw) {
|
|
10
|
+
if (!raw || !raw.trim())
|
|
11
|
+
return null;
|
|
12
|
+
const trimmed = raw.trim();
|
|
13
|
+
// Attempt 1: direct parse
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(trimmed);
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
// Attempt 2: strip markdown code fences
|
|
19
|
+
const stripped = trimmed.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```$/, '').trim();
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(stripped);
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
// Attempt 3: regex extraction of JSON object or array
|
|
25
|
+
const objectMatch = stripped.match(/\{[\s\S]*\}/);
|
|
26
|
+
if (objectMatch) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(objectMatch[0]);
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
}
|
|
32
|
+
const arrayMatch = stripped.match(/\[[\s\S]*\]/);
|
|
33
|
+
if (arrayMatch) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(arrayMatch[0]);
|
|
36
|
+
}
|
|
37
|
+
catch { }
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate extracted data against a Zod schema.
|
|
43
|
+
* Returns the parsed data if valid, null otherwise.
|
|
44
|
+
*/
|
|
45
|
+
export function validateAgainstSchema(data, schema) {
|
|
46
|
+
const result = schema.safeParse(data);
|
|
47
|
+
if (result.success)
|
|
48
|
+
return result.data;
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Enforce JSON response from a model that may produce non-JSON output.
|
|
53
|
+
*
|
|
54
|
+
* Calls modelFn up to maxRetries times. On each attempt:
|
|
55
|
+
* 1. Extracts JSON from the raw response using extractJSON
|
|
56
|
+
* 2. Validates against the provided Zod schema
|
|
57
|
+
* 3. If validation fails, retries with corrective feedback
|
|
58
|
+
*
|
|
59
|
+
* If all attempts fail, throws an error with details.
|
|
60
|
+
*/
|
|
61
|
+
export async function enforceJSONResponse(opts) {
|
|
62
|
+
const { prompt, schema, modelFn, maxRetries = 1 } = opts;
|
|
63
|
+
let lastError = null;
|
|
64
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
65
|
+
const currentPrompt = attempt === 0
|
|
66
|
+
? prompt
|
|
67
|
+
: `${prompt}\n\nYour previous response was not valid JSON. Respond ONLY with valid JSON matching the required schema. Do not include any text outside the JSON object.`;
|
|
68
|
+
const raw = await modelFn(currentPrompt);
|
|
69
|
+
const extracted = extractJSON(raw);
|
|
70
|
+
if (extracted !== null) {
|
|
71
|
+
const validated = validateAgainstSchema(extracted, schema);
|
|
72
|
+
if (validated !== null) {
|
|
73
|
+
return validated;
|
|
74
|
+
}
|
|
75
|
+
lastError = 'Extracted JSON does not match expected schema';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
lastError = 'Could not extract valid JSON from model output';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`enforceJSONResponse failed after ${1 + maxRetries} attempts: ${lastError}`);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=jsonExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonExtractor.js","sourceRoot":"","sources":["../../src/llm/jsonExtractor.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAE3B,0BAA0B;IAC1B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,wCAAwC;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3F,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,sDAAsD;IACtD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAClD,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACjD,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAI,IAAa,EAAE,MAAoB;IAC1E,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAI,IAK5C;IACC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC;IACzD,IAAI,SAAS,GAAkB,IAAI,CAAC;IAEpC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,MAAM,aAAa,GAAG,OAAO,KAAK,CAAC;YACjC,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,GAAG,MAAM,4JAA4J,CAAC;QAE1K,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAEnC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC3D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;gBACvB,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,SAAS,GAAG,+CAA+C,CAAC;QAC9D,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,gDAAgD,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,GAAG,UAAU,cAAc,SAAS,EAAE,CAAC,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type OllamaProvider } from 'ai-sdk-ollama';
|
|
2
|
+
interface OllamaConfig {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Get a cached Ollama provider instance by configuration.
|
|
7
|
+
* Same config returns the same instance; different configs create new instances.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getOllamaProvider(config: OllamaConfig): OllamaProvider;
|
|
10
|
+
/**
|
|
11
|
+
* Clear the Ollama cache (useful for testing).
|
|
12
|
+
*/
|
|
13
|
+
export declare function clearOllamaCache(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get the current number of cached providers (useful for testing).
|
|
16
|
+
*/
|
|
17
|
+
export declare function getCacheSize(): number;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=ollamaCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaCache.d.ts","sourceRoot":"","sources":["../../src/llm/ollamaCache.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAElE,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAStE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Factory function that returns a memoized createOllama instance
|
|
2
|
+
// Used for caching Ollama providers by configuration
|
|
3
|
+
import { createOllama } from 'ai-sdk-ollama';
|
|
4
|
+
// Map to store cached providers by config
|
|
5
|
+
// Using a stringified config as the key for simplicity
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Get a cached Ollama provider instance by configuration.
|
|
9
|
+
* Same config returns the same instance; different configs create new instances.
|
|
10
|
+
*/
|
|
11
|
+
export function getOllamaProvider(config) {
|
|
12
|
+
const cacheKey = JSON.stringify(config);
|
|
13
|
+
if (!cache.has(cacheKey)) {
|
|
14
|
+
const provider = createOllama(config);
|
|
15
|
+
cache.set(cacheKey, provider);
|
|
16
|
+
}
|
|
17
|
+
return cache.get(cacheKey);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clear the Ollama cache (useful for testing).
|
|
21
|
+
*/
|
|
22
|
+
export function clearOllamaCache() {
|
|
23
|
+
cache.clear();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the current number of cached providers (useful for testing).
|
|
27
|
+
*/
|
|
28
|
+
export function getCacheSize() {
|
|
29
|
+
return cache.size;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ollamaCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaCache.js","sourceRoot":"","sources":["../../src/llm/ollamaCache.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,qDAAqD;AACrD,OAAO,EAAE,YAAY,EAAuB,MAAM,eAAe,CAAC;AAMlE,0CAA0C;AAC1C,uDAAuD;AACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;AAEhD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAExC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AppConfig } from '../config.js';
|
|
2
|
+
/** Default request timeout in milliseconds (60 seconds) */
|
|
3
|
+
export declare const OLLAMA_DEFAULT_TIMEOUT = 60000;
|
|
4
|
+
export declare class OllamaApiError extends Error {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly body: string;
|
|
7
|
+
constructor(status: number, body: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class OllamaParseError extends Error {
|
|
10
|
+
readonly rawResponse: string;
|
|
11
|
+
constructor(message: string, rawResponse: string);
|
|
12
|
+
}
|
|
13
|
+
/** Options for the ollamaClient request */
|
|
14
|
+
export interface OllamaClientOptions {
|
|
15
|
+
/** Request timeout in milliseconds. Default: 60000 (60 seconds) */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Low-level Ollama client used for structured JSON output via /api/generate.
|
|
20
|
+
* Tracks token usage for context window auto-offload.
|
|
21
|
+
*
|
|
22
|
+
* @param prompt - The prompt to send to Ollama
|
|
23
|
+
* @param format - The format specification (string or JSON Schema object)
|
|
24
|
+
* @param config - Application configuration
|
|
25
|
+
* @param options - Optional request options including timeout
|
|
26
|
+
* @throws OllamaApiError on timeout or HTTP errors
|
|
27
|
+
*/
|
|
28
|
+
export declare function ollamaClient<T>(prompt: string, format: string | object, config: AppConfig, options?: OllamaClientOptions): Promise<T>;
|
|
29
|
+
//# sourceMappingURL=ollamaClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaClient.d.ts","sourceRoot":"","sources":["../../src/llm/ollamaClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,qBAAa,cAAe,SAAQ,KAAK;aAErB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBADZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAGvB,WAAW,EAAE,MAAM;gBADnC,OAAO,EAAE,MAAM,EACC,WAAW,EAAE,MAAM;CAKtC;AAWD,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,MAAM,EAAE,SAAS,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,CAAC,CAAC,CAmGZ"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { TOKEN_BUDGETS } from './promptSections.js';
|
|
2
|
+
/** Default request timeout in milliseconds (60 seconds) */
|
|
3
|
+
export const OLLAMA_DEFAULT_TIMEOUT = 60_000;
|
|
4
|
+
export class OllamaApiError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
body;
|
|
7
|
+
constructor(status, body) {
|
|
8
|
+
super(`Ollama request failed with status ${status}: ${body}`);
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.body = body;
|
|
11
|
+
this.name = 'OllamaApiError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class OllamaParseError extends Error {
|
|
15
|
+
rawResponse;
|
|
16
|
+
constructor(message, rawResponse) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.rawResponse = rawResponse;
|
|
19
|
+
this.name = 'OllamaParseError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Token tracking — scoped per model for logging only.
|
|
23
|
+
// Accumulated tokens are used for log messages about high usage;
|
|
24
|
+
// they are NOT used to trigger context offload (that proved unreliable).
|
|
25
|
+
const accumulatedTokensByModel = new Map();
|
|
26
|
+
function addAccumulatedTokens(modelName, tokens) {
|
|
27
|
+
accumulatedTokensByModel.set(modelName, (accumulatedTokensByModel.get(modelName) ?? 0) + tokens);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Low-level Ollama client used for structured JSON output via /api/generate.
|
|
31
|
+
* Tracks token usage for context window auto-offload.
|
|
32
|
+
*
|
|
33
|
+
* @param prompt - The prompt to send to Ollama
|
|
34
|
+
* @param format - The format specification (string or JSON Schema object)
|
|
35
|
+
* @param config - Application configuration
|
|
36
|
+
* @param options - Optional request options including timeout
|
|
37
|
+
* @throws OllamaApiError on timeout or HTTP errors
|
|
38
|
+
*/
|
|
39
|
+
export async function ollamaClient(prompt, format, config, options) {
|
|
40
|
+
const timeoutMs = options?.timeout ?? OLLAMA_DEFAULT_TIMEOUT;
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
43
|
+
// Token budget enforcement — refuse oversized prompts for local models
|
|
44
|
+
const maxPromptChars = TOKEN_BUDGETS.ollamaClient.maxPromptChars;
|
|
45
|
+
if (prompt.length > maxPromptChars) {
|
|
46
|
+
console.error(`[ollamaClient] Prompt ${prompt.length} chars exceeds budget ${maxPromptChars}. Truncating.`);
|
|
47
|
+
prompt = prompt.slice(0, maxPromptChars) + '\n\n[PROMPT TRUNCATED — exceeded token budget]';
|
|
48
|
+
}
|
|
49
|
+
const requestBody = {
|
|
50
|
+
model: config.ollamaModel,
|
|
51
|
+
prompt,
|
|
52
|
+
stream: false,
|
|
53
|
+
format,
|
|
54
|
+
};
|
|
55
|
+
let response;
|
|
56
|
+
try {
|
|
57
|
+
response = await fetch(`${config.ollamaBaseUrl}/api/generate`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: { 'Content-Type': 'application/json' },
|
|
60
|
+
body: JSON.stringify(requestBody),
|
|
61
|
+
signal: controller.signal,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
clearTimeout(timeoutId);
|
|
66
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
67
|
+
throw new OllamaApiError(0, `Ollama request timed out after ${timeoutMs}ms`);
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
clearTimeout(timeoutId);
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
let errorBody;
|
|
74
|
+
try {
|
|
75
|
+
const jsonBody = await response.json();
|
|
76
|
+
errorBody = JSON.stringify(jsonBody);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
errorBody = await response.text();
|
|
80
|
+
}
|
|
81
|
+
throw new OllamaApiError(response.status, errorBody);
|
|
82
|
+
}
|
|
83
|
+
const data = await response.json();
|
|
84
|
+
// Track token usage for logging
|
|
85
|
+
const promptTokens = data.prompt_eval_count || 0;
|
|
86
|
+
const evalTokens = data.eval_count || 0;
|
|
87
|
+
const totalTokens = promptTokens + evalTokens;
|
|
88
|
+
const modelName = data.model || config.ollamaModel;
|
|
89
|
+
addAccumulatedTokens(modelName, totalTokens);
|
|
90
|
+
const accumulated = accumulatedTokensByModel.get(modelName) ?? 0;
|
|
91
|
+
if (totalTokens > 10000) {
|
|
92
|
+
console.error(`[ollamaClient] High token usage: ${totalTokens} (accumulated: ${accumulated})`);
|
|
93
|
+
}
|
|
94
|
+
// Some models return JSON in 'thinking' field instead of 'response'
|
|
95
|
+
let rawResponse = data.response || data.thinking || '';
|
|
96
|
+
// Strip markdown code fences (```json ... ```) that some cloud models wrap JSON in
|
|
97
|
+
rawResponse = rawResponse.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '').trim();
|
|
98
|
+
// Try direct JSON parse first
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(rawResponse);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Fallback: some cloud models ignore the format parameter and return
|
|
104
|
+
// plain text with JSON embedded. Try to extract the first JSON object.
|
|
105
|
+
const jsonMatch = rawResponse.match(/\{[\s\S]*\}/);
|
|
106
|
+
if (jsonMatch) {
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(jsonMatch[0]);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Fall through to error
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new OllamaParseError(`Failed to parse Ollama response as JSON`, rawResponse);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=ollamaClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaClient.js","sourceRoot":"","sources":["../../src/llm/ollamaClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const SUGGESTER_QUEUE: {
|
|
2
|
+
enqueue: <T>(label: string, task: () => Promise<T>) => Promise<T>;
|
|
3
|
+
};
|
|
4
|
+
export declare const INFERENCE_QUEUE: {
|
|
5
|
+
enqueue: <T>(label: string, task: () => Promise<T>) => Promise<T>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=ollamaQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaQueue.d.ts","sourceRoot":"","sources":["../../src/llm/ollamaQueue.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,eAAe;cAxBH,CAAC,SAAS,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;CAwBvB,CAAC;AAGxD,eAAO,MAAM,eAAe;cA3BH,CAAC,SAAS,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;CA2BvB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function createQueue(queueName) {
|
|
2
|
+
let queueDepth = 0;
|
|
3
|
+
let queueTail = Promise.resolve();
|
|
4
|
+
async function enqueue(label, task) {
|
|
5
|
+
queueDepth++;
|
|
6
|
+
const result = queueTail.then(async () => {
|
|
7
|
+
const startTime = Date.now();
|
|
8
|
+
try {
|
|
9
|
+
return await task();
|
|
10
|
+
}
|
|
11
|
+
finally {
|
|
12
|
+
const duration = Date.now() - startTime;
|
|
13
|
+
queueDepth--;
|
|
14
|
+
if (duration > 5000) {
|
|
15
|
+
console.error(`[ollamaQueue:${queueName}] ${label} slow (${duration}ms)`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
queueTail = result.catch(() => { });
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return { enqueue };
|
|
23
|
+
}
|
|
24
|
+
// FIX: Dedicated queue for suggester — runs independently of inference queue
|
|
25
|
+
export const SUGGESTER_QUEUE = createQueue('suggester');
|
|
26
|
+
// FIX: Shared queue for summarizer and prompt builder — serialized together
|
|
27
|
+
export const INFERENCE_QUEUE = createQueue('inference');
|
|
28
|
+
//# sourceMappingURL=ollamaQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollamaQueue.js","sourceRoot":"","sources":["../../src/llm/ollamaQueue.ts"],"names":[],"mappings":"AAAA,SAAS,WAAW,CAAC,SAAiB;IACpC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEpD,KAAK,UAAU,OAAO,CAAI,KAAa,EAAE,IAAsB;QAC7D,UAAU,EAAE,CAAC;QAEb,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,EAAE,CAAC;YACtB,CAAC;oBAAS,CAAC;gBACT,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;gBACxC,UAAU,EAAE,CAAC;gBACb,IAAI,QAAQ,GAAG,IAAI,EAAE,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,gBAAgB,SAAS,KAAK,KAAK,UAAU,QAAQ,KAAK,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;AAExD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppConfig } from '../config.js';
|
|
2
|
+
import type { ContextSummary, TaskSuggestion, GeneratedPrompt } from '../../../shared/dist/index.js';
|
|
3
|
+
export declare class MissingApiKeyError extends Error {
|
|
4
|
+
readonly provider: string;
|
|
5
|
+
constructor(provider: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function buildPrompt(task: TaskSuggestion, summary: ContextSummary, config: AppConfig): Promise<GeneratedPrompt>;
|
|
8
|
+
//# sourceMappingURL=promptBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptBuilder.d.ts","sourceRoot":"","sources":["../../src/llm/promptBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAA0B,MAAM,eAAe,CAAC;AAO7G,qBAAa,kBAAmB,SAAQ,KAAK;aACf,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAI7C;AAiED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,eAAe,CAAC,CAmB1B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { stateManager } from '../stateManager.js';
|
|
2
|
+
import { generateText } from 'ai';
|
|
3
|
+
import { eventStore } from '../eventStore.js';
|
|
4
|
+
import { buildRepoPromptSections, formatEventCompact, PLACEHOLDER_PATTERNS } from './promptSections.js';
|
|
5
|
+
import { getModel } from './providerModels.js';
|
|
6
|
+
export class MissingApiKeyError extends Error {
|
|
7
|
+
provider;
|
|
8
|
+
constructor(provider) {
|
|
9
|
+
super(`${provider} API key not configured`);
|
|
10
|
+
this.provider = provider;
|
|
11
|
+
this.name = 'MissingApiKeyError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const MIN_PROMPT_LENGTH = 50;
|
|
15
|
+
function buildPromptText(task, summary, notes, recentEvents) {
|
|
16
|
+
const notesSection = notes?.content
|
|
17
|
+
? `\n## Dev Notes\n${notes.content}\n`
|
|
18
|
+
: '';
|
|
19
|
+
const repo = buildRepoPromptSections();
|
|
20
|
+
const recentEventsText = recentEvents.length > 0
|
|
21
|
+
? recentEvents.map(e => formatEventCompact(e)).join('\n')
|
|
22
|
+
: 'No recent events.';
|
|
23
|
+
const toolsUsed = summary.toolsUsed;
|
|
24
|
+
const errorCount = summary.errorCount;
|
|
25
|
+
const testStatus = summary.testStatus;
|
|
26
|
+
return `Generate a precise, imperative prompt for a coding agent to execute.
|
|
27
|
+
${repo.instructions}
|
|
28
|
+
## Task
|
|
29
|
+
Title: ${task.title} | Priority: ${task.priority} | Rationale: ${task.rationale}
|
|
30
|
+
${task.affectedFiles ? `Files: ${task.affectedFiles.join(', ')}` : ''}
|
|
31
|
+
${task.suggestedToolCalls ? `Tools: ${task.suggestedToolCalls.join(', ')}` : ''}
|
|
32
|
+
${task.conflictsWithNotes ? `⚠ Conflicts with notes. ${task.conflictExplanation || ''}` : ''}
|
|
33
|
+
${notesSection}
|
|
34
|
+
## State
|
|
35
|
+
Doing: ${summary.whatIsHappening}
|
|
36
|
+
Changed: ${summary.filesChanged.join(', ') || 'None'}
|
|
37
|
+
Goal: ${summary.currentGoal}
|
|
38
|
+
Blockers: ${summary.blockers.join(', ') || 'None'}
|
|
39
|
+
Events: ${summary.rawEventCount}
|
|
40
|
+
${repo.root}
|
|
41
|
+
${toolsUsed ? `Session tools: ${toolsUsed.join(', ')}` : ''}
|
|
42
|
+
${errorCount !== undefined ? `Errors: ${errorCount}` : ''}
|
|
43
|
+
${testStatus ? `Tests: ${testStatus}` : ''}
|
|
44
|
+
|
|
45
|
+
## Recent (${recentEvents.length} events)
|
|
46
|
+
${recentEventsText}
|
|
47
|
+
|
|
48
|
+
## Rules
|
|
49
|
+
1. State exact goal in imperative form
|
|
50
|
+
2. List specific file paths to modify
|
|
51
|
+
3. Include verifiable acceptance criteria
|
|
52
|
+
4. Suggest approach/pattern
|
|
53
|
+
5. Reference relevant events (errors, diffs, outputs)
|
|
54
|
+
6. If tests failing, include exact test names/error messages
|
|
55
|
+
7. If blockers, explain resolution
|
|
56
|
+
|
|
57
|
+
Output only the prompt text.`;
|
|
58
|
+
}
|
|
59
|
+
function validatePrompt(prompt) {
|
|
60
|
+
if (!prompt || prompt.trim().length < MIN_PROMPT_LENGTH) {
|
|
61
|
+
throw new Error(`Prompt too short (${prompt?.length || 0} chars). Minimum ${MIN_PROMPT_LENGTH} characters required. Got: "${prompt?.substring(0, 50) || '(empty)'}"`);
|
|
62
|
+
}
|
|
63
|
+
const matchedPattern = PLACEHOLDER_PATTERNS.find(pattern => pattern.test(prompt.trim()));
|
|
64
|
+
if (matchedPattern) {
|
|
65
|
+
process.stderr.write(`[VALIDATION REJECT] Pattern: ${matchedPattern.source} | Content: "${prompt.substring(0, 100)}"\n`);
|
|
66
|
+
throw new Error(`Placeholder prompt detected: "${prompt.substring(0, 100)}". Please regenerate.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function buildPrompt(task, summary, config) {
|
|
70
|
+
const notes = stateManager.getNotes();
|
|
71
|
+
const provider = config.llmProvider || 'ollama';
|
|
72
|
+
const model = getModel(provider, config);
|
|
73
|
+
// Fetch recent events for grounding the prompt in current reality
|
|
74
|
+
const recentEvents = eventStore.getLast(10);
|
|
75
|
+
const promptText = buildPromptText(task, summary, notes, recentEvents);
|
|
76
|
+
const { text } = await generateText({ model, prompt: promptText });
|
|
77
|
+
validatePrompt(text);
|
|
78
|
+
return {
|
|
79
|
+
taskId: task.id,
|
|
80
|
+
provider: provider,
|
|
81
|
+
prompt: text,
|
|
82
|
+
generatedAt: Date.now(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=promptBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptBuilder.js","sourceRoot":"","sources":["../../src/llm/promptBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared prompt section builders — eliminates 4x duplication of
|
|
3
|
+
* repo instructions/root sections across promptBuilder, suggester,
|
|
4
|
+
* summarizer, and blueprint.
|
|
5
|
+
*/
|
|
6
|
+
export interface RepoPromptSections {
|
|
7
|
+
instructions: string;
|
|
8
|
+
root: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function buildRepoPromptSections(): RepoPromptSections;
|
|
11
|
+
/**
|
|
12
|
+
* Compact event formatter — reduces token waste from verbose field joining.
|
|
13
|
+
* ~40% fewer tokens vs original formatEvent() for typical events.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatEventCompact(event: {
|
|
16
|
+
type: string;
|
|
17
|
+
tool?: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
thinking?: string;
|
|
20
|
+
output?: string;
|
|
21
|
+
diff?: string;
|
|
22
|
+
status?: string;
|
|
23
|
+
args?: Record<string, unknown>;
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
turnNumber?: number;
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
}): string;
|
|
28
|
+
/**
|
|
29
|
+
* Estimate token count from character length.
|
|
30
|
+
* Conservative: uses chars/3.5 for code-heavy content (higher than prose /4).
|
|
31
|
+
*/
|
|
32
|
+
export declare function estimateTokens(text: string): number;
|
|
33
|
+
/**
|
|
34
|
+
* Token budget configuration per feature path.
|
|
35
|
+
*/
|
|
36
|
+
export declare const TOKEN_BUDGETS: {
|
|
37
|
+
readonly summarizer: {
|
|
38
|
+
readonly maxEvents: 30;
|
|
39
|
+
readonly maxEventChars: 400;
|
|
40
|
+
readonly maxTotalChars: 12000;
|
|
41
|
+
};
|
|
42
|
+
readonly suggester: {
|
|
43
|
+
readonly maxInstructionsChars: 600;
|
|
44
|
+
};
|
|
45
|
+
readonly promptBuilder: {
|
|
46
|
+
readonly maxEvents: 10;
|
|
47
|
+
readonly maxEventChars: 300;
|
|
48
|
+
};
|
|
49
|
+
readonly blueprint: {
|
|
50
|
+
readonly maxGroundingChars: 4000;
|
|
51
|
+
readonly maxAgentsMdChars: 6000;
|
|
52
|
+
};
|
|
53
|
+
readonly ollamaClient: {
|
|
54
|
+
readonly maxPromptChars: 50000;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export declare const PLACEHOLDER_PATTERNS: RegExp[];
|
|
58
|
+
export declare function hasMeaningfulSummaryContent(summary: {
|
|
59
|
+
whatIsHappening?: string;
|
|
60
|
+
currentGoal?: string;
|
|
61
|
+
blockers?: string[];
|
|
62
|
+
filesChanged?: string[];
|
|
63
|
+
} | null | undefined): boolean;
|
|
64
|
+
//# sourceMappingURL=promptSections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptSections.d.ts","sourceRoot":"","sources":["../../src/llm/promptSections.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,uBAAuB,IAAI,kBAAkB,CAU5D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAiCT;AAqBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;CAoBhB,CAAC;AAEX,eAAO,MAAM,oBAAoB,UAOhC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAQ7B"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared prompt section builders — eliminates 4x duplication of
|
|
3
|
+
* repo instructions/root sections across promptBuilder, suggester,
|
|
4
|
+
* summarizer, and blueprint.
|
|
5
|
+
*/
|
|
6
|
+
import { stateManager } from '../stateManager.js';
|
|
7
|
+
export function buildRepoPromptSections() {
|
|
8
|
+
const repoCtx = stateManager.getRepoContext();
|
|
9
|
+
return {
|
|
10
|
+
instructions: repoCtx?.repoInstructions
|
|
11
|
+
? `\n## Repo Instructions (${repoCtx.instructionsFile})\n${repoCtx.repoInstructions}\n`
|
|
12
|
+
: '',
|
|
13
|
+
root: repoCtx?.repoRoot
|
|
14
|
+
? `\n- Repo root: ${repoCtx.repoRoot}`
|
|
15
|
+
: '',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compact event formatter — reduces token waste from verbose field joining.
|
|
20
|
+
* ~40% fewer tokens vs original formatEvent() for typical events.
|
|
21
|
+
*/
|
|
22
|
+
export function formatEventCompact(event) {
|
|
23
|
+
const parts = [`[${event.type}]`];
|
|
24
|
+
if (event.tool)
|
|
25
|
+
parts.push(`t=${event.tool}`);
|
|
26
|
+
if (event.status)
|
|
27
|
+
parts.push(`s=${event.status}`);
|
|
28
|
+
if (event.durationMs !== undefined)
|
|
29
|
+
parts.push(`${event.durationMs}ms`);
|
|
30
|
+
if (event.text)
|
|
31
|
+
parts.push(trunc(event.text, 400));
|
|
32
|
+
if (event.thinking)
|
|
33
|
+
parts.push(`(think: ${trunc(event.thinking, 300)})`);
|
|
34
|
+
if (event.args) {
|
|
35
|
+
const paths = extractFilePaths(event.args);
|
|
36
|
+
const argsStr = JSON.stringify(event.args);
|
|
37
|
+
if (argsStr.length > 300) {
|
|
38
|
+
parts.push(`args=${trunc(argsStr, 300)}`);
|
|
39
|
+
if (paths.length)
|
|
40
|
+
parts.push(`files=[${paths.join(',')}]`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
parts.push(`args=${argsStr}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (event.output) {
|
|
47
|
+
const outStr = typeof event.output === 'string' ? event.output : JSON.stringify(event.output);
|
|
48
|
+
parts.push(`out=${trunc(outStr, 500)}`);
|
|
49
|
+
}
|
|
50
|
+
if (event.diff) {
|
|
51
|
+
const hdr = event.diff.split('\n').slice(0, 3).join('\n');
|
|
52
|
+
const body = event.diff.split('\n').slice(3).join('\n');
|
|
53
|
+
parts.push(`diff=${hdr}\n${trunc(body, 300)}`);
|
|
54
|
+
}
|
|
55
|
+
return parts.join(' ');
|
|
56
|
+
}
|
|
57
|
+
function trunc(s, max) {
|
|
58
|
+
return s.length <= max ? s : s.substring(0, max) + '…';
|
|
59
|
+
}
|
|
60
|
+
function extractFilePaths(args) {
|
|
61
|
+
const paths = [];
|
|
62
|
+
const search = (obj) => {
|
|
63
|
+
if (typeof obj === 'string' && /^\/?[\w\-./]+\.[a-zA-Z0-9]+$/.test(obj) && obj.includes('/')) {
|
|
64
|
+
paths.push(obj);
|
|
65
|
+
}
|
|
66
|
+
else if (Array.isArray(obj)) {
|
|
67
|
+
obj.forEach(search);
|
|
68
|
+
}
|
|
69
|
+
else if (obj && typeof obj === 'object') {
|
|
70
|
+
Object.values(obj).forEach(search);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
search(args);
|
|
74
|
+
return [...new Set(paths)];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Estimate token count from character length.
|
|
78
|
+
* Conservative: uses chars/3.5 for code-heavy content (higher than prose /4).
|
|
79
|
+
*/
|
|
80
|
+
export function estimateTokens(text) {
|
|
81
|
+
return Math.ceil(text.length / 3.5);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Token budget configuration per feature path.
|
|
85
|
+
*/
|
|
86
|
+
export const TOKEN_BUDGETS = {
|
|
87
|
+
summarizer: {
|
|
88
|
+
maxEvents: 30, // was 50 — reduce by 40%
|
|
89
|
+
maxEventChars: 400, // was 800 — halve per-event size
|
|
90
|
+
maxTotalChars: 12000, // hard cap on total event section
|
|
91
|
+
},
|
|
92
|
+
suggester: {
|
|
93
|
+
maxInstructionsChars: 600, // cap instruction text
|
|
94
|
+
},
|
|
95
|
+
promptBuilder: {
|
|
96
|
+
maxEvents: 10, // already 10 — keep but compact format
|
|
97
|
+
maxEventChars: 300,
|
|
98
|
+
},
|
|
99
|
+
blueprint: {
|
|
100
|
+
maxGroundingChars: 4000, // hard cap on grounding context
|
|
101
|
+
maxAgentsMdChars: 6000, // cap AGENTS.md when injected into phases
|
|
102
|
+
},
|
|
103
|
+
ollamaClient: {
|
|
104
|
+
maxPromptChars: 50000, // safety cap for local models
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
export const PLACEHOLDER_PATTERNS = [
|
|
108
|
+
/^\.+$/,
|
|
109
|
+
/^Implement Feature/i,
|
|
110
|
+
/^Task title$/i,
|
|
111
|
+
/^Feature [A-Z]$/i,
|
|
112
|
+
/^[A-Z_]+$/,
|
|
113
|
+
/^task[a-z0-9]{4,}$/i,
|
|
114
|
+
];
|
|
115
|
+
export function hasMeaningfulSummaryContent(summary) {
|
|
116
|
+
if (!summary)
|
|
117
|
+
return false;
|
|
118
|
+
const what = (summary.whatIsHappening ?? '').trim();
|
|
119
|
+
const goal = (summary.currentGoal ?? '').trim();
|
|
120
|
+
if (what.length === 0 && goal.length === 0)
|
|
121
|
+
return false;
|
|
122
|
+
if (what && PLACEHOLDER_PATTERNS.some(p => p.test(what)))
|
|
123
|
+
return false;
|
|
124
|
+
if (goal && PLACEHOLDER_PATTERNS.some(p => p.test(goal)))
|
|
125
|
+
return false;
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=promptSections.js.map
|