@open330/kiwimu 0.8.0 โ 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -27
- package/package.json +1 -1
- package/src/build/renderer.ts +272 -32
- package/src/build/static/dynamic-qa.js +423 -0
- package/src/build/static/edit-page.js +58 -0
- package/src/build/static/peek-panel.css +201 -0
- package/src/build/static/peek-panel.js +470 -0
- package/src/build/static/search.js +30 -15
- package/src/build/static/style.css +821 -6
- package/src/build/templates.ts +700 -48
- package/src/config.ts +41 -3
- package/src/demo/sample-data.ts +69 -2
- package/src/demo/setup.ts +25 -6
- package/src/expand/llm.ts +2 -2
- package/src/index.ts +467 -60
- package/src/ingest/docx.ts +1 -1
- package/src/ingest/markdown.ts +21 -0
- package/src/ingest/pdf.ts +4 -2
- package/src/llm-client.ts +63 -69
- package/src/pipeline/citations.ts +107 -0
- package/src/pipeline/llm-chunker.ts +277 -131
- package/src/pipeline/standardizer.ts +41 -0
- package/src/server.ts +465 -32
- package/src/services/dynamic-qa.ts +190 -0
- package/src/services/embedding.ts +122 -0
- package/src/services/index-generator.ts +185 -0
- package/src/services/ingest.ts +83 -25
- package/src/services/lint.ts +249 -0
- package/src/services/promote.ts +150 -0
- package/src/store.test.ts +11 -0
- package/src/store.ts +561 -28
- package/src/utils.ts +30 -0
package/src/config.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { join, dirname } from "path";
|
|
|
5
5
|
export const CONFIG_FILE = "kiwi.toml";
|
|
6
6
|
export const DB_FILE = "kiwi.db";
|
|
7
7
|
export const SITE_DIR = "_site";
|
|
8
|
+
export const SUPPORTED_EXTENSIONS = ['pdf', 'docx', 'pptx', 'doc', 'ppt', 'key', 'rtf', 'md'];
|
|
8
9
|
|
|
9
10
|
export interface LLMConfig {
|
|
10
11
|
provider: string; // "gemini" | "azure-openai" | "openai" | "anthropic"
|
|
@@ -20,13 +21,50 @@ export interface Persona {
|
|
|
20
21
|
content_style: string; // injected into content generation prompts
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
export interface EmbeddingConfig {
|
|
25
|
+
provider: string; // "gemini" | "openai" | "azure-openai"
|
|
26
|
+
api_key: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface QAConfig {
|
|
30
|
+
auto_promote: boolean; // If true, automatically save all Q&A answers as wiki pages
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface WikiSchema {
|
|
34
|
+
categories?: string[];
|
|
35
|
+
naming_convention?: 'noun_phrase' | 'question' | 'topic';
|
|
36
|
+
min_page_length?: number;
|
|
37
|
+
max_page_length?: number;
|
|
38
|
+
terms?: Record<string, string>;
|
|
39
|
+
page_template?: { sections?: string[] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A user-defined source category for sidebar/index grouping. */
|
|
43
|
+
export interface SourceCategory {
|
|
44
|
+
/** Display label, e.g. "๐ PRD". Korean/emoji OK. */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Display order; lower numbers appear first. */
|
|
47
|
+
order: number;
|
|
48
|
+
/**
|
|
49
|
+
* Glob-like patterns matched against the source URI (case-insensitive).
|
|
50
|
+
* Supports `*` as a wildcard. Patterns are tested against the basename
|
|
51
|
+
* (filename) and the full URI; matching either counts.
|
|
52
|
+
* Examples: "F[0-9][0-9]_*", "weekly/*", "STUDY_PLAN*"
|
|
53
|
+
*/
|
|
54
|
+
patterns: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
23
57
|
export interface KiwiConfig {
|
|
24
58
|
project: { name: string; created: string };
|
|
25
59
|
build: { output_dir: string };
|
|
26
60
|
llm: LLMConfig;
|
|
61
|
+
embedding?: EmbeddingConfig; // separate config for embeddings (optional, falls back to llm)
|
|
62
|
+
qa?: QAConfig; // Q&A feedback loop settings
|
|
27
63
|
deploy: { target: string };
|
|
28
64
|
personas?: Persona[];
|
|
29
65
|
active_persona?: string; // name of the active persona
|
|
66
|
+
schema?: WikiSchema;
|
|
67
|
+
categories?: SourceCategory[]; // optional source categorization for grouped rendering
|
|
30
68
|
}
|
|
31
69
|
|
|
32
70
|
/** Directory containing built-in persona JSON files (shipped with the package) */
|
|
@@ -62,7 +100,7 @@ export function defaultConfig(name: string): KiwiConfig {
|
|
|
62
100
|
return {
|
|
63
101
|
project: { name, created: new Date().toISOString().slice(0, 10) },
|
|
64
102
|
build: { output_dir: SITE_DIR },
|
|
65
|
-
llm: { provider: "gemini", model: "gemini-
|
|
103
|
+
llm: { provider: "gemini", model: "gemini-3.1-flash-lite-preview", api_key: "", endpoint: "" },
|
|
66
104
|
deploy: { target: "gh-pages" },
|
|
67
105
|
personas: builtins.length > 0 ? builtins : [getDefaultPersona()],
|
|
68
106
|
active_persona: builtins[0]?.name || "default",
|
|
@@ -79,11 +117,11 @@ export function saveConfig(root: string, config: KiwiConfig): void {
|
|
|
79
117
|
}
|
|
80
118
|
|
|
81
119
|
export function loadConfig(root: string): KiwiConfig {
|
|
82
|
-
const content =
|
|
120
|
+
const content = readFileSync(join(root, CONFIG_FILE), "utf-8");
|
|
83
121
|
const raw = parse(content) as Partial<KiwiConfig> & Record<string, unknown>;
|
|
84
122
|
// Migrate old config format
|
|
85
123
|
if (!raw.llm) {
|
|
86
|
-
raw.llm = { provider: "gemini", model: "gemini-
|
|
124
|
+
raw.llm = { provider: "gemini", model: "gemini-3.1-flash-lite-preview", api_key: "", endpoint: "" };
|
|
87
125
|
}
|
|
88
126
|
// Migrate: add default persona if missing
|
|
89
127
|
if (!raw.personas || !raw.personas.length) {
|
package/src/demo/sample-data.ts
CHANGED
|
@@ -47,13 +47,13 @@ export const DEMO_PAGES = [
|
|
|
47
47
|
|
|
48
48
|
export const DEMO_QUIZZES = [
|
|
49
49
|
{ page_slug: "ํ์ด์ ๋ฒ ๋ฅดํฌ", question: "___์ ์์น์ ์ด๋๋์ ๋์์ ์ ํํ ์ธก์ ํ ์ ์๋ค๋ ์์์ญํ์ ์๋ฆฌ์ด๋ค.", answer: "๋ถํ์ ์ฑ ์๋ฆฌ", explanation: "ํ์ด์ ๋ฒ ๋ฅดํฌ๊ฐ 1927๋
์ ์ ์ํ ์ด ์๋ฆฌ๋ ์์์ญํ์ ๊ทผ๋ณธ์ ํ๊ณ๋ฅผ ๋ณด์ฌ์ค๋๋ค. ์์น๋ฅผ ์ ํํ ์ธก์ ํ๋ฉด ์ด๋๋์ ๋ถํ์ ์ฑ์ด ์ปค์ง๊ณ , ๊ทธ ๋ฐ๋๋ ๋ง์ฐฌ๊ฐ์ง์
๋๋ค.", quiz_type: "fill_blank" },
|
|
50
|
-
{ page_slug: "์๋ขฐ๋ฉ๊ฑฐ-๋ฐฉ์ ์", question: "์๋ขฐ๋ฉ๊ฑฐ ๋ฐฉ์ ์์
|
|
50
|
+
{ page_slug: "์๋ขฐ๋ฉ๊ฑฐ-๋ฐฉ์ ์", question: "์๋ขฐ๋ฉ๊ฑฐ ๋ฐฉ์ ์์ 1936๋
์ ๋ฐํ๋์๋ค.", answer: "X", explanation: "์๋ขฐ๋ฉ๊ฑฐ ๋ฐฉ์ ์์ 1936๋
์ด ์๋๋ผ 1926๋
์ ์๋ฅด๋น ์๋ขฐ๋ฉ๊ฑฐ๊ฐ ๋ฐํํ์ต๋๋ค.", quiz_type: "ox" },
|
|
51
51
|
{ page_slug: "์์-์ค์ฒฉ", question: "์์ ์ค์ฒฉ์์ ๊ด์ธกํ๋ฉด ์ด๋ค ํ์์ด ์ผ์ด๋๋๊ฐ?", answer: "์ค์ฒฉ์ด ๋ถ๊ดดํ์ฌ ํ๋์ ์ํ๋ง ๋จ๋๋ค", explanation: "๊ด์ธก ํ์๊ฐ ์์ ์์คํ
์ ์ํฅ์ ์ฃผ์ด ์ฌ๋ฌ ๊ฐ๋ฅํ ์ํ ์ค ํ๋๋ก '๋ถ๊ดด'๋ฉ๋๋ค. ์ด๋ฅผ ํ๋ํจ์ ๋ถ๊ดด๋ผ๊ณ ํฉ๋๋ค.", quiz_type: "short_answer" },
|
|
52
52
|
{ page_slug: "์์-์ฝํ", question: "์์ธ์ํ์ธ์ ์์ ์ฝํ์ '___ํ ์๊ฒฉ ์์ฉ'์ด๋ผ ๋ถ๋ ๋ค.", answer: "์ผ์ค์ค", explanation: "์์ธ์ํ์ธ์ ์์ ์ฝํ์ด ๊ตญ์์ ์ค์ฌ๋ก ์ ์๋ฐฐ๋๋ค๊ณ ์๊ฐํ์ฌ 'spooky action at a distance(์ผ์ค์คํ ์๊ฒฉ ์์ฉ)'์ด๋ผ ๋นํํ์ต๋๋ค.", quiz_type: "fill_blank" },
|
|
53
53
|
{ page_slug: "์์-์ค์ฒฉ", question: "์๋ขฐ๋ฉ๊ฑฐ์ ๊ณ ์์ด ์ฌ๊ณ ์คํ์์ ๊ณ ์์ด๋ ์ด์์๋ ์ํ์ ์ฃฝ์ด์๋ ์ํ์ ___์ ๋์ธ๋ค.", answer: "์ค์ฒฉ", explanation: "์ด ์ฌ๊ณ ์คํ์ ์์ ์ค์ฒฉ์ ๊ฐ๋
์ ๊ฑฐ์์ ์ธ๊ณ์ ์ ์ฉํ์ฌ ์์์ญํ์ ํด์ ๋ฌธ์ ๋ฅผ ๋๋ฌ๋ด๊ธฐ ์ํด ๊ณ ์๋์์ต๋๋ค.", quiz_type: "fill_blank" },
|
|
54
54
|
{ page_slug: "์์-์ฝํ", question: "2022๋
๋
ธ๋ฒจ ๋ฌผ๋ฆฌํ์์ ์์ ์ฝํ์ ๋ฒจ ๋ถ๋ฑ์ ์คํ ๊ฒ์ฆ๊ณผ ๊ด๋ จ์ด ์๋ค.", answer: "O", explanation: "์๋ญ ์์คํ, ์กด ํด๋ผ์ฐ์ , ์ํค ์ฐจ์ผ๋ง๊ฑฐ๊ฐ ๋ฒจ ๋ถ๋ฑ์ ์๋ฐ์ ์คํ์ ์ผ๋ก ์ฆ๋ช
ํ์ฌ ์์ ์ฝํ์ ์ค์ฌ์ฑ์ ํ์ธํ ๊ณต๋ก๋ก ์์ํ์ต๋๋ค.", quiz_type: "ox" },
|
|
55
55
|
{ page_slug: "์๋ขฐ๋ฉ๊ฑฐ-๋ฐฉ์ ์", question: "์๊ฐ ๋
๋ฆฝ ์๋ขฐ๋ฉ๊ฑฐ ๋ฐฉ์ ์์์ E๋ ๋ฌด์์ ๋ํ๋ด๋๊ฐ?", answer: "์๋์ง ๊ณ ์ ๊ฐ", explanation: "์๊ฐ ๋
๋ฆฝ ์๋ขฐ๋ฉ๊ฑฐ ๋ฐฉ์ ์ Hฯ = Eฯ์์ E๋ ์์คํ
์ ์๋์ง ๊ณ ์ ๊ฐ์ผ๋ก, ํ์ฉ๋ ์๋์ง ์ค์๋ฅผ ๋ํ๋
๋๋ค.", quiz_type: "short_answer" },
|
|
56
|
-
{ page_slug: "ํ์ด์ ๋ฒ ๋ฅดํฌ", question: "๋ถํ์ ์ฑ ์๋ฆฌ์์ ฮxยทฮp โฅ
|
|
56
|
+
{ page_slug: "ํ์ด์ ๋ฒ ๋ฅดํฌ", question: "๋ถํ์ ์ฑ ์๋ฆฌ์์ ฮxยทฮp โฅ โ ์ด๋ค.", answer: "X", explanation: "์ ํํ ๋ถ๋ฑ์์ ฮxยทฮp โฅ โ/2 ์
๋๋ค. โ๊ฐ ์๋๋ผ โ/2(ํ๋ํฌ ์์์ ์ ๋ฐ)๊ฐ ํํ๊ฐ์
๋๋ค.", quiz_type: "ox" },
|
|
57
57
|
];
|
|
58
58
|
|
|
59
59
|
export const DEMO_LINKS = [
|
|
@@ -68,3 +68,70 @@ export const DEMO_LINKS = [
|
|
|
68
68
|
{ from: "์์-์ค์ฒฉ", to: "ํ์ด์ ๋ฒ ๋ฅดํฌ" },
|
|
69
69
|
{ from: "์์-์ฝํ", to: "์์์ญํ-์
๋ฌธ" },
|
|
70
70
|
];
|
|
71
|
+
|
|
72
|
+
export const CS_DEMO_SOURCES = [
|
|
73
|
+
{
|
|
74
|
+
uri: "demo://data-structures",
|
|
75
|
+
type: "demo",
|
|
76
|
+
title: "์๋ฃ๊ตฌ์กฐ์ ์๊ณ ๋ฆฌ์ฆ ์
๋ฌธ",
|
|
77
|
+
raw_content: "์๋ฃ๊ตฌ์กฐ์ ์๊ณ ๋ฆฌ์ฆ์ ๊ธฐ์ด๋ฅผ ์๊ฐํฉ๋๋ค..."
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
export const CS_DEMO_PAGES = [
|
|
82
|
+
{
|
|
83
|
+
slug: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ",
|
|
84
|
+
title: "์๋ฃ๊ตฌ์กฐ์ ์๊ณ ๋ฆฌ์ฆ ์
๋ฌธ",
|
|
85
|
+
content: `# ์๋ฃ๊ตฌ์กฐ์ ์๊ณ ๋ฆฌ์ฆ ์
๋ฌธ\n\n์๋ฃ๊ตฌ์กฐ๋ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ ์ฅํ๊ณ ์ ๊ทผํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์
๋๋ค.\n\n## ์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ฐฐ์์ผ ํ๋๊ฐ?\n\n๊ฐ์ ๋ฌธ์ ๋ผ๋ ์ด๋ค ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ ํํ๋๋์ ๋ฐ๋ผ ์๊ฐ ๋ณต์ก๋๊ฐ $O(n)$์์ $O(\\log n)$์ผ๋ก ๋ฐ๋ ์ ์์ต๋๋ค. ~~์ฝ๋ฉ ํ
์คํธ ํฉ๊ฒฉ์ ๋น๋ฐ์ด ์ฌ๊ธฐ์~~\n\n## ํต์ฌ ์๋ฃ๊ตฌ์กฐ\n\n- [[๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ]] โ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ ํ ์๋ฃ๊ตฌ์กฐ\n- [[์คํ๊ณผ-ํ]] โ LIFO์ FIFO\n- [[ํด์ํ
์ด๋ธ]] โ $O(1)$ ํ์์ ๋ง๋ฒ\n- [[์ด์งํ์ํธ๋ฆฌ]] โ ์ ๋ ฌ๋ ๋ฐ์ดํฐ์ ํจ์จ์ ๊ด๋ฆฌ\n\n## ์๊ฐ ๋ณต์ก๋\n\n| ์๋ฃ๊ตฌ์กฐ | ํ์ | ์ฝ์
| ์ญ์ |\n|----------|------|------|------|\n| ๋ฐฐ์ด | $O(n)$ | $O(n)$ | $O(n)$ |\n| ํด์ํ
์ด๋ธ | $O(1)$ | $O(1)$ | $O(1)$ |\n| BST | $O(\\log n)$ | $O(\\log n)$ | $O(\\log n)$ |`,
|
|
86
|
+
page_type: "source",
|
|
87
|
+
source_id: 2
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
slug: "๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ",
|
|
91
|
+
title: "๋ฐฐ์ด๊ณผ ์ฐ๊ฒฐ๋ฆฌ์คํธ",
|
|
92
|
+
content: `# ๋ฐฐ์ด๊ณผ ์ฐ๊ฒฐ๋ฆฌ์คํธ\n\n**๋ฐฐ์ด**(Array)๊ณผ **์ฐ๊ฒฐ๋ฆฌ์คํธ**(Linked List)๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ ํ [[์๋ฃ๊ตฌ์กฐ-์
๋ฌธ|์๋ฃ๊ตฌ์กฐ]]์
๋๋ค.\n\n## ๋ฐฐ์ด\n\n์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํฉ๋๋ค.\n\n\`\`\`\n์ธ๋ฑ์ค: [0] [1] [2] [3] [4]\n๊ฐ: 10 20 30 40 50\n\`\`\`\n\n- ์ฅ์ : $O(1)$ ๋๋ค ์ ๊ทผ\n- ๋จ์ : ์ฝ์
/์ญ์ ์ $O(n)$ ์ด๋ ํ์\n\n## ์ฐ๊ฒฐ๋ฆฌ์คํธ\n\n๊ฐ ๋
ธ๋๊ฐ ๋ค์ ๋
ธ๋๋ฅผ ๊ฐ๋ฆฌํต๋๋ค. (ํฌ์ธํฐ์ ํ!)\n\n- ์ฅ์ : $O(1)$ ์ฝ์
/์ญ์ (์์น๋ฅผ ์ ๋)\n- ๋จ์ : $O(n)$ ํ์ โ ์ธ๋ฑ์ค ์ ๊ทผ ๋ถ๊ฐ\n\n## ์ธ์ ๋ญ ์ฐ๋?\n\n๋๋ค ์ ๊ทผ์ด ๋ง์ผ๋ฉด ๋ฐฐ์ด, ์ฝ์
/์ญ์ ๊ฐ ๋ง์ผ๋ฉด ์ฐ๊ฒฐ๋ฆฌ์คํธ. ~~๋ฉด์ ์์ ์ด๊ฒ๋ง ๋๋ตํด๋ ๋ฐ์ ๋จน๊ณ ๋ค์ด๊ฐ๋ค~~`,
|
|
93
|
+
page_type: "concept",
|
|
94
|
+
source_id: 2
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
slug: "์คํ๊ณผ-ํ",
|
|
98
|
+
title: "์คํ๊ณผ ํ",
|
|
99
|
+
content: `# ์คํ๊ณผ ํ\n\n**์คํ**(Stack)๊ณผ **ํ**(Queue)๋ [[์๋ฃ๊ตฌ์กฐ-์
๋ฌธ|์๋ฃ๊ตฌ์กฐ]]์ ํต์ฌ ์ถ์ ์๋ฃํ์
๋๋ค.\n\n## ์คํ (LIFO)\n\nLast In, First Out. ๋ง์ง๋ง์ ๋ฃ์ ๊ฒ์ด ๋จผ์ ๋์ต๋๋ค.\n\n$$push(x) โ [1, 2, 3, x]$$\n$$pop() โ x$$\n\nํ์ฉ: ํจ์ ํธ์ถ ์คํ, ๊ดํธ ๋งค์นญ, DFS, Undo ๊ธฐ๋ฅ\n\n## ํ (FIFO)\n\nFirst In, First Out. ๋จผ์ ๋ฃ์ ๊ฒ์ด ๋จผ์ ๋์ต๋๋ค.\n\nํ์ฉ: BFS, ์์
์ค์ผ์ค๋ง, ํ๋ฆฐํฐ ํ\n\n## ๋ฑ (Deque)\n\n์์ชฝ์์ ์ฝ์
/์ญ์ ๊ฐ๋ฅ. ์คํ๊ณผ ํ์ ์ผ๋ฐํ. (์ฌ์ค ์ด๊ฒ๋ง ์๋ฉด ๋ฉ๋๋ค)`,
|
|
100
|
+
page_type: "concept",
|
|
101
|
+
source_id: 2
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
slug: "ํด์ํ
์ด๋ธ",
|
|
105
|
+
title: "ํด์ํ
์ด๋ธ",
|
|
106
|
+
content: `# ํด์ํ
์ด๋ธ\n\n**ํด์ํ
์ด๋ธ**(Hash Table)์ ํค-๊ฐ ์์ $O(1)$์ ์ ์ฅํ๊ณ ๊ฒ์ํ๋ [[์๋ฃ๊ตฌ์กฐ-์
๋ฌธ|์๋ฃ๊ตฌ์กฐ]]์
๋๋ค.\n\n## ํด์ ํจ์\n\n$$h(key) = key \\mod m$$\n\nํค๋ฅผ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ก ๋ณํํฉ๋๋ค. ์ข์ ํด์ ํจ์๋ ์ถฉ๋์ ์ต์ํํฉ๋๋ค.\n\n## ์ถฉ๋ ํด๊ฒฐ\n\n### ์ฒด์ด๋ (Chaining)\n๊ฐ์ ์ธ๋ฑ์ค์ ์ฐ๊ฒฐ๋ฆฌ์คํธ๋ก ์ ์ฅ. [[๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ|์ฐ๊ฒฐ๋ฆฌ์คํธ]] ํ์ฉ.\n\n### ๊ฐ๋ฐฉ ์ฃผ์๋ฒ (Open Addressing)\n์ถฉ๋ ์ ๋ค์ ๋น ์ฌ๋กฏ์ ์ฐพ์. ์ ํ ํ์ฌ, ์ด์ฐจ ํ์ฌ ๋ฑ.\n\n## Python์ dict\n\nPython์ ๋์
๋๋ฆฌ๊ฐ ๋ฐ๋ก ํด์ํ
์ด๋ธ์
๋๋ค. ~~์ด๋ฏธ ๋งค์ผ ์ฐ๊ณ ์์๋ค๋ ์ฌ์ค~~`,
|
|
107
|
+
page_type: "concept",
|
|
108
|
+
source_id: 2
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
slug: "์ด์งํ์ํธ๋ฆฌ",
|
|
112
|
+
title: "์ด์งํ์ํธ๋ฆฌ",
|
|
113
|
+
content: `# ์ด์งํ์ํธ๋ฆฌ (BST)\n\n**์ด์งํ์ํธ๋ฆฌ**๋ ์ผ์ชฝ ์์ < ๋ถ๋ชจ < ์ค๋ฅธ์ชฝ ์์ ๊ท์น์ ๋ฐ๋ฅด๋ [[์๋ฃ๊ตฌ์กฐ-์
๋ฌธ|์๋ฃ๊ตฌ์กฐ]]์
๋๋ค.\n\n## ์๊ฐ ๋ณต์ก๋\n\n| ์ฐ์ฐ | ํ๊ท | ์ต์
|\n|------|------|------|\n| ํ์ | $O(\\log n)$ | $O(n)$ |\n| ์ฝ์
| $O(\\log n)$ | $O(n)$ |\n| ์ญ์ | $O(\\log n)$ | $O(n)$ |\n\n์ต์
์ ๊ฒฝ์ฐ๋ ํธ๋ฆฌ๊ฐ ํ์ชฝ์ผ๋ก ์น์ฐ์น ๋ ๋ฐ์ํฉ๋๋ค. (ํธํฅ ์ด์ง ํธ๋ฆฌ)\n\n## ๊ท ํ ํธ๋ฆฌ\n\nAVL ํธ๋ฆฌ, Red-Black ํธ๋ฆฌ๋ **ํ์ (rotation)** ์ฐ์ฐ์ ํตํด ์๋์ผ๋ก ๊ท ํ์ ์ ์งํ์ฌ ํญ์ $O(\\log n)$์ ๋ณด์ฅํฉ๋๋ค. ํ์ ์ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ฅผ ์ฌ๋ฐฐ์นํด ํธ๋ฆฌ์ ๋์ด๋ฅผ ๋ฎ์ถ๋ ํต์ฌ ์ฐ์ฐ์
๋๋ค.\n\n## ์ํ\n\n- ์ค์ ์ํ (Inorder): ์ ๋ ฌ๋ ์์๋ก ์ถ๋ ฅ\n- ์ ์ ์ํ (Preorder): ํธ๋ฆฌ ๋ณต์ฌ\n- ํ์ ์ํ (Postorder): ํธ๋ฆฌ ์ญ์ `,
|
|
114
|
+
page_type: "concept",
|
|
115
|
+
source_id: 2
|
|
116
|
+
}
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
export const CS_DEMO_LINKS = [
|
|
120
|
+
{ from: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ", to: "๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ" },
|
|
121
|
+
{ from: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ", to: "์คํ๊ณผ-ํ" },
|
|
122
|
+
{ from: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ", to: "ํด์ํ
์ด๋ธ" },
|
|
123
|
+
{ from: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ", to: "์ด์งํ์ํธ๋ฆฌ" },
|
|
124
|
+
{ from: "ํด์ํ
์ด๋ธ", to: "๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ" },
|
|
125
|
+
{ from: "์คํ๊ณผ-ํ", to: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ" },
|
|
126
|
+
{ from: "์ด์งํ์ํธ๋ฆฌ", to: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ" },
|
|
127
|
+
{ from: "๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ", to: "์๋ฃ๊ตฌ์กฐ-์
๋ฌธ" },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
export const CS_DEMO_QUIZZES = [
|
|
131
|
+
{ page_slug: "๋ฐฐ์ด๊ณผ-์ฐ๊ฒฐ๋ฆฌ์คํธ", question: "๋ฐฐ์ด์์ ์ธ๋ฑ์ค๋ฅผ ํตํ ๋๋ค ์ ๊ทผ์ ์๊ฐ ๋ณต์ก๋๋?", answer: "O(1)", quiz_type: "short_answer", explanation: "๋ฐฐ์ด์ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ ์ฅ๋๋ฏ๋ก ์ธ๋ฑ์ค ๊ณ์ฐ๋ง์ผ๋ก ์ง์ ์ ๊ทผ ๊ฐ๋ฅํฉ๋๋ค." },
|
|
132
|
+
{ page_slug: "ํด์ํ
์ด๋ธ", question: "ํด์ํ
์ด๋ธ์ ํ๊ท ํ์ ์๊ฐ ๋ณต์ก๋๋ ___์ด๋ค.", answer: "O(1)", quiz_type: "fill_blank", explanation: "ํด์ ํจ์๊ฐ ํค๋ฅผ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ก ์ง์ ๋ณํํ๋ฏ๋ก ์์ ์๊ฐ์ ์ ๊ทผ ๊ฐ๋ฅํฉ๋๋ค." },
|
|
133
|
+
{ page_slug: "์คํ๊ณผ-ํ", question: "์คํ์ FIFO(First In First Out) ๊ตฌ์กฐ์ด๋ค.", answer: "X", quiz_type: "ox", explanation: "์คํ์ LIFO(Last In First Out)์
๋๋ค. FIFO๋ ํ์ ํน์ฑ์
๋๋ค." },
|
|
134
|
+
{ page_slug: "์ด์งํ์ํธ๋ฆฌ", question: "BST์์ ์ค์ ์ํ๋ฅผ ํ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ ๋ ฌ๋ ์์๋ก ์ถ๋ ฅ๋๋ค.", answer: "O", quiz_type: "ox", explanation: "BST์ ์ผ์ชฝ < ๋ถ๋ชจ < ์ค๋ฅธ์ชฝ ๊ท์น์ ์ํด ์ค์ ์ํ๋ ์ค๋ฆ์ฐจ์ ์ ๋ ฌ์ ๋ณด์ฅํฉ๋๋ค." },
|
|
135
|
+
{ page_slug: "ํด์ํ
์ด๋ธ", question: "ํด์ ์ถฉ๋์ ํด๊ฒฐํ๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ___๊ณผ ๊ฐ๋ฐฉ ์ฃผ์๋ฒ์ด๋ค.", answer: "์ฒด์ด๋", quiz_type: "fill_blank", explanation: "์ฒด์ด๋์ ๊ฐ์ ์ธ๋ฑ์ค์ ์ฐ๊ฒฐ๋ฆฌ์คํธ๋ก ์ ์ฅํ๊ณ , ๊ฐ๋ฐฉ ์ฃผ์๋ฒ์ ๋ค์ ๋น ์ฌ๋กฏ์ ์ฐพ์ต๋๋ค." },
|
|
136
|
+
{ page_slug: "์ด์งํ์ํธ๋ฆฌ", question: "ํธํฅ ์ด์ง ํธ๋ฆฌ์์ ํ์์ ์ต์
์๊ฐ ๋ณต์ก๋๋?", answer: "O(n)", quiz_type: "short_answer", explanation: "ํธ๋ฆฌ๊ฐ ํ์ชฝ์ผ๋ก ์น์ฐ์น๋ฉด ์ฌ์ค์ ์ฐ๊ฒฐ๋ฆฌ์คํธ์ ๊ฐ์์ ธ ์ ํ ํ์์ด ๋ฉ๋๋ค." },
|
|
137
|
+
];
|
package/src/demo/setup.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import { DEMO_SOURCES, DEMO_PAGES, DEMO_LINKS, DEMO_QUIZZES } from "./sample-data";
|
|
1
|
+
import { DEMO_SOURCES, DEMO_PAGES, DEMO_LINKS, DEMO_QUIZZES, CS_DEMO_SOURCES, CS_DEMO_PAGES, CS_DEMO_LINKS, CS_DEMO_QUIZZES } from "./sample-data";
|
|
2
2
|
import type { Store } from "../store";
|
|
3
3
|
|
|
4
4
|
export async function setupDemo(store: Store): Promise<void> {
|
|
5
|
-
// 1. Insert demo
|
|
5
|
+
// 1. Insert demo sources (physics + CS)
|
|
6
6
|
for (const source of DEMO_SOURCES) {
|
|
7
7
|
store.addSource(source.uri, source.type, source.title, source.raw_content);
|
|
8
8
|
}
|
|
9
|
+
for (const source of CS_DEMO_SOURCES) {
|
|
10
|
+
store.addSource(source.uri, source.type, source.title, source.raw_content);
|
|
11
|
+
}
|
|
9
12
|
|
|
10
|
-
// 2. Insert demo pages
|
|
13
|
+
// 2. Insert demo pages (physics + CS)
|
|
11
14
|
for (const page of DEMO_PAGES) {
|
|
12
|
-
store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type);
|
|
15
|
+
store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type as 'source' | 'concept');
|
|
16
|
+
}
|
|
17
|
+
for (const page of CS_DEMO_PAGES) {
|
|
18
|
+
store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type as 'source' | 'concept');
|
|
13
19
|
}
|
|
14
20
|
|
|
15
|
-
// 3. Insert demo links
|
|
21
|
+
// 3. Insert demo links (physics + CS)
|
|
16
22
|
for (const link of DEMO_LINKS) {
|
|
17
23
|
const fromPage = store.getPage(link.from);
|
|
18
24
|
const toPage = store.getPage(link.to);
|
|
@@ -20,12 +26,25 @@ export async function setupDemo(store: Store): Promise<void> {
|
|
|
20
26
|
store.addLink(fromPage.id, toPage.id, toPage.title);
|
|
21
27
|
}
|
|
22
28
|
}
|
|
29
|
+
for (const link of CS_DEMO_LINKS) {
|
|
30
|
+
const fromPage = store.getPage(link.from);
|
|
31
|
+
const toPage = store.getPage(link.to);
|
|
32
|
+
if (fromPage && toPage) {
|
|
33
|
+
store.addLink(fromPage.id, toPage.id, toPage.title);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
23
36
|
|
|
24
|
-
// 4. Insert demo quizzes
|
|
37
|
+
// 4. Insert demo quizzes (physics + CS)
|
|
25
38
|
for (const quiz of DEMO_QUIZZES) {
|
|
26
39
|
const page = store.getPage(quiz.page_slug);
|
|
27
40
|
if (page) {
|
|
28
41
|
store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type, (quiz as any).explanation || "");
|
|
29
42
|
}
|
|
30
43
|
}
|
|
44
|
+
for (const quiz of CS_DEMO_QUIZZES) {
|
|
45
|
+
const page = store.getPage(quiz.page_slug);
|
|
46
|
+
if (page) {
|
|
47
|
+
store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type, (quiz as any).explanation || "");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
31
50
|
}
|
package/src/expand/llm.ts
CHANGED
|
@@ -31,7 +31,7 @@ export async function expandWithApi(page: Page, context: Page[], provider: strin
|
|
|
31
31
|
const { default: Anthropic } = await import("@anthropic-ai/sdk");
|
|
32
32
|
const client = new Anthropic();
|
|
33
33
|
const resp = await client.messages.create({
|
|
34
|
-
model: model || "claude-sonnet-4-
|
|
34
|
+
model: model || "claude-sonnet-4-6",
|
|
35
35
|
max_tokens: 4096,
|
|
36
36
|
messages: [{ role: "user", content: prompt }],
|
|
37
37
|
});
|
|
@@ -42,7 +42,7 @@ export async function expandWithApi(page: Page, context: Page[], provider: strin
|
|
|
42
42
|
const { default: OpenAI } = await import("openai");
|
|
43
43
|
const client = new OpenAI();
|
|
44
44
|
const resp = await client.chat.completions.create({
|
|
45
|
-
model: model || "gpt-
|
|
45
|
+
model: model || "gpt-5.4",
|
|
46
46
|
messages: [{ role: "user", content: prompt }],
|
|
47
47
|
});
|
|
48
48
|
return resp.choices[0].message.content || "";
|