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>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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>;
|
||||
Reference in New Issue
Block a user