Files
Arcrun/cypher-executor/src/lib/prompt-recipe-schema.ts
T
uncle6me-web 922a57fe34 arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。

此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在
richblack/arcrun 與本地 backup 分支)。含:
- acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe)
- recipe push 把關(資料外流提醒 + 打通檢查)
- 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1)
- CLI / cypher-executor / registry / 完整 SDD

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:52:38 +08:00

91 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* prompt_recipe Zod schema
* SDD: matrix/arcrun/.agents/specs/recipe-system/design.md §2.1
*
* 平行於既有 auth_recipe / api_recipe,存 RECIPES KV (key: `prompt_recipe:{name}`)
* 容器 + recipe 模式:claude_api 是容器,recipe 是配方
*/
import { z } from 'zod';
// ── Transform 白名單 ──────────────────────────────────────────────────────────
// 限制 transform 種類避免變 mini-DSL;超過範圍請寫零件
export const TRANSFORM_NAMES = [
'json_array', // array → JSON.stringify 整體
'to_string', // 任意值 → String(x)
'join', // array → join(sep)sep 預設換行
'markdown_list', // array → "- a\n- b\n- c"
'extract_field', // array of object → 抽 field 後的 array(再可串其他 transform
'first', // array → first element(取單一)
'pluck_content', // KBDB blocks array → 抽 content 後 join 雙換行(草稿合併常用)
] as const;
/** transform 表示法:name 或 name:arg(如 extract_field:page_name */
export const TransformSchema = z.string().regex(/^[a-z_]+(:.+)?$/, 'transform 必須為 name 或 name:arg 格式');
// ── Fragment:從 KBDB / KV 抓固定資料 ──────────────────────────────────────────
export const KBDBBlockFragmentSchema = z.object({
var: z.string().min(1), // prompt template 內的變數名
source: z.literal('kbdb_block'),
block_id: z.string().optional(), // 二擇一
block_page_name: z.string().optional(), // 比 block_id 穩定
field: z.string().default('content'), // 抓 block 的哪個欄位
});
export const KVFragmentSchema = z.object({
var: z.string().min(1),
source: z.literal('kv'),
key: z.string().min(1),
});
// discriminatedUnion 對 refined zod object 不支援,故拆成驗證後 + 單獨檢查 block_id|page_name
export const FragmentSchema = z.discriminatedUnion('source', [
KBDBBlockFragmentSchema,
KVFragmentSchema,
]).superRefine((d, ctx) => {
if (d.source === 'kbdb_block' && !d.block_id && !d.block_page_name) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'block_id 或 block_page_name 必填其一',
});
}
});
// ── Input:從 workflow context 取值(含 transform) ────────────────────────────
export const InputSchema = z.object({
var: z.string().min(1),
from: z.string().min(1), // JSONPath-lite,如 "ctx.read_drafts.blocks"
transform: TransformSchema.optional(),
default: z.unknown().optional(), // from 取不到時的預設值(避免炸 prompt)
});
// ── Prompt 組裝 ──────────────────────────────────────────────────────────────
export const PromptAssemblySchema = z.object({
system: z.string().min(1), // 模板,可含 {{var}}
user: z.string().min(1),
});
// ── 輸出規格 ──────────────────────────────────────────────────────────────────
export const OutputSpecSchema = z.object({
format: z.enum(['text', 'json']).default('text'),
// 若 format=json,可選 schema 做 parse 後驗證(簡化版,列必填欄位即可)
required_fields: z.array(z.string()).optional(),
});
// ── 完整 prompt_recipe 定義 ────────────────────────────────────────────────────
export const PromptRecipeSchema = z.object({
kind: z.literal('prompt_recipe'),
name: z.string().min(1).regex(/^[a-z][a-z0-9_]*$/, 'name 為 lowercase + underscore'),
version: z.number().int().positive().default(1),
description: z.string().optional(),
model: z.enum(['haiku', 'sonnet', 'opus']).default('sonnet'),
fragments: z.array(FragmentSchema).default([]),
inputs: z.array(InputSchema).default([]),
prompt_assembly: PromptAssemblySchema,
output: OutputSpecSchema.default({ format: 'text' }),
});
export type PromptRecipe = z.infer<typeof PromptRecipeSchema>;
export type Fragment = z.infer<typeof FragmentSchema>;
export type RecipeInput = z.infer<typeof InputSchema>;