合流 batch:步驟 3/4/5/6 併成一版出貨(同一顆 cypher worker,bundle 是 worker 粒度)

鐵律(08-01 血淚):CP 可分步驗收,但 bundle 是 worker 粒度——凡改同一顆 worker 的多步,
出貨前必先合流成一版再重生 bundle,否則 stage 只拿到一步(三條分支各自建 bundle 的坑)。

base=feat/step3-missing-guidance(最前緣,已含 step4+step6+t158-t162 prod 修)
merge=feat/step5-conditional-edges(引擎條件邊+recipe 三層+教材世代修)
This commit is contained in:
uncle6me-web
2026-07-31 16:56:09 +08:00
21 changed files with 1544 additions and 18 deletions
+26
View File
@@ -16,6 +16,7 @@
import { Hono } from 'hono';
import type { Bindings } from '../types';
import { deriveRecipeHash } from '../lib/hash';
import type { ResponseMap } from '../lib/recipe-payload';
export const recipesRouter = new Hono<{ Bindings: Bindings }>();
@@ -34,6 +35,26 @@ export interface RecipeDefinition {
method?: string; // GET | POST | PUT | PATCH | DELETE,預設 POST
headers?: Record<string, string>;
body?: Record<string, unknown>;
/**
* ③ payload 層(SDD workflow-discovery 3.12):帶 body 的 API 把 payload 收回 recipe
* 不必寫進 workflow code。與 `body` 的差別=支援巢狀 {{var}} 與 dot path、
* 單一引用保留原型別。兩者並存時 body_template 優先(新欄位贏,舊 recipe 不受影響)。
*/
body_template?: Record<string, unknown>;
/**
* ③ 回應正規化層:各家 API 回應形狀不同(GeminiClaudeWorkers AI),
* 取值路徑・思考型模型旗標・淨化規則**隨 recipe 走** ⇒ 換源=換 recipe,不必改 workflow。
* 未設=原樣回傳(既有 recipe 行為零變化)。
*/
response_map?: ResponseMap;
/**
* 認證型別。未設=沿用既有 auth_service 判斷(向後相容)。
* `binding`**免金鑰**,用平台內建能力(env.AIVECTORIZEBROWSERQUEUE),
* 不是為 Workers AI 開特例——Cloudflare 這一整類都被舊抽象(只認 HTTP+金鑰)排除在外。
*/
auth?: 'static_key' | 'service_account' | 'oauth2' | 'binding';
/** auth='binding' 時指定用哪個 binding(例 'AI''VECTORIZE')。 */
binding_name?: string;
/**
* 此 recipe 要用哪個 auth recipeauth_recipe:{auth_service})。
* 讓多個 recipe 共用同一把 auth(例:kbdb_get / kbdb_create_block 都設 "kbdb")。
@@ -116,6 +137,11 @@ recipesRouter.post('/recipes', async (c) => {
method: (body.method ?? 'POST').toUpperCase(),
headers: body.headers,
body: body.body,
// ③ payload/回應/binding 三層(3.12):全選填,沒給就是 undefined=既有行為
body_template: body.body_template,
response_map: body.response_map,
auth: body.auth,
binding_name: body.binding_name,
auth_service: body.auth_service,
credentials_required: body.credentials_required,
created_at: existing?.created_at ?? now,