mlld
Version:
mlld: llm scripting language
275 lines (271 loc) • 11.1 kB
JavaScript
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