ai: rich-output blocks via lazy-fetched typed-fence protocol
Assistant replies can now emit typed fenced blocks that render as @crema/*-ui components inline at their position in the reply. - message-body.tsx: segmented rendering — alternating prose chunks and block dispatch (was: all blocks appended at end). Renderers for kpi, table, chart-bar/-line/-donut/-spark, code, diff, flowchart, orgchart, steps, checklist, welcome, hint, plus the legacy card kinds. - block-schemas.ts: single source of truth — BLOCK_INDEX (one-line purpose per kind, always in prompt) + SCHEMAS (full JSON shape + example, fetched on demand). - admin-tools.ts: new get_block_schema(kind) tool the model calls once per kind per thread to fetch the exact schema. Keeps the always-on prompt small (~110 tokens vs ~400 inline). - assistant.tsx: replaces the inline schema dump with the generated thin index. - ai.tsx: empty-state preview button injects a synthetic assistant message exercising every block, for renderer/theme smoke-testing. - console.css + ai.tsx: shrink ATLAS headline so it doesn't slip under the composer with the added preview button. - tsconfig.json + app.css: wire lib-data-ui, lib-code-ui, lib-diagram-ui, lib-onboarding-ui as siblings. Adding a new block kind = add the lib paths, add a renderer case, add a schema entry. No prompt edits required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
||||
suspendTenant,
|
||||
type Tenant,
|
||||
} from "~/lib/arcadia/tenants"
|
||||
import { searchDocs } from "~/lib/docs-search"
|
||||
import { BLOCK_INDEX, getBlockSchema } from "~/lib/block-schemas"
|
||||
|
||||
export type ToolCall = {
|
||||
name: string
|
||||
@@ -221,6 +223,71 @@ const TOOLS: ToolDef[] = [
|
||||
return summarize(updated)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "search_docs",
|
||||
description:
|
||||
"Search the arcadia-app documentation (architecture, API surface, deploy/setup guides) for passages relevant to a question. Use this for conceptual or procedural questions where the live API tools won't help — e.g. 'how does multi-tenant isolation work', 'how do I deploy to production', 'what is the modular monolith pattern'. Returns up to `limit` ranked passages with title, sourcePath, and excerpt. Cite results by sourcePath in your reply.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
description:
|
||||
"Lexical search query. Use specific terms from the docs (endpoint names, schema fields, concept names) — paraphrase poorly.",
|
||||
},
|
||||
limit: {
|
||||
type: "integer",
|
||||
description: "Max passages to return. Default 5, cap 10.",
|
||||
minimum: 1,
|
||||
maximum: 10,
|
||||
},
|
||||
},
|
||||
required: ["query"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
isWrite: false,
|
||||
run: async (args) => {
|
||||
const query = typeof args.query === "string" ? args.query.trim() : ""
|
||||
if (!query) throw new Error("search_docs requires a non-empty { query }")
|
||||
const limit = Math.min(
|
||||
10,
|
||||
Math.max(1, typeof args.limit === "number" ? args.limit : 5),
|
||||
)
|
||||
const hits = await searchDocs(query, limit)
|
||||
return { query, count: hits.length, hits }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get_block_schema",
|
||||
description: `Fetch the full JSON schema + example for a rich-output block kind so you can emit it correctly in your reply. Call this the first time in a thread that you intend to render a particular kind. Available kinds: ${Object.entries(
|
||||
BLOCK_INDEX,
|
||||
)
|
||||
.map(([k, v]) => `${k} (${v})`)
|
||||
.join(", ")}.`,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
kind: {
|
||||
type: "string",
|
||||
description: "The block kind to fetch the schema for.",
|
||||
enum: Object.keys(BLOCK_INDEX),
|
||||
},
|
||||
},
|
||||
required: ["kind"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
isWrite: false,
|
||||
run: async (args) => {
|
||||
const kind = typeof args.kind === "string" ? args.kind : ""
|
||||
const schema = getBlockSchema(kind)
|
||||
if (!schema) {
|
||||
return {
|
||||
error: `Unknown block kind "${kind}". Available: ${Object.keys(BLOCK_INDEX).join(", ")}.`,
|
||||
}
|
||||
}
|
||||
return { kind, schema }
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
interface AuditEntry {
|
||||
|
||||
Reference in New Issue
Block a user