Skip to content

Commit 32bb1d7

Browse files
os-zhuangclaude
andauthored
fix(console): the API console lists the whole AI family, and the tool preview stops linking to a 404 (framework#3718) (#2925)
#2921 fixed this page for ONE server-side builder, `buildAIRoutes()`. The `/api/v1/ai/**` family is seven builders plus one route mounted by `objectos-runtime`, so the AI group still showed under half of what exists. All 26 routes are here now, grouped as cloud's ledger groups them (objectstack-ai/cloud#903 widens the audit on the server side). Fourteen entries added: `/agents` ×2, `/assistant` ×3, `/tools` ×2, `/pending-actions` ×4, `/evals/runs`, `/conversations/:id/debug`, `/usage`. Two behavioural details carried over from the #2921 finding rather than re-learned: - `/agents/:agentName/chat` and `/assistant/chat` are dual-mode on the same `stream !== false` flag `/chat` is, so their "try it" templates send `stream: false`. Without it the console buffers an SSE body through `res.text()` and renders a wall of `data:` frames for a JSON-shaped request. - `/tools/:toolName/execute` needs `{ parameters: {} }`; `/evals/runs` needs `{ caseId }` and makes real paid LLM calls, which its description says. Separately, ToolPreview's "Open in API Console" deep-linked to `/api/v1/ai/tools/:toolName/invoke`. That verb has never been mounted — the route is `/execute` — so the one first-party caller of the tool routes pointed at a guaranteed 404, the same shape as the three dead AI endpoints #2921 removed from this very page. Mount gating is documented where it matters: four of the seven builders are conditional on a metadata service and/or a data engine, so on a stripped host those routes 404 because they are NOT MOUNTED, which is a different fact from "they do not exist" — the distinction #2921 was about. Verification: `vitest run apps/console/src/pages/developer` 8/8 across 2 files; eslint on both changed files 0 errors (10 pre-existing warnings, none on touched lines). Data and one string literal — no component or hook logic. Claude-Session: https://claude.ai/code/session_01WJX6GnuNix7HisBc92THMN Co-authored-by: Claude <noreply@anthropic.com>
1 parent b5efe85 commit 32bb1d7

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

apps/console/src/pages/developer/hooks/useApiDiscovery.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,33 @@ function buildAuthEndpoints(authBase: string): EndpointDef[] {
4545
}
4646

4747
export const SERVICE_ENDPOINT_CATALOG: Record<string, { group: string; defaultRoute: string; endpoints: ServiceEndpointEntry[] }> = {
48-
// The twelve routes `buildAIRoutes()` mounts, in table order (framework#3718).
48+
// The `/api/v1/ai/**` family, in ledger order (framework#3718).
49+
//
4950
// `/nlq`, `/suggest` and `/insights` used to sit here with "try it" bodies and
5051
// always 404'd — they were declared in the framework's `DEFAULT_AI_ROUTES` and
5152
// never implemented anywhere, so this page offered three endpoints that had no
5253
// server. The audited table is cloud's `packages/service-ai/src/
5354
// ai-route-ledger.ts`; when a route is added or renamed there, mirror it here.
5455
//
55-
// `/status` and `/effective-model` are deliberately NOT SDK surface (operator
56-
// diagnostics) — which is exactly why they belong on this page: it explores raw
57-
// HTTP, not `client.*`.
56+
// That first fix listed one server-side builder, `buildAIRoutes()`. The family
57+
// is SEVEN builders plus one route mounted by `objectos-runtime`, so this page
58+
// still showed under half of it. All 26 are here now, grouped as the ledger
59+
// groups them.
60+
//
61+
// TWO KINDS OF ENTRY LIVE HERE, and both belong:
62+
// - routes the SDK also expresses (`/chat`, `/conversations/*`) — this page is
63+
// where you check the wire shape behind `client.ai.*`;
64+
// - routes it deliberately does not (`/status`, `/effective-model`, `/tools/*`,
65+
// `/evals/runs`, `/usage`) — operator and console surfaces, which is exactly
66+
// why they belong on a page that explores raw HTTP rather than `client.*`.
67+
//
68+
// MOUNTING IS CONDITIONAL for four of the builders (see AI_ROUTE_MOUNT_GATES in
69+
// the ledger): `/agents/*` and `/assistant/*` need a metadata service,
70+
// `/evals/runs` also needs a data engine, `/conversations/:id/debug` needs one.
71+
// A full Cloud deployment wires all of them; a stripped host may not, and there
72+
// these 404 because they are not mounted — NOT because they do not exist. That
73+
// is a different failure from the one above, and the only honest place to say so
74+
// is here.
5875
ai: {
5976
group: 'AI',
6077
defaultRoute: '/api/v1/ai',
@@ -74,6 +91,39 @@ export const SERVICE_ENDPOINT_CATALOG: Record<string, { group: string; defaultRo
7491
{ method: 'PATCH', path: '/conversations/:id', desc: 'Update conversation (title, metadata)', bodyTemplate: { title: '' } },
7592
{ method: 'DELETE', path: '/conversations/:id', desc: 'Delete conversation' },
7693
{ method: 'POST', path: '/conversations/:id/messages', desc: 'Add message to conversation', bodyTemplate: { role: 'user', content: '' } },
94+
{ method: 'GET', path: '/conversations/:id/debug', desc: 'Reconcile a build conversation: agent-claimed vs live metadata' },
95+
96+
// Named agents. `/agents/:agentName/chat` is dual-mode on the same flag as
97+
// `/chat` above — `stream: false` for the JSON reply this page can render.
98+
{ method: 'GET', path: '/agents', desc: 'List active agents' },
99+
{ method: 'POST', path: '/agents/:agentName/chat', desc: 'Chat with a named agent (JSON)', bodyTemplate: { messages: [{ role: 'user', content: '' }], stream: false } },
100+
101+
// Ambient assistant — resolves the agent and skills from context instead of
102+
// being told. Same `stream: false` treatment on its chat route.
103+
{ method: 'GET', path: '/assistant', desc: 'Resolve the default assistant and its active skills' },
104+
{ method: 'GET', path: '/assistant/skills', desc: 'List active skills for a context' },
105+
{ method: 'POST', path: '/assistant/chat', desc: 'Ambient chat — auto-resolves agent and skills (JSON)', bodyTemplate: { messages: [{ role: 'user', content: '' }], stream: false } },
106+
107+
// Tools. `execute` is the mounted verb — the metadata-admin tool preview
108+
// used to deep-link here with `/invoke`, which has never existed.
109+
{ method: 'GET', path: '/tools', desc: 'List registered AI tools' },
110+
{ method: 'POST', path: '/tools/:toolName/execute', desc: 'Execute one tool directly (playground)', bodyTemplate: { parameters: {} } },
111+
112+
// HITL approval queue. Reads take `ai:read`; approve/reject take the
113+
// separate `ai:approve` — a token that lists the queue may not clear it.
114+
{ method: 'GET', path: '/pending-actions', desc: 'List pending actions awaiting approval' },
115+
{ method: 'GET', path: '/pending-actions/:id', desc: 'Get one pending action' },
116+
{ method: 'POST', path: '/pending-actions/:id/approve', desc: 'Approve a pending action and execute it', bodyTemplate: {} },
117+
{ method: 'POST', path: '/pending-actions/:id/reject', desc: 'Reject a pending action', bodyTemplate: { reason: '' } },
118+
119+
// Operator surfaces. A run here makes real (paid) LLM calls — hence
120+
// `ai:admin`, and hence the warning in the description rather than a
121+
// friendlier one-liner.
122+
{ method: 'POST', path: '/evals/runs', desc: 'Run an eval case — ai:admin, makes paid LLM calls', bodyTemplate: { caseId: '' } },
123+
124+
// Mounted by `objectos-runtime`, not `service-ai`: the console usage ring
125+
// reads it. Reports a fraction of quota, never a token count.
126+
{ method: 'GET', path: '/usage', desc: 'AI quota headroom per meter (console usage indicator)' },
77127
],
78128
},
79129
workflow: {

packages/app-shell/src/views/metadata-admin/previews/ToolPreview.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,11 @@ export function ToolPreview({ name, draft }: MetadataPreviewProps) {
135135
toolbar={
136136
toolName && (
137137
<a
138-
href={`/developer/api-console?path=/api/v1/ai/tools/${encodeURIComponent(toolName)}/invoke`}
138+
// `/execute` is the verb service-ai mounts (buildToolRoutes). This
139+
// link said `/invoke`, which nothing has ever mounted, so "Open in
140+
// API Console" landed on a path that could only 404 — the same
141+
// shape as the three dead AI endpoints framework#3718 removed.
142+
href={`/developer/api-console?path=/api/v1/ai/tools/${encodeURIComponent(toolName)}/execute`}
139143
target="_blank"
140144
rel="noreferrer"
141145
className="text-xs inline-flex items-center gap-1 text-muted-foreground hover:text-foreground"

0 commit comments

Comments
 (0)