UNPKG

@nestbox-ai/cli

Version:

The cli tools that helps developers to build agents

225 lines 10.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_MODEL = void 0; exports.runDocProcAgent = runDocProcAgent; const sdk_1 = __importDefault(require("@anthropic-ai/sdk")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const js_yaml_1 = __importDefault(require("js-yaml")); const ajv_1 = __importDefault(require("ajv")); // ─── Constants ──────────────────────────────────────────────────────────────── const AGENTS_DIR = __dirname; exports.DEFAULT_MODEL = 'claude-sonnet-4-6'; const DEFAULT_MAX_ITERATIONS = 8; // ─── Helpers ───────────────────────────────────────────────────────────────── function readLocalFile(filename) { return fs_1.default.readFileSync(path_1.default.join(AGENTS_DIR, filename), 'utf8'); } function validateYaml(content, schemaContent) { var _a; // Replace ${ENV_VAR} references with a plain string so YAML parses cleanly // and AJV treats them as valid string values. const preprocessed = content.replace(/\$\{[^}]+\}/g, 'env-var-placeholder'); let data; try { data = js_yaml_1.default.load(preprocessed); } catch (e) { return { valid: false, errors: [`YAML parse error: ${e.message}`] }; } let schema; try { schema = js_yaml_1.default.load(schemaContent); } catch (e) { return { valid: false, errors: [`Schema parse error: ${e.message}`] }; } const ajv = new ajv_1.default({ strict: false, allErrors: true }); // Suppress "unknown format 'uri' ignored" console warnings from AJV ajv.addFormat('uri', () => true); const validate = ajv.compile(schema); const ok = validate(data); if (ok) return { valid: true, errors: [] }; const errors = ((_a = validate.errors) !== null && _a !== void 0 ? _a : []).map((err) => { const loc = err.instancePath || '(root)'; return `[${loc}] ${err.message}`; }); return { valid: false, errors }; } // ─── System Prompt ──────────────────────────────────────────────────────────── function buildSystemPrompt() { const systemPrompt = readLocalFile('SYSTEM_PROMPT.md'); const configGuide = readLocalFile('CONFIG_GUIDE.md'); const evalGuide = readLocalFile('EVAL_GUIDE.md'); return `${systemPrompt}\n\n---\n\n${configGuide}\n\n---\n\n${evalGuide}`; } // ─── Tool Definitions ───────────────────────────────────────────────────────── const TOOLS = [ { name: 'write_and_validate_config', description: 'Write the config.yaml content and validate it against the schema. Returns "VALID" on success or a list of validation errors to fix.', input_schema: { type: 'object', properties: { yaml_content: { type: 'string', description: 'The complete YAML content for config.yaml', }, }, required: ['yaml_content'], }, }, { name: 'write_and_validate_eval', description: 'Write the eval.yaml content and validate it against the schema. Returns "VALID" on success or a list of validation errors to fix.', input_schema: { type: 'object', properties: { yaml_content: { type: 'string', description: 'The complete YAML content for eval.yaml', }, }, required: ['yaml_content'], }, }, { name: 'finish', description: 'Signal that both files are complete and valid. Call this only after both write_and_validate_config and write_and_validate_eval have returned "VALID".', input_schema: { type: 'object', properties: { summary: { type: 'string', description: 'Brief summary of what was generated and why key choices were made', }, }, required: ['summary'], }, }, ]; // ─── Agent ──────────────────────────────────────────────────────────────────── function runDocProcAgent(options) { return __awaiter(this, void 0, void 0, function* () { const { instructions, anthropicApiKey, model = exports.DEFAULT_MODEL, maxIterations = DEFAULT_MAX_ITERATIONS, maxTokens = 8096, onProgress = () => { }, } = options; const configSchema = readLocalFile('config.schema.yaml'); const evalSchema = readLocalFile('eval-test-cases.schema.yaml'); const client = new sdk_1.default({ apiKey: anthropicApiKey }); let latestConfig = ''; let latestEval = ''; let configValid = false; let evalValid = false; let finished = false; let iteration = 0; function executeTool(name, input) { var _a, _b; if (name === 'write_and_validate_config') { const content = (_a = input.yaml_content) !== null && _a !== void 0 ? _a : ''; latestConfig = content; const result = validateYaml(content, configSchema); configValid = result.valid; if (result.valid) return 'VALID'; return `VALIDATION ERRORS — fix all of these before calling again:\n${result.errors.map((e) => ` • ${e}`).join('\n')}`; } if (name === 'write_and_validate_eval') { const content = (_b = input.yaml_content) !== null && _b !== void 0 ? _b : ''; latestEval = content; const result = validateYaml(content, evalSchema); evalValid = result.valid; if (result.valid) return 'VALID'; return `VALIDATION ERRORS — fix all of these before calling again:\n${result.errors.map((e) => ` • ${e}`).join('\n')}`; } if (name === 'finish') { finished = true; return 'Done.'; } return `Unknown tool: ${name}`; } // Build the system prompt once and mark it for caching. // On iteration 1 the prompt is written to the cache (normal price). // On iterations 2+ the large system prompt is read from cache at // 10% of the normal input token price. const systemPromptText = buildSystemPrompt(); const systemPrompt = [ { type: 'text', text: systemPromptText, cache_control: { type: 'ephemeral' }, }, ]; const messages = [ { role: 'user', content: `Here are the instructions for the pipeline you need to configure:\n\n${instructions}\n\nGenerate the config.yaml and eval.yaml files now. Use the tools to write and validate them.`, }, ]; onProgress('Starting agent...'); while (iteration < maxIterations && !finished) { iteration++; onProgress(`Iteration ${iteration}/${maxIterations} — calling Claude...`); const response = yield client.messages.create({ model, max_tokens: maxTokens, system: systemPrompt, tools: TOOLS, // Force Claude to call a tool every turn — prevents it from // replying with plain text and exiting the loop prematurely. tool_choice: { type: 'any' }, messages, }); // Append assistant turn messages.push({ role: 'assistant', content: response.content }); if (response.stop_reason !== 'tool_use') { onProgress(`Agent stopped unexpectedly: ${response.stop_reason}`); break; } // Process all tool calls and collect results const toolResults = []; for (const block of response.content) { if (block.type !== 'tool_use') continue; onProgress(` → tool: ${block.name}`); const result = executeTool(block.name, block.input); onProgress(` ${result.startsWith('VALID') ? '✓ valid' : result.split('\n')[0]}`); toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: result, }); if (finished) break; } if (toolResults.length > 0) { messages.push({ role: 'user', content: toolResults }); } if (finished) break; } if (iteration >= maxIterations && !finished) { onProgress(`Warning: reached max iterations (${maxIterations}) without finishing.`); } return { configYaml: latestConfig, evalYaml: latestEval, iterations: iteration, configValid, evalValid, }; }); } //# sourceMappingURL=anthropic.js.map