Skip to content

Commit 3d101ae

Browse files
authored
Merge pull request #178 from InvolutionHell/main
Merge Rebase
2 parents 4e7c43d + 3c142ea commit 3d101ae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+6192
-187
lines changed

CONTRIBUTING.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pnpm dev
127127

128128
打开浏览器访问 [http://localhost:3000](http://localhost:3000)
129129

130-
修改 `docs/` 下的 `.md` `.mdx` 文件,会自动热更新。
130+
修改 `docs/` 下的 `.md` 文件,会自动热更新。
131131

132132
---
133133

@@ -155,7 +155,7 @@ pnpm postinstall # 同步必要的 Husky/Fumadocs 配置
155155
docxA 引用了 imgA 图片, 那么他们的文档结构应该是 `docxA.assets/imgA`:
156156

157157
```md
158-
docsA.mdx
158+
docsA.md
159159
docsA.assets/
160160
imgA
161161
```
@@ -199,35 +199,35 @@ tags:
199199
```
200200
📂 docs/
201201
├── 📂 computer-science/ # 计算机科学
202-
│ ├── 📄 index.mdx # 概述
202+
│ ├── 📄 index.md # 概述
203203
│ └── 📂 data-structures/ # 数据结构
204-
│ ├── 📄 index.mdx # 概述
204+
│ ├── 📄 index.md # 概述
205205
│ ├── 📂 array/ # 数组
206-
│ │ ├── 📄 index.mdx # 概述
207-
│ │ ├── 📄 01-static-array.mdx # 静态数组
208-
│ │ └── 📄 02-dynamic-array.mdx # 动态数组
206+
│ │ ├── 📄 index.md # 概述
207+
│ │ ├── 📄 01-static-array.md # 静态数组
208+
│ │ └── 📄 02-dynamic-array.md # 动态数组
209209
│ └── 📂 linked-list/ # 链表
210-
│ ├── 📄 index.mdx # 概述
211-
│ └── 📄 01-singly-linked-list.mdx # 单向链表
210+
│ ├── 📄 index.md # 概述
211+
│ └── 📄 01-singly-linked-list.md # 单向链表
212212
```
213213

214214
### URL 生成
215215

216216
文件结构会自动生成简洁的 URL:
217217

218-
- `docs/computer-science/index.mdx``/computer-science`
219-
- `docs/computer-science/data-structures/array/01-static-array.mdx``/computer-science/data-structures/array/static-array`
218+
- `docs/computer-science/index.md``/computer-science`
219+
- `docs/computer-science/data-structures/array/01-static-array.md``/computer-science/data-structures/array/static-array`
220220

221221
### 命名约定
222222

223223
**文件夹:**
224224

225225
- 使用 `kebab-case` 命名: `computer-science`, `data-structures`
226-
- 每个主题文件夹应该有一个 `index.mdx` 文件作为概述
226+
- 每个主题文件夹应该有一个 `index.md` 文件作为概述
227227

228228
**文件:**
229229

230-
- 使用 `kebab-case` 命名: `static-array.mdx`
230+
- 使用 `kebab-case` 命名: `static-array.md`
231231
- 使用数字前缀排序: `01-`, `02-`
232232
- 前缀会自动从最终 URL 中移除
233233

README.en.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,24 @@
22
<a href="./README.md">简体中文</a> | <a href="./README.en.md">English</a>
33
</p>
44

5-
# Involution Hell Knowledge Base
5+
<p align="center">
6+
<a href="https://involutionhell.github.io">
7+
<img src="./public/mascot.svg" width="150">
8+
</a>
9+
</p>
10+
<p align="center"><a href="https://git.io/typing-svg"><img src="https://readme-typing-svg.demolab.com/?font=Fira+Code&weight=700&size=32&pause=1000&color=f6671b&center=true&vCenter=true&width=280&lines=Involution+Hell&duration=3000" alt="Typing SVG" /></a></p>
11+
12+
<p align="center">
13+
<img alt="Next.js" src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white" />
14+
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
15+
<img alt="Tailwind CSS" src="https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwindcss&logoColor=white" />
16+
<img alt="Vercel" src="https://img.shields.io/badge/Vercel-000000?style=for-the-badge&logo=vercel&logoColor=white" />
17+
<a href="https://github.com/InvolutionHell/involutionhell.github.io/blob/main/LICENSE">
18+
<img alt="License" src="https://img.shields.io/github/license/InvolutionHell/involutionhell.github.io?style=for-the-badge&color=blue">
19+
</a>
20+
</p>
21+
22+
623

724
## 📋 About
825

@@ -64,6 +81,8 @@ Community contributions are always welcome:
6481

6582
For the full workflow, PR checklist, and UI collaboration guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
6683

84+
[![Contributors](https://contrib.rocks/image?repo=InvolutionHell/involutionhell.github.io)](https://github.com/InvolutionHell/involutionhell.github.io/graphs/contributors)
85+
6786
## 🖼️ Documentation & Assets
6887

6988
The repo ships with automated image migration and linting. Learn how to place assets, reference images, and structure frontmatter in:

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,23 @@
22
<a href="./README.md">简体中文</a> | <a href="./README.en.md">English</a>
33
</p>
44

5-
# 内卷地狱知识库
5+
<p align="center">
6+
<a href="https://involutionhell.github.io">
7+
<img src="./public/mascot.svg" width="150">
8+
</a>
9+
</p>
10+
11+
<p align="center"><a href="https://git.io/typing-svg"><img src="https://readme-typing-svg.demolab.com/?font=Noto+Sans+SC&weight=700&size=32&pause=1000&color=f6671b&center=true&vCenter=true&width=420&lines=%E5%86%85%E5%8D%B7%E5%9C%B0%E7%8B%B1%E7%9F%A5%E8%AF%86%E5%BA%93&duration=3000" alt="Typing SVG" /></a></p>
12+
13+
<p align="center">
14+
<img alt="Next.js" src="https://img.shields.io/badge/Next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white" />
15+
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
16+
<img alt="Tailwind CSS" src="https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwindcss&logoColor=white" />
17+
<img alt="Vercel" src="https://img.shields.io/badge/Vercel-000000?style=for-the-badge&logo=vercel&logoColor=white" />
18+
<a href="https://github.com/InvolutionHell/involutionhell.github.io/blob/main/LICENSE">
19+
<img alt="License" src="https://img.shields.io/github/license/InvolutionHell/involutionhell.github.io?style=for-the-badge&color=blue">
20+
</a>
21+
</p>
622

723
## 📋 关于
824

@@ -74,6 +90,8 @@ pnpm dev
7490

7591
完整流程、PR 检查与 UI 协作约定请参考 [CONTRIBUTING.md](CONTRIBUTING.md)
7692

93+
[![Contributors](https://contrib.rocks/image?repo=InvolutionHell/involutionhell.github.io)](https://github.com/InvolutionHell/involutionhell.github.io/graphs/contributors)
94+
7795
## 🖼️ 文档与资产
7896

7997
仓库提供自动化图片迁移与 Lint 规则。如何放置图片、引用资产、撰写 Frontmatter 等细节已在贡献指南中整理:

app/api/chat/route.ts

Lines changed: 45 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,52 @@
1-
import { createOpenAI } from "@ai-sdk/openai";
2-
import { createGoogleGenerativeAI } from "@ai-sdk/google";
31
import { streamText, UIMessage, convertToModelMessages } from "ai";
2+
import { getModel, requiresApiKey, type AIProvider } from "@/lib/ai/models";
3+
import { buildSystemMessage } from "@/lib/ai/prompt";
44

5-
// Allow streaming responses up to 30 seconds
5+
// 流式响应最长30秒
66
export const maxDuration = 30;
77

8-
export async function POST(req: Request) {
9-
const {
10-
messages,
11-
system,
12-
pageContext,
13-
provider,
14-
apiKey,
15-
}: {
16-
messages: UIMessage[];
17-
system?: string; // System message forwarded from AssistantChatTransport
18-
tools?: unknown; // Frontend tools forwarded from AssistantChatTransport
19-
pageContext?: {
20-
title?: string;
21-
description?: string;
22-
content?: string;
23-
slug?: string;
24-
};
25-
provider?: "openai" | "gemini";
26-
apiKey?: string;
27-
} = await req.json();
28-
29-
// Check if API key is provided
30-
if (!apiKey || apiKey.trim() === "") {
31-
return Response.json(
32-
{
33-
error:
34-
"API key is required. Please configure your API key in the settings.",
35-
},
36-
{ status: 400 },
37-
);
38-
}
8+
interface ChatRequest {
9+
messages: UIMessage[];
10+
system?: string;
11+
tools?: unknown;
12+
pageContext?: {
13+
title?: string;
14+
description?: string;
15+
content?: string;
16+
slug?: string;
17+
};
18+
provider?: AIProvider;
19+
apiKey?: string;
20+
}
3921

22+
export async function POST(req: Request) {
4023
try {
41-
// Build system message with page context
42-
let systemMessage =
43-
system ||
44-
`You are a helpful AI assistant for a documentation website.
45-
You can help users understand the documentation, answer questions about the content,
46-
and provide guidance on the topics covered in the docs. Be concise and helpful.`;
24+
const {
25+
messages,
26+
system,
27+
pageContext,
28+
provider = "intern", // 默认使用书生模型
29+
apiKey,
30+
}: ChatRequest = await req.json();
4731

48-
// Add current page context if available
49-
if (pageContext?.content) {
50-
systemMessage += `\n\n--- CURRENT PAGE CONTEXT ---\n`;
51-
if (pageContext.title) {
52-
systemMessage += `Page Title: ${pageContext.title}\n`;
53-
}
54-
if (pageContext.description) {
55-
systemMessage += `Page Description: ${pageContext.description}\n`;
56-
}
57-
if (pageContext.slug) {
58-
systemMessage += `Page URL: /docs/${pageContext.slug}\n`;
59-
}
60-
systemMessage += `Page Content:\n${pageContext.content}`;
61-
systemMessage += `\n--- END OF CONTEXT ---\n\nWhen users ask about "this page", "current page", or refer to the content they're reading, use the above context to provide accurate answers. You can summarize, explain, or answer specific questions about the current page content.`;
32+
// 对指定Provider验证key是否存在
33+
if (requiresApiKey(provider) && (!apiKey || apiKey.trim() === "")) {
34+
return Response.json(
35+
{
36+
error:
37+
"API key is required. Please configure your API key in the settings.",
38+
},
39+
{ status: 400 },
40+
);
6241
}
6342

64-
// Select model based on provider
65-
let model;
66-
if (provider === "gemini") {
67-
const customGoogle = createGoogleGenerativeAI({
68-
apiKey: apiKey,
69-
});
70-
model = customGoogle("models/gemini-2.0-flash");
71-
} else {
72-
// Default to OpenAI
73-
const customOpenAI = createOpenAI({
74-
apiKey: apiKey,
75-
});
76-
model = customOpenAI("gpt-4.1-nano");
77-
}
43+
// 构建系统消息,包含页面上下文
44+
const systemMessage = buildSystemMessage(system, pageContext);
45+
46+
// 根据Provider获取 AI 模型实例
47+
const model = getModel(provider, apiKey);
7848

49+
// 生成流式响应
7950
const result = streamText({
8051
model: model,
8152
system: systemMessage,
@@ -85,6 +56,12 @@ export async function POST(req: Request) {
8556
return result.toUIMessageStreamResponse();
8657
} catch (error) {
8758
console.error("Chat API error:", error);
59+
60+
// 处理特定模型创建错误
61+
if (error instanceof Error && error.message.includes("API key")) {
62+
return Response.json({ error: error.message }, { status: 400 });
63+
}
64+
8865
return Response.json(
8966
{ error: "Failed to process chat request" },
9067
{ status: 500 },

app/components/Contribute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const FILENAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]+$/;
2727

2828
// 统一调用工具函数生成 GitHub 新建链接,路径规则与 Edit 按钮一致
2929
function buildGithubNewUrl(dirPath: string, filename: string, title: string) {
30-
const file = filename.endsWith(".mdx") ? filename : `${filename}.mdx`;
30+
const file = filename.endsWith(".md") ? filename : `${filename}.md`;
3131
const frontMatter = `---
3232
title: '${title || "New Article"}'
3333
description: ""

app/components/DocsAssistant.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ function DocsAssistantInner({ pageContext }: DocsAssistantProps) {
5353
const currentApiKey =
5454
currentProvider === "openai"
5555
? openaiApiKeyRef.current
56-
: geminiApiKeyRef.current;
56+
: currentProvider === "gemini"
57+
? geminiApiKeyRef.current
58+
: ""; // intern provider doesn't need API key
5759

5860
console.log("[DocsAssistant] useChat body function called with:", {
5961
provider: currentProvider,
@@ -118,9 +120,14 @@ interface AssistantErrorState {
118120

119121
function deriveAssistantError(
120122
err: unknown,
121-
provider: "openai" | "gemini",
123+
provider: "openai" | "gemini" | "intern",
122124
): AssistantErrorState {
123-
const providerLabel = provider === "gemini" ? "Google Gemini" : "OpenAI";
125+
const providerLabel =
126+
provider === "gemini"
127+
? "Google Gemini"
128+
: provider === "intern"
129+
? "Intern-AI"
130+
: "OpenAI";
124131
const fallback: AssistantErrorState = {
125132
message:
126133
"The assistant couldn't complete that request. Please try again later.",
@@ -176,14 +183,16 @@ function deriveAssistantError(
176183

177184
let showSettingsCTA = false;
178185

186+
// For intern provider, don't show settings CTA for API key related errors
179187
if (
180-
statusCode === 400 ||
181-
statusCode === 401 ||
182-
statusCode === 403 ||
183-
normalized.includes("api key") ||
184-
normalized.includes("apikey") ||
185-
normalized.includes("missing key") ||
186-
normalized.includes("unauthorized")
188+
provider !== "intern" &&
189+
(statusCode === 400 ||
190+
statusCode === 401 ||
191+
statusCode === 403 ||
192+
normalized.includes("api key") ||
193+
normalized.includes("apikey") ||
194+
normalized.includes("missing key") ||
195+
normalized.includes("unauthorized"))
187196
) {
188197
showSettingsCTA = true;
189198
}

app/components/assistant-ui/SettingsDialog.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,13 @@ export const SettingsDialog = ({
5151
<RadioGroup
5252
value={provider}
5353
onValueChange={(value) =>
54-
setProvider(value as "openai" | "gemini")
54+
setProvider(value as "openai" | "gemini" | "intern")
5555
}
5656
>
57+
<div className="flex items-center space-x-2">
58+
<RadioGroupItem value="intern" id="intern" />
59+
<Label htmlFor="intern">InternS1 (Free)</Label>
60+
</div>
5761
<div className="flex items-center space-x-2">
5862
<RadioGroupItem value="openai" id="openai" />
5963
<Label htmlFor="openai">OpenAI</Label>
@@ -90,6 +94,15 @@ export const SettingsDialog = ({
9094
/>
9195
</div>
9296
)}
97+
98+
{provider === "intern" && (
99+
<div className="space-y-2">
100+
<div className="text-sm text-muted-foreground">
101+
感谢上海AILab的书生大模型对本项目的算力支持,Intern-AI
102+
模型已预配置,无需提供 API Key。
103+
</div>
104+
</div>
105+
)}
93106
</div>
94107

95108
<DialogFooter>

0 commit comments

Comments
 (0)