Skip to content

Commit faac42c

Browse files
committed
fix(showcase): wire byoc-hashbrown backend agents correctly
- agno: add default agent alias + per-request runtime - langgraph-fastapi: add default agent alias - llamaindex: fix agent name mismatch (byoc_hashbrown → byoc-hashbrown-demo) - mastra: create dedicated byocHashbrownAgent with hashbrown system prompt (was using weatherAgent which produced plain text instead of JSON) - ms-agent-dotnet: upgrade byoc page to V2 CopilotKit import
1 parent d36660b commit faac42c

7 files changed

Lines changed: 137 additions & 19 deletions

File tree

  • showcase/integrations
    • agno/src/app/api/copilotkit-byoc-hashbrown
    • langgraph-fastapi/src/app/api/copilotkit-byoc-hashbrown
    • llamaindex/src/app/api/copilotkit-byoc-hashbrown
    • mastra/src
    • ms-agent-dotnet/src/app/demos/byoc-hashbrown

showcase/integrations/agno/src/app/api/copilotkit-byoc-hashbrown/route.ts

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,53 @@ import {
66
ExperimentalEmptyAdapter,
77
copilotRuntimeNextJSAppRouterEndpoint,
88
} from "@copilotkit/runtime";
9-
import { HttpAgent } from "@ag-ui/client";
9+
import { AbstractAgent, HttpAgent } from "@ag-ui/client";
1010

1111
const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000";
1212

13-
const byocHashbrownAgent = new HttpAgent({
14-
url: `${AGENT_URL}/byoc-hashbrown/agui`,
15-
});
13+
console.log(
14+
`[copilotkit-byoc-hashbrown/route] AGENT_URL: ${AGENT_URL}`,
15+
);
1616

17-
const runtime = new CopilotRuntime({
18-
// @ts-ignore -- see main route.ts
19-
agents: { "byoc-hashbrown-demo": byocHashbrownAgent },
20-
});
17+
function createByocHashbrownAgent() {
18+
return new HttpAgent({
19+
url: `${AGENT_URL}/byoc-hashbrown/agui`,
20+
});
21+
}
22+
23+
// Register both the named agent and a default fallback so the runtime
24+
// can always resolve regardless of which agent name the frontend sends.
25+
const agents: Record<string, AbstractAgent> = {
26+
"byoc-hashbrown-demo": createByocHashbrownAgent(),
27+
default: createByocHashbrownAgent(),
28+
};
2129

2230
export const POST = async (req: NextRequest) => {
31+
const url = req.url;
32+
console.log(`[copilotkit-byoc-hashbrown/route] POST ${url}`);
33+
2334
try {
2435
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
2536
endpoint: "/api/copilotkit-byoc-hashbrown",
2637
serviceAdapter: new ExperimentalEmptyAdapter(),
27-
runtime,
38+
runtime: new CopilotRuntime({
39+
// @ts-ignore -- see main route.ts
40+
agents,
41+
}),
2842
});
29-
return await handleRequest(req);
43+
const response = await handleRequest(req);
44+
console.log(
45+
`[copilotkit-byoc-hashbrown/route] Response status: ${response.status}`,
46+
);
47+
return response;
3048
} catch (error: unknown) {
3149
const e = error as { message?: string; stack?: string };
50+
console.error(
51+
`[copilotkit-byoc-hashbrown/route] ERROR: ${e.message}`,
52+
);
53+
console.error(
54+
`[copilotkit-byoc-hashbrown/route] Stack: ${e.stack}`,
55+
);
3256
return NextResponse.json(
3357
{ error: e.message, stack: e.stack },
3458
{ status: 500 },

showcase/integrations/langgraph-fastapi/src/app/api/copilotkit-byoc-hashbrown/route.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,18 @@ const byocHashbrownAgent = new LangGraphAgent({
3030
langsmithApiKey: process.env.LANGSMITH_API_KEY || "",
3131
});
3232

33+
const agents: Record<string, LangGraphAgent> = {
34+
"byoc-hashbrown-demo": byocHashbrownAgent,
35+
// Internal components (headless-chat, example-canvas) call `useAgent()` with
36+
// no args, which defaults to agentId "default". Alias to the same graph so
37+
// those component hooks resolve instead of throwing "Agent 'default' not
38+
// found".
39+
default: byocHashbrownAgent,
40+
};
41+
3342
const runtime = new CopilotRuntime({
3443
// @ts-ignore -- see main route.ts
35-
agents: { "byoc-hashbrown-demo": byocHashbrownAgent },
44+
agents,
3645
});
3746

3847
export const POST = async (req: NextRequest) => {

showcase/integrations/llamaindex/src/app/api/copilotkit-byoc-hashbrown/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const byocHashbrownAgent = new HttpAgent({
2020

2121
const runtime = new CopilotRuntime({
2222
// @ts-ignore -- see main route.ts
23-
agents: { byoc_hashbrown: byocHashbrownAgent as AbstractAgent },
23+
agents: { "byoc-hashbrown-demo": byocHashbrownAgent as AbstractAgent },
2424
});
2525

2626
export const POST = async (req: NextRequest) => {

showcase/integrations/mastra/src/app/api/copilotkit-byoc-hashbrown/route.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
// hashbrown-shaped structured output via `@hashbrownai/react`'s `useUiKit`
66
// + `useJsonParser`.
77
//
8-
// In Mastra, the same shared weatherAgent backs this demo — the dashboard
9-
// shape is enforced entirely on the frontend by the catalog the renderer
10-
// consumes. For full parity (system prompt tuned to emit the dashboard
11-
// envelope), a dedicated Mastra agent could be wired here later.
8+
// Uses the dedicated `byocHashbrownAgent` whose system prompt forces the
9+
// model to emit the hashbrown JSON envelope `{ "ui": [...] }`. The default
10+
// weatherAgent produces plain text that `useJsonParser` parses as `null`,
11+
// leaving the dashboard empty — which is why D5 probes time out.
1212

1313
import { NextRequest, NextResponse } from "next/server";
1414
import {
@@ -21,13 +21,13 @@ import { mastra } from "@/mastra";
2121

2222
const byocHashbrownAgent = getLocalAgent({
2323
mastra,
24-
agentId: "weatherAgent",
24+
agentId: "byocHashbrownAgent",
2525
resourceId: "mastra-byoc-hashbrown",
2626
});
2727

2828
if (!byocHashbrownAgent) {
2929
throw new Error(
30-
"getLocalAgent returned null for weatherAgent — required for /demos/byoc-hashbrown",
30+
"getLocalAgent returned null for byocHashbrownAgent — required for /demos/byoc-hashbrown",
3131
);
3232
}
3333

showcase/integrations/mastra/src/mastra/agents/index.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,89 @@ Do NOT call \`read_me\`, do NOT iterate, do NOT make multiple calls. Ship on the
312312
}),
313313
});
314314

315+
// @region[byoc-hashbrown-agent]
316+
/**
317+
* Mastra agent backing the byoc-hashbrown demo.
318+
*
319+
* The demo page wraps CopilotChat in the HashBrownDashboard provider and
320+
* overrides the assistant message slot with a renderer that consumes
321+
* hashbrown-shaped structured output via `@hashbrownai/react`'s `useUiKit`
322+
* + `useJsonParser`.
323+
*
324+
* The system prompt forces the model to emit a single JSON envelope
325+
* `{ "ui": [ { <componentName>: { "props": { ... } } }, ... ] }` matching
326+
* the schema consumed by `useSalesDashboardKit()` in the frontend renderer.
327+
* Without this prompt the default weatherAgent produces plain text, which
328+
* `useJsonParser` parses as `null` and the dashboard renders nothing.
329+
*/
330+
export const byocHashbrownAgent = new Agent({
331+
id: "byoc-hashbrown-agent",
332+
name: "BYOC Hashbrown Agent",
333+
model: openai("gpt-4o-mini"),
334+
instructions: `You are a sales analytics assistant that replies by emitting a single JSON
335+
object consumed by a streaming JSON parser on the frontend.
336+
337+
ALWAYS respond with a single JSON object of the form:
338+
339+
{
340+
"ui": [
341+
{ <componentName>: { "props": { ... } } },
342+
...
343+
]
344+
}
345+
346+
Do NOT wrap the response in code fences. Do NOT include any preface or
347+
explanation outside the JSON object. The response MUST be valid JSON.
348+
349+
Available components and their prop schemas:
350+
351+
- "metric": { "props": { "label": string, "value": string } }
352+
A KPI card. \`value\` is a pre-formatted string like "$1.2M" or "248".
353+
354+
- "pieChart": { "props": { "title": string, "data": string } }
355+
A donut chart. \`data\` is a JSON-encoded STRING (embedded JSON) of an
356+
array of {label, value} objects with at least 3 segments, e.g.
357+
"data": "[{\\"label\\":\\"Enterprise\\",\\"value\\":600000}]".
358+
359+
- "barChart": { "props": { "title": string, "data": string } }
360+
A vertical bar chart. \`data\` is a JSON-encoded STRING of an array of
361+
{label, value} objects with at least 3 bars, typically time-ordered.
362+
363+
- "dealCard": { "props": { "title": string, "stage": string, "value": number } }
364+
A single sales deal. \`stage\` MUST be one of: "prospect", "qualified",
365+
"proposal", "negotiation", "closed-won", "closed-lost". \`value\` is a
366+
raw number (no currency symbol or comma).
367+
368+
- "Markdown": { "props": { "children": string } }
369+
Short explanatory text. Use for section headings and brief summaries.
370+
Standard markdown is supported in \`children\`.
371+
372+
Rules:
373+
- Always produce plausible sample data when the user asks for a dashboard or
374+
chart — do not refuse for lack of data.
375+
- Prefer 3-6 rows of data in charts; keep labels short.
376+
- Use "Markdown" for short headings or linking sentences between visual
377+
components. Do not emit long prose.
378+
- Do not emit components that are not listed above.
379+
- \`data\` props on charts MUST be a JSON STRING — escape inner quotes.
380+
381+
Example response (sales dashboard):
382+
{"ui":[{"Markdown":{"props":{"children":"## Q4 Sales Summary"}}},{"metric":{"props":{"label":"Total Revenue","value":"$1.2M"}}},{"metric":{"props":{"label":"New Customers","value":"248"}}},{"pieChart":{"props":{"title":"Revenue by Segment","data":"[{\\"label\\":\\"Enterprise\\",\\"value\\":600000},{\\"label\\":\\"SMB\\",\\"value\\":400000},{\\"label\\":\\"Startup\\",\\"value\\":200000}]"}}},{"barChart":{"props":{"title":"Monthly Revenue","data":"[{\\"label\\":\\"Oct\\",\\"value\\":350000},{\\"label\\":\\"Nov\\",\\"value\\":400000},{\\"label\\":\\"Dec\\",\\"value\\":450000}]"}}}]}`,
383+
memory: new Memory({
384+
storage: new LibSQLStore({
385+
id: "byoc-hashbrown-agent-memory",
386+
url: WORKING_MEMORY_DB_URL,
387+
}),
388+
options: {
389+
workingMemory: {
390+
enabled: true,
391+
schema: AgentState,
392+
},
393+
},
394+
}),
395+
});
396+
// @endregion[byoc-hashbrown-agent]
397+
315398
/**
316399
* Vision-capable Mastra agent backing the Multimodal Attachments demo.
317400
*

showcase/integrations/mastra/src/mastra/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
interruptAgent,
99
multimodalAgent,
1010
mcpAppsAgent,
11+
byocHashbrownAgent,
1112
} from "./agents";
1213
import { ConsoleLogger, LogLevel } from "@mastra/core/logger";
1314

@@ -22,6 +23,7 @@ export const mastra = new Mastra({
2223
interruptAgent,
2324
multimodalAgent,
2425
mcpAppsAgent,
26+
byocHashbrownAgent,
2527
},
2628
storage: new LibSQLStore({
2729
id: "mastra-storage",

showcase/integrations/ms-agent-dotnet/src/app/demos/byoc-hashbrown/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
import React from "react";
1515
import {
16+
CopilotKit,
1617
CopilotChat,
1718
CopilotChatAssistantMessage,
1819
useConfigureSuggestions,
1920
} from "@copilotkit/react-core/v2";
20-
import { CopilotKit } from "@copilotkit/react-core";
2121
import {
2222
HashBrownDashboard,
2323
useHashBrownMessageRenderer,

0 commit comments

Comments
 (0)