diff --git a/cypher-executor/src/routes/portal-data.ts b/cypher-executor/src/routes/portal-data.ts index 54fdb24..6dd5ad3 100644 --- a/cypher-executor/src/routes/portal-data.ts +++ b/cypher-executor/src/routes/portal-data.ts @@ -199,6 +199,43 @@ portalDataRouter.get('/portal/data/graph/neighbors/:name', (c) => }), ); +// GET /portal/data/chat?question=... — AI 問答(portal-demo-suite)。 +// 設計哲學:AI 檢索=用戶手動搜尋同一套——同 search 的 requirePortalUser 閘、同一個租戶資料面, +// 只是把「人下關鍵字」換成「workflow 代查再作答」;前端不因走 AI 多拿任何權限。 +// 機制同 graph_neighbors:in-process 執行 tenant 的 rag_chat workflow(executeWebhookGraph, +// 絕不 fetch 自己 hostname);workflow 不存在 → 誠實 404,不假裝這實例有問答能力。 +portalDataRouter.get('/portal/data/chat', (c) => + run(c, async () => { + const auth = await requirePortalUser(c); + if (!auth.ok) return auth.res; + const question = c.req.query('question'); + if (!question) return c.json({ error: 'question 必填' }, 400); + + const wfGraph = await getTenantWorkflowGraph(c.env, 'rag_chat'); + if (!wfGraph) return c.json({ error: '此實例未安裝問答 workflow' }, 404); + + const result = await executeWebhookGraph( + c.env, + wfGraph, + { question }, + 'rag_chat', + portalTenant(c.env), + c.executionCtx, + ); + if (!result.success) { + // workflow 執行失敗 → 誠實 502(不把錯誤編成答案) + return c.json({ error: `rag_chat workflow 執行失敗:${result.error ?? '未知錯誤'}` }, 502); + } + // 回 workflow 回應內層 data:{answer, sources, graph_facts}(缺欄位誠實回空,不編造) + const inner = unwrapWorkflowData(result.data, 'answer'); + return c.json({ + answer: typeof inner.answer === 'string' ? inner.answer : '', + sources: Array.isArray(inner.sources) ? inner.sources : [], + graph_facts: inner.graph_facts ?? null, + }); + }), +); + // GET /portal/data/workflows — 工作流顯示(D-8):唯讀 list+每條的最近一次執行,**不開 trigger** //(trigger 是 owner/console 的事;回應也不含 webhook_url,不給可打的把手)。 // 可見性:PORTAL_SHOW_WORKFLOWS=admin(預設,role 閘 403)/ all / off(整頁不存在 → 404)。 diff --git a/cypher-executor/src/routes/portal-ui.ts b/cypher-executor/src/routes/portal-ui.ts index 654f1d5..26a7ae6 100644 --- a/cypher-executor/src/routes/portal-ui.ts +++ b/cypher-executor/src/routes/portal-ui.ts @@ -236,6 +236,17 @@ function renderPortalHtml(brand: string): string { + +