UNPKG

mlld

Version:

mlld: llm scripting language

275 lines (271 loc) 11.1 kB
import { evaluateExecInvocation } from './chunk-KZFFCWXR.mjs'; import { interpolate } from './chunk-IA26UJYI.mjs'; import { InterpolationContext } from './chunk-PHUTH3LV.mjs'; import { logger } from './chunk-M3R2H5KU.mjs'; import { createSimpleTextVariable, isExecutableVariable } from './chunk-RKGZ44GZ.mjs'; import { __name } from './chunk-NJQT543K.mjs'; // interpreter/eval/prose-execution.ts function buildSkillInjectionPrompt(skills) { const runSkill = skills.find((s) => s.includes("run")) || skills[0]; return `Run /${runSkill} with this program: `; } __name(buildSkillInjectionPrompt, "buildSkillInjectionPrompt"); function buildSkillInjectionEnd(skills) { return ` Return only the output. If the skill is unavailable, respond: ERROR: SKILLS_NOT_FOUND`; } __name(buildSkillInjectionEnd, "buildSkillInjectionEnd"); async function executeProseExecutable(definition, args, env) { if (process.env.DEBUG_EXEC) { logger.debug("Executing prose executable:", { contentType: definition.contentType, hasConfig: !!definition.configRef, paramNames: definition.paramNames }); } const configRef = definition.configRef; if (!configRef || configRef.length === 0) { throw new Error("Prose executable missing config reference"); } const configRefNode = configRef[0]; let configVarName; if (configRefNode.type === "VariableReference") { configVarName = configRefNode.identifier; } else if (configRefNode.type === "Text") { configVarName = configRefNode.content; } else { throw new Error("Invalid config reference in prose executable"); } const configVar = env.getVariable(configVarName); if (!configVar) { throw new Error(`Prose config not found: @${configVarName}`); } const config = extractProseConfig(configVar, configVarName, env); const proseEnv = env.createChild(); for (const [key, value] of Object.entries(args)) { proseEnv.setParameterVariable(key, createSimpleTextVariable(key, value)); } let proseContent; if (definition.contentType === "inline") { if (!definition.contentTemplate) { throw new Error("Inline prose executable missing content"); } proseContent = await interpolate(definition.contentTemplate, proseEnv, InterpolationContext.Default); } else if (definition.contentType === "file") { if (!definition.pathTemplate) { throw new Error("File-based prose executable missing path"); } const filePath = await interpolate(definition.pathTemplate, proseEnv, InterpolationContext.Default); let fileContent; try { fileContent = await env.readFile(filePath); } catch (err) { throw new Error(`Failed to read prose file "${filePath}": ${err.message || err}`); } const lowerPath = filePath.toLowerCase(); if (lowerPath.endsWith(".prose.att")) { proseContent = await parseAndInterpolateTemplate(fileContent, "att", proseEnv); } else if (lowerPath.endsWith(".prose.mtt")) { proseContent = await parseAndInterpolateTemplate(fileContent, "mtt", proseEnv); } else { proseContent = await interpolateProseTemplate(fileContent, proseEnv); } } else if (definition.contentType === "template") { if (!definition.pathTemplate) { throw new Error("Template prose executable missing path"); } const filePath = await interpolate(definition.pathTemplate, proseEnv, InterpolationContext.Default); let fileContent; try { fileContent = await env.readFile(filePath); } catch (err) { throw new Error(`Failed to read prose template "${filePath}": ${err.message || err}`); } const lowerPath = filePath.toLowerCase(); const templateStyle = lowerPath.endsWith(".mtt") ? "mtt" : "att"; proseContent = await parseAndInterpolateTemplate(fileContent, templateStyle, proseEnv); } else { throw new Error(`Unknown prose content type: ${definition.contentType}`); } if (!proseContent || proseContent.trim() === "") { const source = definition.contentType === "inline" ? "inline block" : definition.contentType === "file" ? "file" : "template"; throw new Error(`Prose ${source} is empty. Prose content must contain at least one instruction.`); } const skillPrompt = config.skillPrompt || buildSkillInjectionPrompt(config.skills); const skillPromptEnd = config.skillPromptEnd || buildSkillInjectionEnd(config.skills); const fullPrompt = skillPrompt + proseContent + skillPromptEnd; if (process.env.DEBUG_EXEC) { logger.debug("Prose prompt constructed:", { contentLength: proseContent.length, fullPromptLength: fullPrompt.length, modelName: config.modelName, skills: config.skills }); } const result = await invokeModelExecutor(fullPrompt, config, env); if (result.includes("ERROR: SKILLS_NOT_FOUND:")) { throw new Error(`Prose execution failed: OpenProse skills not available. Skills must be installed AND approved. Required skills: ${config.skills.join(", ")}`); } return result; } __name(executeProseExecutable, "executeProseExecutable"); function extractProseConfig(configVar, configVarName, env) { const value = configVar.value; if (value === null || value === void 0) { throw new Error(`Prose config @${configVarName} is ${value === null ? "null" : "undefined"}. Expected an object with { model: @executor, skillName?: string }.`); } if (typeof value === "object" && !Array.isArray(value)) { if (!value.model) { throw new Error(`Prose config @${configVarName} missing required 'model' field. Expected: { model: @opus } where @opus is an executable from @mlld/claude.`); } let modelVar; let modelName; if (isExecutableVariable(value.model)) { modelVar = value.model; modelName = modelVar.name || "model"; } else if (typeof value.model === "string") { const resolved = env.getVariable(value.model); if (!resolved) { throw new Error(`Prose config @${configVarName}.model references unknown variable @${value.model}. Import an executor like: import { @opus } from @mlld/claude`); } if (!isExecutableVariable(resolved)) { throw new Error(`Prose config @${configVarName}.model must be an executable, but @${value.model} is not. Use an executor like @opus from @mlld/claude.`); } modelVar = resolved; modelName = value.model; } else { throw new Error(`Prose config @${configVarName}.model must be an executable (like @opus from @mlld/claude), got ${typeof value.model}.`); } if (value.skills !== void 0 && !Array.isArray(value.skills)) { throw new Error(`Prose config @${configVarName}.skills must be an array, got ${typeof value.skills}.`); } const defaultSkills = [ "open-prose:prose-boot", "open-prose:prose-compile", "open-prose:prose-run" ]; return { model: modelVar, modelName, cwd: value.cwd, skills: value.skills || defaultSkills, skillPrompt: value.skillPrompt, skillPromptEnd: value.skillPromptEnd, maxTokens: value.maxTokens, temperature: value.temperature }; } if (isExecutableVariable(configVar)) { return { model: configVar, modelName: configVar.name || configVarName, skills: [ "open-prose:prose-boot", "open-prose:prose-compile", "open-prose:prose-run" ] }; } if (Array.isArray(value)) { throw new Error(`Prose config @${configVarName} is an array. Expected: { model: @opus } where @opus is an executable.`); } throw new Error(`Prose config @${configVarName} has invalid type '${typeof value}'. Expected an object with { model: @executor } or an executable directly.`); } __name(extractProseConfig, "extractProseConfig"); async function parseAndInterpolateTemplate(templateContent, style, env) { const { parseSync } = await import('./parser-6HNWFG6W.mjs'); const startRule = style === "mtt" ? "TemplateBodyMtt" : "TemplateBodyAtt"; let templateNodes; try { templateNodes = parseSync(templateContent, { startRule }); } catch (parseErr) { if (process.env.DEBUG_EXEC) { logger.debug("Template parse failed, using fallback:", parseErr.message); } if (style === "mtt") { const normalized = templateContent.replace(/{{\s*([A-Za-z_][\w.]*)\s*}}/g, "@$1"); return interpolateProseTemplate(normalized, env); } return interpolateProseTemplate(templateContent, env); } return interpolate(templateNodes, env, InterpolationContext.Default); } __name(parseAndInterpolateTemplate, "parseAndInterpolateTemplate"); async function interpolateProseTemplate(templateContent, env) { const regex = /@([a-zA-Z_][\w.]*)/g; let result = templateContent; let match; const seen = /* @__PURE__ */ new Set(); while ((match = regex.exec(templateContent)) !== null) { const varPath = match[1]; if (seen.has(varPath)) continue; seen.add(varPath); const parts = varPath.split("."); const varName = parts[0]; const variable = env.getVariable(varName); if (variable) { let value = variable.value; for (let i = 1; i < parts.length; i++) { if (value && typeof value === "object") { value = value[parts[i]]; } else { value = void 0; break; } } if (value !== void 0) { const stringValue = typeof value === "string" ? value : JSON.stringify(value); result = result.replace(new RegExp(`@${varPath.replace(".", "\\.")}`, "g"), stringValue); } } } return result; } __name(interpolateProseTemplate, "interpolateProseTemplate"); async function invokeModelExecutor(prompt, config, env) { const modelVar = config.model; const modelName = config.modelName; if (!isExecutableVariable(modelVar)) { throw new Error(`Prose config model is not an executable. Expected an executor like @opus from @mlld/claude.`); } const commandRef = { type: "CommandReference", identifier: modelName, args: [ { type: "VariableReference", identifier: "__prose_prompt__", location: null } ] }; const invocation = { type: "ExecInvocation", commandRef, location: null }; const execEnv = env.createChild(); execEnv.setVariable("__prose_prompt__", createSimpleTextVariable("__prose_prompt__", prompt)); if (!execEnv.getVariable(modelName)) { execEnv.setVariable(modelName, modelVar); } try { const result = await evaluateExecInvocation(invocation, execEnv); if (typeof result.value === "string") { return result.value; } if (result.value && typeof result.value === "object" && "value" in result.value) { return String(result.value.value); } return String(result.value ?? ""); } catch (err) { throw new Error(`Prose execution via @${modelName} failed: ${err.message || err}`); } } __name(invokeModelExecutor, "invokeModelExecutor"); export { executeProseExecutable }; //# sourceMappingURL=prose-execution-BCUOZNHM.mjs.map //# sourceMappingURL=prose-execution-BCUOZNHM.mjs.map