Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5b44757
feat(docs): add resizable AI assistant panel and chat integration
junghyeonsu Feb 27, 2026
323da7f
refactor(docs): remove chat api env override
junghyeonsu Feb 28, 2026
fe67b01
fix(docs): harden ai panel request and tool validation
junghyeonsu Feb 28, 2026
eee2398
fix(docs): stabilize ai chat tool handling
junghyeonsu Feb 28, 2026
67964da
feat(docs-ai): persist chat session and improve message rendering
junghyeonsu Feb 28, 2026
ecfd65d
feat(docs-ai): add richer tool rendering and related links tool
junghyeonsu Feb 28, 2026
c67d508
feat(docs-ai): add fade-in motion to tool renderer outputs
junghyeonsu Feb 28, 2026
bd4a98b
feat(docs-ai): animate copy button feedback icon transitions
junghyeonsu Feb 28, 2026
411cdd8
feat(docs-ai): add react props type table tool rendering
junghyeonsu Feb 28, 2026
3cc2e71
fix(docs-ai): stabilize tool-first chat flow and message interactions
junghyeonsu Feb 28, 2026
b7403a9
fix(docs-ai): derive structured ui from markdown fallback responses
junghyeonsu Feb 28, 2026
3e12f68
feat(docs-ai): render assistant markdown in chat panel
junghyeonsu Feb 28, 2026
d7ad47b
refactor(docs-ai): share mdx component map for chat rendering
junghyeonsu Feb 28, 2026
602bb3f
feat(docs-ai): switch ai panel props and guide intent to llms fetch
junghyeonsu Feb 28, 2026
3282327
docs(agents): add scoped guides for docs AI paths
junghyeonsu Feb 28, 2026
cadb183
feat(docs-ai): enforce verified links in assistant responses
junghyeonsu Feb 28, 2026
07fa7f6
fix(docs-ai): ensure verified links are reliably shown
junghyeonsu Feb 28, 2026
063aafb
feat(docs-ai): migrate chat runtime to ToolLoopAgent orchestration
junghyeonsu Feb 28, 2026
3d821ae
feat(docs-ai-panel): refine tool rendering with MCP collapse and link…
junghyeonsu Feb 28, 2026
44b1a63
fix(docs-ai): prevent icon-tool bias for non-icon queries
junghyeonsu Feb 28, 2026
846d5dc
fix(docs): harden ai panel chat route and tool handling
junghyeonsu Feb 28, 2026
193ea92
fix(docs-ai): remove request-url base derivation and harden tool maps
junghyeonsu Feb 28, 2026
ff51bbc
fix(docs-ai): avoid regex backtracking in intent parser
junghyeonsu Feb 28, 2026
bbd9c15
refactor(docs-ai): simplify assistant panel runtime and remove dead p…
junghyeonsu Feb 28, 2026
7da6d3c
fix(docs-ai): align base-url env typing with process.env
junghyeonsu Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions docs/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,15 @@
NEXT_PUBLIC_SANITY_PROJECT_ID=
NEXT_PUBLIC_SANITY_DATASET=
NEXT_PUBLIC_SANITY_API_VERSION=

# AI Assistant - LLM Router
LLM_ROUTER_URL=
LLM_ROUTER_KATALOG_ID=
LLM_ROUTER_KATALOG_NAME=
LLM_ROUTER_MODEL=

# AI Assistant - MCP (Streamable HTTP)
SEED_DOCS_MCP_SERVER_URL=

# AI Assistant - Client (별도 API 서버 사용 시)
NEXT_PUBLIC_CHAT_API_URL=
65 changes: 65 additions & 0 deletions docs/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { createOpenAI } from "@ai-sdk/openai";
import { convertToModelMessages, safeValidateUIMessages, stepCountIs, streamText } from "ai";
import { systemPrompt } from "@/lib/ai/system-prompt";
import { clientTools } from "@/lib/ai/tools";
import { getMCPTools } from "@/lib/ai/mcp-client";
import { z } from "zod";

const llmRouter = createOpenAI({
baseURL: process.env.LLM_ROUTER_URL,
apiKey: "-",
headers: {
"x-request-katalog-id": process.env.LLM_ROUTER_KATALOG_ID ?? "",
"x-request-katalog-name": process.env.LLM_ROUTER_KATALOG_NAME ?? "",
},
name: "llm-router",
});

const llmRouterModel = process.env.LLM_ROUTER_MODEL?.trim() || "openai/gpt-4o";

const chatRequestSchema = z.object({
messages: z.array(z.unknown()).min(1),
});

export async function POST(req: Request) {
let body: unknown;
try {
body = await req.json();
} catch {
return Response.json({ error: "Invalid JSON" }, { status: 400 });
}

const parsed = chatRequestSchema.safeParse(body);
if (!parsed.success) {
return Response.json({ error: "Invalid request body" }, { status: 400 });
}

const mcpTools = await getMCPTools();
const tools = {
...clientTools,
...mcpTools,
};

const validatedMessages = await safeValidateUIMessages({
messages: parsed.data.messages,
});

if (!validatedMessages.success) {
return Response.json({ error: "Invalid messages format" }, { status: 400 });
}

const messages = await convertToModelMessages(validatedMessages.data, {
tools,
ignoreIncompleteToolCalls: true,
});

const result = streamText({
model: llmRouter(llmRouterModel),
system: systemPrompt,
messages,
tools,
stopWhen: stepCountIs(5),
});

return result.toUIMessageStreamResponse();
}
64 changes: 64 additions & 0 deletions docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,67 @@ pre, code, pre code {
user-select: text !important;
-webkit-user-select: text !important;
}

/* AI Panel resize handle */
#seed-ai-panel {
height: 100dvh !important;
}

#seed-ai-panel > [data-separator] {
cursor: col-resize;
width: 1px;
background-color: var(--color-fd-border);
opacity: 0.6;
transition:
width 220ms cubic-bezier(0.2, 0, 0, 1),
opacity 220ms cubic-bezier(0.2, 0, 0, 1),
background-color 150ms;
flex-shrink: 0;
}

#seed-ai-panel > [data-separator]:hover,
#seed-ai-panel > [data-separator][data-active] {
background-color: var(--color-fd-primary);
opacity: 0.35;
}

#seed-ai-panel[data-ai-open="false"] > [data-separator] {
width: 0;
opacity: 0;
pointer-events: none;
}

#seed-ai-panel[data-ai-transitioning="true"] #ai-panel[data-panel] {
transition:
flex-grow 220ms cubic-bezier(0.2, 0, 0, 1),
width 220ms cubic-bezier(0.2, 0, 0, 1);
}

.seed-docs-pane {
container-type: inline-size;
container-name: seed-docs-pane;
}

@container seed-docs-pane (max-width: 1220px) {
.seed-docs-pane {
--fd-toc-width: 0px;
}

.seed-docs-pane #nd-toc {
display: none !important;
}
}

#seed-ai-panel[data-ai-open="true"] #nd-docs-layout {
--fd-toc-width: 0px !important;
}

#seed-ai-panel[data-ai-open="true"] #nd-toc {
display: none !important;
}

/* Keep AI toggle only in top header, not in section sidebars */
#nd-sidebar .seed-ai-panel-toggle,
#nd-sidebar-mobile .seed-ai-panel-toggle {
display: none;
}
2 changes: 2 additions & 0 deletions docs/app/layout.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
docsSource,
} from "@/app/source";
import { ReactVersionSwitcher } from "@/components/react-version-switcher";
import { AIPanelToggle } from "@/components/ai-panel/ai-panel-toggle";
import { IconSparkle2, IconTree } from "@karrotmarket/react-multicolor-icon";
import clsx from "clsx";
import type { DocsLayoutProps } from "fumadocs-ui/layouts/notebook";
Expand Down Expand Up @@ -94,6 +95,7 @@ export const baseOptions: Omit<DocsLayoutProps, "tree"> = {
tabMode: "navbar",
nav: {
mode: "top",
children: <AIPanelToggle />,
title: (
<div className="flex gap-2 justify-center items-center">
<svg
Expand Down
15 changes: 12 additions & 3 deletions docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import "./global.css";

import GoogleAnalytics from "@/components/google-analytics";
import { LatestVersionBanner } from "@/components/latest-version-banner";
import { AIPanelProvider } from "@/components/ai-panel/ai-panel-provider";
import { AIPanelLayout } from "@/components/ai-panel/ai-panel-layout";
import { MotionProvider } from "@/components/MotionProvider";
import { Inter } from "next/font/google";
import type { ReactNode } from "react";
import ThemeSync from "@/components/theme-sync";
Expand All @@ -28,9 +31,15 @@ export default function Layout({ children }: { children: ReactNode }) {
<GoogleAnalytics GA_MEASUREMENT_ID="G-02SS22W02G" />
</head>
<body>
<LatestVersionBanner />
<ThemeSync />
{children}
<MotionProvider>
<AIPanelProvider>
<AIPanelLayout>
<LatestVersionBanner />
<ThemeSync />
{children}
</AIPanelLayout>
</AIPanelProvider>
</MotionProvider>
</body>
</html>
);
Expand Down
Loading
Loading