openprompt-lang 1.2.7 β 1.4.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 +62 -8
- package/bin/cli.js +2 -0
- package/docs/00-ARCHITECTURE/OPL-BOOST-MULTI-AGENT.md +406 -0
- package/docs/02-STANDARDS/AGENTS.template.md +89 -0
- package/docs/02-STANDARDS/ticket-driven-development.md +99 -0
- package/docs/04-TICKETS/BOOST-001-profile-registry.md +66 -0
- package/docs/04-TICKETS/BOOST-002-context-compression.md +58 -0
- package/docs/04-TICKETS/BOOST-003-template-hydration.md +69 -0
- package/docs/04-TICKETS/BOOST-004-fewshot-engine.md +58 -0
- package/docs/04-TICKETS/BOOST-005-agent-pool.md +69 -0
- package/docs/04-TICKETS/BOOST-006-specialized-agents.md +53 -0
- package/docs/04-TICKETS/BOOST-007-validation-loop.md +56 -0
- package/docs/04-TICKETS/BOOST-008-orchestrator.md +71 -0
- package/docs/04-TICKETS/BOOST-009-cache-system.md +56 -0
- package/docs/04-TICKETS/BOOST-010-cli-mcp.md +67 -0
- package/docs/04-TICKETS/BOOST-011-self-learning.md +50 -0
- package/docs/04-TICKETS/BOOST-012-prompt-preamble.md +109 -0
- package/docs/04-TICKETS/BOOST-013-hydrator-duplicate-code.md +132 -0
- package/docs/04-TICKETS/BOOST-014-multiagent-missing-parts.md +87 -0
- package/docs/04-TICKETS/BOOST-015-skeleton-type-missing.md +76 -0
- package/docs/04-TICKETS/BOOST-016-output-path-duplicate.md +68 -0
- package/docs/04-TICKETS/INDEX.md +89 -0
- package/docs/04-TICKETS/_archive/BOOST-005-micro-tasking.md +67 -0
- package/docs/04-TICKETS/_archive/BOOST-006-validation-loop.md +66 -0
- package/docs/04-TICKETS/_archive/BOOST-007-progressive-pipeline.md +69 -0
- package/docs/04-TICKETS/_archive/BOOST-008-cli-mcp-integration.md +74 -0
- package/docs/AI_CONTEXT.md +16 -0
- package/docs/EMBEDDINGS.md +214 -0
- package/docs/ONBOARDING_WORKFLOW.md +151 -0
- package/docs/OPL_ACADEMIC_ISSUES.md +158 -0
- package/docs/WEB_SCRAPER_PLAN.md +454 -0
- package/package.json +9 -2
- package/scripts/postinstall.js +37 -0
- package/src/boost/agent-pool.js +442 -0
- package/src/boost/agents/index.js +79 -0
- package/src/boost/cache.js +241 -0
- package/src/boost/context-compressor.js +354 -0
- package/src/boost/fewshot-retriever.js +332 -0
- package/src/boost/hardware-detector.js +486 -0
- package/src/boost/hydrator.js +398 -0
- package/src/boost/index.js +60 -0
- package/src/boost/orchestrator.js +615 -0
- package/src/boost/preamble.js +217 -0
- package/src/boost/profile-registry.js +264 -0
- package/src/boost/self-learn.js +247 -0
- package/src/boost/skeletons/component.skeleton.js +24 -0
- package/src/boost/skeletons/hook.skeleton.js +27 -0
- package/src/boost/skeletons/index.js +67 -0
- package/src/boost/skeletons/page.skeleton.js +22 -0
- package/src/boost/skeletons/service.skeleton.js +20 -0
- package/src/boost/skeletons/store.skeleton.js +18 -0
- package/src/boost/skeletons/type.skeleton.js +11 -0
- package/src/boost/task-dispatcher.js +142 -0
- package/src/boost/validation-loop.js +495 -0
- package/src/cli/commands-boost.js +394 -0
- package/src/cli/commands-knowledge.js +1 -0
- package/src/cli/commands-opl.js +79 -1
- package/src/cli/commands-workflow.js +125 -6
- package/src/commands/init-core.js +169 -5
- package/src/commands/knowledge-ops.js +52 -0
- package/src/commands/opl-embeddings.js +556 -0
- package/src/commands/opl-help.js +26 -2
- package/src/commands/opl-search.js +106 -2
- package/src/commands/opl-webscrape.js +390 -0
- package/src/commands/workflow/epic-cli.js +192 -0
- package/src/commands/workflow/select.js +146 -0
- package/src/commands/workflow/sprint-cli.js +174 -0
- package/src/core/webscrape/analyzer.js +481 -0
- package/src/core/webscrape/deep-scraper.js +1027 -0
- package/src/core/workflow/epic-manager.js +845 -0
- package/src/core/workflow/gates.js +180 -1
- package/src/core/workflow/selector.js +707 -0
- package/src/embeddings/chunker.js +450 -0
- package/src/embeddings/embedder.js +431 -0
- package/src/embeddings/index-pipeline.js +320 -0
- package/src/embeddings/vector-store.js +505 -0
- package/src/mcp-refactor/handlers/boost.js +295 -0
- package/src/mcp-refactor/router.js +19 -0
- package/src/mcp-refactor/tools.js +113 -0
package/scripts/postinstall.js
CHANGED
|
@@ -90,6 +90,43 @@ try {
|
|
|
90
90
|
console.log(" π‘ O instΓ‘lalo manualmente segΓΊn tu plataforma.")
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
94
|
+
// InformaciΓ³n sobre embeddings y Ollama
|
|
95
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
96
|
+
try {
|
|
97
|
+
const { execSync } = await import("child_process")
|
|
98
|
+
const ollamaPath = execSync("which ollama 2>/dev/null || echo ''", {
|
|
99
|
+
encoding: "utf-8",
|
|
100
|
+
}).trim()
|
|
101
|
+
|
|
102
|
+
if (ollamaPath) {
|
|
103
|
+
const models = execSync("ollama list 2>/dev/null || echo ''", {
|
|
104
|
+
encoding: "utf-8",
|
|
105
|
+
}).trim()
|
|
106
|
+
const hasEmbedModel = models.toLowerCase().includes("nomic-embed-text")
|
|
107
|
+
|
|
108
|
+
if (hasEmbedModel) {
|
|
109
|
+
console.log("β
Ollama + nomic-embed-text listos para embeddings semΓ‘nticos")
|
|
110
|
+
} else {
|
|
111
|
+
console.log("π¦ Ollama detectado. Para embeddings semΓ‘nticos ejecuta:")
|
|
112
|
+
console.log(" ollama pull nomic-embed-text")
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
console.log("π¦ Ollama no detectado. Usando Transformers.js como fallback.")
|
|
116
|
+
console.log(" π‘ Instala Ollama para mejor rendimiento: https://ollama.com")
|
|
117
|
+
console.log(" π‘ Luego: ollama pull nomic-embed-text")
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// No crΓtico
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log("")
|
|
124
|
+
console.log("π BΓΊsqueda semΓ‘ntica disponible:")
|
|
125
|
+
console.log(' opl search "consulta" --mode vector')
|
|
126
|
+
console.log("")
|
|
127
|
+
console.log("π Indexa documentos en el vector store:")
|
|
128
|
+
console.log(" opl embeddings index <docId>")
|
|
129
|
+
console.log("")
|
|
93
130
|
console.log("π‘ Ejecuta 'opl init' para crear un nuevo proyecto con planificaciΓ³n")
|
|
94
131
|
} catch (err) {
|
|
95
132
|
console.log(` β οΈ opencode: ${err.message}`)
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
// @use(kind, contract, limit, error)
|
|
2
|
+
// @kind(util)
|
|
3
|
+
// @contract(in: prompt, role, profile -> out: AgentPool instance + callOllama, buildPrompt, checkOllama @error: OllamaConnectionError, AgentTimeoutError)
|
|
4
|
+
// @limit(lines: 200)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Agent Pool β MΓ³dulo OPL Boost
|
|
8
|
+
*
|
|
9
|
+
* Conector a Ollama + pool de agentes para orquestaciΓ³n interna.
|
|
10
|
+
* Permite que OPL llame a modelos locales sin depender de OpenCode.
|
|
11
|
+
*
|
|
12
|
+
* Modo de uso:
|
|
13
|
+
* import { agentPool } from './agent-pool.js'
|
|
14
|
+
* const result = await agentPool.submit('types', 'hook useAuth')
|
|
15
|
+
*
|
|
16
|
+
* Modo cola (FIFO): default, una solicitud a la vez
|
|
17
|
+
* Modo paralelo: configurable via profile.agentPool.maxConcurrency
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { getProfile } from "./profile-registry.js"
|
|
21
|
+
|
|
22
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
23
|
+
// ConfiguraciΓ³n
|
|
24
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
25
|
+
|
|
26
|
+
const OLLAMA_HOST = process.env.OLLAMA_HOST || "http://localhost:11434"
|
|
27
|
+
const DEFAULT_TIMEOUT = 30_000 // 30s
|
|
28
|
+
|
|
29
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
30
|
+
// System prompts por rol de agente
|
|
31
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
32
|
+
|
|
33
|
+
const AGENT_SYSTEM_PROMPTS = {
|
|
34
|
+
types:
|
|
35
|
+
"Eres un experto en TypeScript. Tu ΓΊnica tarea es definir TYPES e INTERFACES. " +
|
|
36
|
+
"No implementes lΓ³gica. No generes JSX. Solo tipos, interfaces, y anotaciones de tipo. " +
|
|
37
|
+
"Output exacto: bloques de cΓ³digo TypeScript con export.",
|
|
38
|
+
|
|
39
|
+
state:
|
|
40
|
+
"Eres un experto en React state management. Recibes types ya definidos. " +
|
|
41
|
+
"Tu tarea es diseΓ±ar el ESTADO del componente/hook: useState, useReducer, useEffect. " +
|
|
42
|
+
"No implementes lΓ³gica de negocio. Solo estructura de estado y efectos. " +
|
|
43
|
+
"Output exacto: bloques de cΓ³digo con hooks de React.",
|
|
44
|
+
|
|
45
|
+
logic:
|
|
46
|
+
"Eres un experto en implementar lΓ³gica de negocio. Recibes types + estado ya definidos. " +
|
|
47
|
+
"Tu tarea es IMPLEMENTAR las funciones con lΓ³gica real. " +
|
|
48
|
+
"Usa los types y estado existentes. No los redefinas. " +
|
|
49
|
+
"Output exacto: la funciΓ³n/componente completo con implementaciΓ³n.",
|
|
50
|
+
|
|
51
|
+
cleaner:
|
|
52
|
+
"Eres un compresor de contexto para desarrollo asistido por IA. " +
|
|
53
|
+
"Recibes reglas extensas y debes devolver SOLO las reglas relevantes para la tarea especΓfica. " +
|
|
54
|
+
"Prioriza: lΓmites de lΓneas (@kind + @limit), reglas de enforcement, prohibiciones (NUNCA). " +
|
|
55
|
+
"Omite: stack, UI, colores, documentaciΓ³n de comandos no relevantes. " +
|
|
56
|
+
"Output exacto: markdown compacto con las reglas esenciales.",
|
|
57
|
+
|
|
58
|
+
validate:
|
|
59
|
+
"Eres un corrector de cΓ³digo. Recibes cΓ³digo que fallΓ³ validaciΓ³n y los errores especΓficos. " +
|
|
60
|
+
"Tu tarea es CORREGIR solo los errores reportados, no reescribir todo. " +
|
|
61
|
+
"Conserva la estructura y lΓ³gica original. Arregla solo lo que fallΓ³. " +
|
|
62
|
+
"Output exacto: el cΓ³digo completo corregido.",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
66
|
+
// Builders de prompts
|
|
67
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
68
|
+
|
|
69
|
+
const PROMPT_TEMPLATES = {
|
|
70
|
+
types: (input) =>
|
|
71
|
+
`Task: Define types for "${input}"
|
|
72
|
+
|
|
73
|
+
Rules:
|
|
74
|
+
- Export all interfaces
|
|
75
|
+
- Use TypeScript strict mode
|
|
76
|
+
- Add JSDoc comments
|
|
77
|
+
- Maximum 60 lines
|
|
78
|
+
|
|
79
|
+
Output ONLY TypeScript types.`,
|
|
80
|
+
|
|
81
|
+
state: (input) =>
|
|
82
|
+
`Task: Design state management
|
|
83
|
+
|
|
84
|
+
Context: ${input}
|
|
85
|
+
|
|
86
|
+
Rules:
|
|
87
|
+
- Use React hooks (useState, useEffect, useCallback)
|
|
88
|
+
- Reference existing types
|
|
89
|
+
- No business logic
|
|
90
|
+
- Maximum 60 lines
|
|
91
|
+
|
|
92
|
+
Output ONLY React hooks and state.`,
|
|
93
|
+
|
|
94
|
+
logic: (input) =>
|
|
95
|
+
`Task: Implement business logic
|
|
96
|
+
|
|
97
|
+
Context: ${input}
|
|
98
|
+
|
|
99
|
+
Rules:
|
|
100
|
+
- Use existing types and state
|
|
101
|
+
- Implement full logic
|
|
102
|
+
- Handle errors and edge cases
|
|
103
|
+
- Maximum 80 lines
|
|
104
|
+
|
|
105
|
+
Output ONLY the implementation.`,
|
|
106
|
+
|
|
107
|
+
cleaner: (input) =>
|
|
108
|
+
`Task: Compress context for code generation
|
|
109
|
+
|
|
110
|
+
Target task: ${input.substring(0, 200)}
|
|
111
|
+
|
|
112
|
+
Rules to preserve (if present in context):
|
|
113
|
+
- @kind limits (hook:80, component:120, page:200, service:150, store:100, util:100)
|
|
114
|
+
- @limit blocks commits, NOT a suggestion
|
|
115
|
+
- @use() required at file top
|
|
116
|
+
- Named exports over default
|
|
117
|
+
- NUNCA rules (any, template files, etc.)
|
|
118
|
+
- Annotations first, implementation second
|
|
119
|
+
|
|
120
|
+
Compress to <200 lines. Preserve critical rules only.`,
|
|
121
|
+
|
|
122
|
+
validate: (input) =>
|
|
123
|
+
`Task: Fix validation errors
|
|
124
|
+
|
|
125
|
+
Code with errors:
|
|
126
|
+
\`\`\`
|
|
127
|
+
${input.substring(0, 1500)}
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
Fix ONLY the reported errors. Do not rewrite.
|
|
131
|
+
Output the ENTIRE corrected file.`,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function buildPrompt(role, input) {
|
|
135
|
+
const systemPrompt = AGENT_SYSTEM_PROMPTS[role]
|
|
136
|
+
const template = PROMPT_TEMPLATES[role]
|
|
137
|
+
|
|
138
|
+
if (!systemPrompt || !template) {
|
|
139
|
+
throw new Error(`Rol de agente desconocido: "${role}". Roles: ${Object.keys(AGENT_SYSTEM_PROMPTS).join(", ")}`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
system: systemPrompt,
|
|
144
|
+
prompt: template(input),
|
|
145
|
+
role,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
150
|
+
// Ollama connector
|
|
151
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
152
|
+
|
|
153
|
+
export class OllamaConnectionError extends Error {
|
|
154
|
+
constructor(message, cause) {
|
|
155
|
+
super(message)
|
|
156
|
+
this.name = "OllamaConnectionError"
|
|
157
|
+
this.cause = cause
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export class AgentTimeoutError extends Error {
|
|
162
|
+
constructor(role, timeout) {
|
|
163
|
+
super(`Agent "${role}" timed out after ${timeout}ms`)
|
|
164
|
+
this.name = "AgentTimeoutError"
|
|
165
|
+
this.role = role
|
|
166
|
+
this.timeout = timeout
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Cache de modelo seleccionado para no re-detectar en cada llamada
|
|
171
|
+
let _bestModelCache = null
|
|
172
|
+
|
|
173
|
+
export async function callOllama(promptText, options = {}) {
|
|
174
|
+
const {
|
|
175
|
+
model = _bestModelCache || "llama3.2:latest", // default seguro siempre disponible
|
|
176
|
+
system = "",
|
|
177
|
+
temperature = 0.2,
|
|
178
|
+
maxTokens = 4096,
|
|
179
|
+
timeout = DEFAULT_TIMEOUT,
|
|
180
|
+
} = options
|
|
181
|
+
|
|
182
|
+
const controller = new AbortController()
|
|
183
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const body = {
|
|
187
|
+
model,
|
|
188
|
+
prompt: promptText,
|
|
189
|
+
system,
|
|
190
|
+
stream: false,
|
|
191
|
+
options: {
|
|
192
|
+
temperature,
|
|
193
|
+
num_predict: maxTokens,
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const response = await fetch(`${OLLAMA_HOST}/api/generate`, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: { "Content-Type": "application/json" },
|
|
200
|
+
body: JSON.stringify(body),
|
|
201
|
+
signal: controller.signal,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
if (!response.ok) {
|
|
205
|
+
throw new OllamaConnectionError(
|
|
206
|
+
`Ollama responded with ${response.status}: ${response.statusText}`
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const data = await response.json()
|
|
211
|
+
|
|
212
|
+
if (data.error) {
|
|
213
|
+
throw new OllamaConnectionError(`Ollama error: ${data.error}`)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
text: data.response || "",
|
|
218
|
+
totalDuration: data.total_duration || 0,
|
|
219
|
+
tokensPerSecond: data.tokens_per_second || 0,
|
|
220
|
+
tokenCount: (data.tokens_evaluated || 0) + (data.prompt_eval_count || 0),
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
if (err.name === "AbortError") {
|
|
224
|
+
throw new AgentTimeoutError(options.role || "unknown", timeout)
|
|
225
|
+
}
|
|
226
|
+
if (err instanceof OllamaConnectionError || err instanceof AgentTimeoutError) {
|
|
227
|
+
throw err
|
|
228
|
+
}
|
|
229
|
+
throw new OllamaConnectionError(
|
|
230
|
+
`Failed to connect to Ollama at ${OLLAMA_HOST}: ${err.message}`,
|
|
231
|
+
err
|
|
232
|
+
)
|
|
233
|
+
} finally {
|
|
234
|
+
clearTimeout(timeoutId)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
239
|
+
// Pool de agentes (cola FIFO)
|
|
240
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
241
|
+
|
|
242
|
+
class AgentPool {
|
|
243
|
+
constructor() {
|
|
244
|
+
this._queue = []
|
|
245
|
+
this._running = false
|
|
246
|
+
this._currentTask = null
|
|
247
|
+
this._stats = { completed: 0, failed: 0, totalTime: 0 }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Encola una solicitud de agente y retorna el resultado.
|
|
252
|
+
* Antes de encolar, verifica el estado del sistema (RAM, CPU).
|
|
253
|
+
*
|
|
254
|
+
* @param {string} role - Rol del agente (types, state, logic, cleaner, validate)
|
|
255
|
+
* @param {string} input - Input para el agente
|
|
256
|
+
* @param {object} [profile] - Perfil Boost (opcional, auto-detecta si se omite)
|
|
257
|
+
* @returns {Promise<{text: string, role: string, duration: number, metrics: object}>}
|
|
258
|
+
*/
|
|
259
|
+
async submit(role, input, profile) {
|
|
260
|
+
const prof = profile || getProfile()
|
|
261
|
+
const agentConfig = prof?.agentPool || {}
|
|
262
|
+
|
|
263
|
+
// Verificar estado del sistema antes de encolar
|
|
264
|
+
const { getSafeParallelism, getRAMWarning } = await import("./hardware-detector.js")
|
|
265
|
+
const safety = getSafeParallelism(prof)
|
|
266
|
+
|
|
267
|
+
if (!safety.safe) {
|
|
268
|
+
const warning = getRAMWarning()
|
|
269
|
+
throw new Error(
|
|
270
|
+
`Boost bloqueado por recursos insuficientes.\n${warning}\n` +
|
|
271
|
+
`β Libera memoria y vuelve a intentarlo.`
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
this._queue.push({
|
|
277
|
+
role,
|
|
278
|
+
input,
|
|
279
|
+
profile: prof,
|
|
280
|
+
agentConfig,
|
|
281
|
+
safety, // guardamos la recomendaciΓ³n de seguridad
|
|
282
|
+
resolve,
|
|
283
|
+
reject,
|
|
284
|
+
})
|
|
285
|
+
this._processQueue()
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async _processQueue() {
|
|
290
|
+
if (this._running || this._queue.length === 0) return
|
|
291
|
+
this._running = true
|
|
292
|
+
|
|
293
|
+
while (this._queue.length > 0) {
|
|
294
|
+
const task = this._queue.shift()
|
|
295
|
+
this._currentTask = task
|
|
296
|
+
const startTime = Date.now()
|
|
297
|
+
|
|
298
|
+
// Re-verificar sistema antes de CADA agente (por si cambiΓ³)
|
|
299
|
+
try {
|
|
300
|
+
const { getSafeParallelism, getRAMWarning } = await import("./hardware-detector.js")
|
|
301
|
+
const currentSafety = getSafeParallelism(task.profile)
|
|
302
|
+
|
|
303
|
+
if (!currentSafety.safe) {
|
|
304
|
+
// Si el sistema se saturΓ³ mientras esperaba, re-encolar
|
|
305
|
+
const warning = getRAMWarning()
|
|
306
|
+
console.warn(`\n βΈ Agente "${task.role}" pausado β ${warning}`)
|
|
307
|
+
// Esperar 5 segundos y re-intentar
|
|
308
|
+
await new Promise((r) => setTimeout(r, 5000))
|
|
309
|
+
this._queue.unshift(task) // re-encolar al principio
|
|
310
|
+
continue
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Si hay demasiados agentes en paralelo y estamos en modo cola, esperar
|
|
314
|
+
if (currentSafety.mode === "queue" && task.safety?.mode === "parallel") {
|
|
315
|
+
// La situaciΓ³n cambiΓ³: degradar a cola automΓ‘ticamente
|
|
316
|
+
console.warn(`\n βΈ Cambiando a modo cola por carga del sistema.`)
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
// Si falla la verificaciΓ³n, continuar de todas formas
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
const prompt = buildPrompt(task.role, task.input)
|
|
324
|
+
const modelName =
|
|
325
|
+
task.agentConfig.modelName || task.profile?.agentPool?.modelName || _bestModelCache || await selectBestModel(task.profile) || "llama3.2:latest"
|
|
326
|
+
|
|
327
|
+
// Estimar RAM que usarΓ‘ el modelo
|
|
328
|
+
const { estimateModelRAM } = await import("./hardware-detector.js")
|
|
329
|
+
const estimatedRAM = estimateModelRAM(modelName)
|
|
330
|
+
|
|
331
|
+
const result = await callOllama(prompt.prompt, {
|
|
332
|
+
model: modelName,
|
|
333
|
+
system: prompt.system,
|
|
334
|
+
temperature: task.agentConfig.temperature || 0.2,
|
|
335
|
+
timeout: task.agentConfig.timeout || DEFAULT_TIMEOUT,
|
|
336
|
+
role: task.role,
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
const duration = Date.now() - startTime
|
|
340
|
+
this._stats.completed++
|
|
341
|
+
this._stats.totalTime += duration
|
|
342
|
+
|
|
343
|
+
task.resolve({
|
|
344
|
+
text: result.text.trim(),
|
|
345
|
+
role: task.role,
|
|
346
|
+
duration,
|
|
347
|
+
metrics: {
|
|
348
|
+
tokensPerSecond: result.tokensPerSecond,
|
|
349
|
+
tokenCount: result.tokenCount,
|
|
350
|
+
model: modelName,
|
|
351
|
+
estimatedRAMGB: estimatedRAM,
|
|
352
|
+
},
|
|
353
|
+
})
|
|
354
|
+
} catch (err) {
|
|
355
|
+
this._stats.failed++
|
|
356
|
+
task.reject(err)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
this._running = false
|
|
361
|
+
this._currentTask = null
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
get stats() {
|
|
365
|
+
return { ...this._stats, queueLength: this._queue.length, isRunning: this._running }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async clear() {
|
|
369
|
+
this._queue = []
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Singleton
|
|
374
|
+
export const agentPool = new AgentPool()
|
|
375
|
+
|
|
376
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
377
|
+
// Utilidades de detecciΓ³n
|
|
378
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
379
|
+
|
|
380
|
+
export async function checkOllama() {
|
|
381
|
+
try {
|
|
382
|
+
const response = await fetch(`${OLLAMA_HOST}/api/tags`, {
|
|
383
|
+
signal: AbortSignal.timeout(5000),
|
|
384
|
+
})
|
|
385
|
+
return response.ok
|
|
386
|
+
} catch {
|
|
387
|
+
return false
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export async function listOllamaModels() {
|
|
392
|
+
try {
|
|
393
|
+
const response = await fetch(`${OLLAMA_HOST}/api/tags`, {
|
|
394
|
+
signal: AbortSignal.timeout(5000),
|
|
395
|
+
})
|
|
396
|
+
if (!response.ok) return []
|
|
397
|
+
const data = await response.json()
|
|
398
|
+
return (data.models || []).map((m) => ({
|
|
399
|
+
name: m.name,
|
|
400
|
+
size: m.size,
|
|
401
|
+
modifiedAt: m.modified_at,
|
|
402
|
+
}))
|
|
403
|
+
} catch {
|
|
404
|
+
return []
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export async function selectBestModel(profile) {
|
|
409
|
+
const models = await listOllamaModels()
|
|
410
|
+
if (models.length === 0) return null
|
|
411
|
+
|
|
412
|
+
const profileName = profile?.name || "medium"
|
|
413
|
+
|
|
414
|
+
// Preferencias segΓΊn perfil β mapeadas a modelos que existen en esta instancia
|
|
415
|
+
const preferences = {
|
|
416
|
+
small: ["qwen2.5-coder:7b", "qwen2.5-coder", "llama3.2", "llama3.2:latest", "phi", "deepseek-coder"],
|
|
417
|
+
medium: ["qwen2.5-coder:7b", "qwen2.5-coder", "codellama", "mistral", "llama3"],
|
|
418
|
+
large: ["qwen2.5-72b", "llama-3-70b", "llama3:latest", "qwen2.5-coder:7b"],
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const preferred = preferences[profileName] || preferences.medium
|
|
422
|
+
|
|
423
|
+
// Buscar el mejor modelo disponible segΓΊn preferencias
|
|
424
|
+
for (const pref of preferred) {
|
|
425
|
+
const match = models.find((m) => m.name === pref || m.name.toLowerCase().includes(pref))
|
|
426
|
+
if (match) {
|
|
427
|
+
_bestModelCache = match.name
|
|
428
|
+
return match.name
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Fallback: primer modelo disponible (excluir embeddings)
|
|
433
|
+
const nonEmbedding = models.find((m) => !m.name.includes("nomic") && !m.name.includes("embed"))
|
|
434
|
+
if (nonEmbedding) {
|
|
435
|
+
_bestModelCache = nonEmbedding.name
|
|
436
|
+
return nonEmbedding.name
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Γltimo recurso
|
|
440
|
+
_bestModelCache = models[0].name
|
|
441
|
+
return models[0].name
|
|
442
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @use(kind, contract, limit)
|
|
2
|
+
// @kind(util)
|
|
3
|
+
// @contract(in: none -> out: agentTypes, agentState, agentLogic, agentValidate, agentCleaner, registry)
|
|
4
|
+
// @limit(lines: 60)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Export unificado de agentes especializados del mΓ³dulo Boost.
|
|
8
|
+
*
|
|
9
|
+
* Cada agente exporta:
|
|
10
|
+
* role β string identificador
|
|
11
|
+
* buildPrompt β (input) => { system, prompt }
|
|
12
|
+
* validate β (output) => boolean
|
|
13
|
+
* parseOutput β (rawText) => parsed
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { buildPrompt } from "../agent-pool.js"
|
|
17
|
+
|
|
18
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
19
|
+
// Registry de agentes
|
|
20
|
+
// ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
21
|
+
|
|
22
|
+
const AGENTS = {
|
|
23
|
+
types: {
|
|
24
|
+
role: "types",
|
|
25
|
+
label: "Type Designer",
|
|
26
|
+
description: "Define tipos e interfaces",
|
|
27
|
+
buildPrompt: (input) => buildPrompt("types", input),
|
|
28
|
+
validateOutput: (text) => text.includes("interface") || text.includes("type "),
|
|
29
|
+
parseOutput: (text) => text.trim(),
|
|
30
|
+
},
|
|
31
|
+
state: {
|
|
32
|
+
role: "state",
|
|
33
|
+
label: "State Architect",
|
|
34
|
+
description: "DiseΓ±a estado y efectos",
|
|
35
|
+
buildPrompt: (input) => buildPrompt("state", input),
|
|
36
|
+
validateOutput: (text) => text.includes("useState") || text.includes("useEffect") || text.includes("useReducer"),
|
|
37
|
+
parseOutput: (text) => text.trim(),
|
|
38
|
+
},
|
|
39
|
+
logic: {
|
|
40
|
+
role: "logic",
|
|
41
|
+
label: "Logic Implementer",
|
|
42
|
+
description: "Implementa lΓ³gica de negocio",
|
|
43
|
+
buildPrompt: (input) => buildPrompt("logic", input),
|
|
44
|
+
validateOutput: (text) => text.length > 50,
|
|
45
|
+
parseOutput: (text) => text.trim(),
|
|
46
|
+
},
|
|
47
|
+
cleaner: {
|
|
48
|
+
role: "cleaner",
|
|
49
|
+
label: "Context Cleaner",
|
|
50
|
+
description: "Comprime contexto eliminando secciones irrelevantes",
|
|
51
|
+
buildPrompt: (input) => buildPrompt("cleaner", input),
|
|
52
|
+
validateOutput: (text) => text.length > 0, // cleaner debe reducir contexto
|
|
53
|
+
parseOutput: (text) => text.trim(),
|
|
54
|
+
},
|
|
55
|
+
validate: {
|
|
56
|
+
role: "validate",
|
|
57
|
+
label: "Code Validator",
|
|
58
|
+
description: "Corrige errores de validaciΓ³n",
|
|
59
|
+
buildPrompt: (input) => buildPrompt("validate", input),
|
|
60
|
+
validateOutput: (text) => text.length > 20,
|
|
61
|
+
parseOutput: (text) => text.trim(),
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getAgent(role) {
|
|
66
|
+
const agent = AGENTS[role]
|
|
67
|
+
if (!agent) throw new Error(`Agente desconocido: "${role}". Agentes: ${Object.keys(AGENTS).join(", ")}`)
|
|
68
|
+
return agent
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function listAgents() {
|
|
72
|
+
return Object.entries(AGENTS).map(([key, agent]) => ({
|
|
73
|
+
id: key,
|
|
74
|
+
label: agent.label,
|
|
75
|
+
description: agent.description,
|
|
76
|
+
}))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default AGENTS
|