@open330/kiwimu 0.4.0 → 0.7.1
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/bin/kiwimu +1 -1
- package/package.json +4 -1
- package/personas/namuwiki.json +6 -0
- package/src/build/renderer.ts +49 -2
- package/src/build/static/search.js +33 -2
- package/src/build/static/style.css +181 -44
- package/src/build/templates.ts +297 -167
- package/src/config.ts +35 -29
- package/src/demo/sample-data.ts +70 -0
- package/src/demo/setup.ts +31 -0
- package/src/expand/llm.ts +1 -1
- package/src/index.ts +208 -458
- package/src/ingest/docx.ts +0 -8
- package/src/ingest/legacy.ts +4 -4
- package/src/ingest/pdf.ts +1 -1
- package/src/ingest/pptx.ts +0 -1
- package/src/ingest/web.test.ts +41 -0
- package/src/ingest/web.ts +61 -62
- package/src/llm-client.ts +203 -126
- package/src/pipeline/chunker.test.ts +42 -0
- package/src/pipeline/chunker.ts +1 -48
- package/src/pipeline/llm-chunker.ts +133 -55
- package/src/server.ts +327 -0
- package/src/services/ingest.ts +100 -0
- package/src/store.test.ts +132 -0
- package/src/store.ts +102 -2
- package/src/pipeline/llm-linker.ts +0 -84
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse, stringify } from "smol-toml";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
4
|
|
|
5
5
|
export const CONFIG_FILE = "kiwi.toml";
|
|
6
6
|
export const DB_FILE = "kiwi.db";
|
|
@@ -29,38 +29,43 @@ export interface KiwiConfig {
|
|
|
29
29
|
active_persona?: string; // name of the active persona
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
/** Directory containing built-in persona JSON files (shipped with the package) */
|
|
33
|
+
function getPersonasDir(): string {
|
|
34
|
+
// Works both in dev (src/) and built (dist/) — personas/ is at project root
|
|
35
|
+
return join(dirname(__dirname), "personas");
|
|
36
|
+
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
5. **구조**: 목차가 잘 정리된 체계적 구조. 소제목을 적극 활용
|
|
42
|
-
6. **부가 정보**: "여담으로~", "참고로~", "사실~" 등의 표현으로 부가 정보 추가
|
|
43
|
-
7. **링크**: 관련 개념에 적극적으로 [[위키 링크]]를 사용
|
|
38
|
+
/** Load a single persona from a JSON file */
|
|
39
|
+
function loadPersonaFile(filePath: string): Persona {
|
|
40
|
+
return JSON.parse(readFileSync(filePath, "utf-8")) as Persona;
|
|
41
|
+
}
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
/** Load all built-in personas from the personas/ directory */
|
|
44
|
+
export function loadBuiltinPersonas(): Persona[] {
|
|
45
|
+
const dir = getPersonasDir();
|
|
46
|
+
if (!existsSync(dir)) return [];
|
|
47
|
+
return readdirSync(dir)
|
|
48
|
+
.filter(f => f.endsWith(".json"))
|
|
49
|
+
.map(f => loadPersonaFile(join(dir, f)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Get the default persona (first built-in, or fallback) */
|
|
53
|
+
export function getDefaultPersona(): Persona {
|
|
54
|
+
const builtins = loadBuiltinPersonas();
|
|
55
|
+
if (builtins.length > 0) return builtins[0];
|
|
56
|
+
// Minimal fallback if no persona files exist
|
|
57
|
+
return { name: "default", description: "Default wiki style", system_prompt: "", content_style: "" };
|
|
58
|
+
}
|
|
55
59
|
|
|
56
60
|
export function defaultConfig(name: string): KiwiConfig {
|
|
61
|
+
const builtins = loadBuiltinPersonas();
|
|
57
62
|
return {
|
|
58
63
|
project: { name, created: new Date().toISOString().slice(0, 10) },
|
|
59
64
|
build: { output_dir: SITE_DIR },
|
|
60
65
|
llm: { provider: "gemini", model: "gemini-2.0-flash-lite", api_key: "", endpoint: "" },
|
|
61
66
|
deploy: { target: "gh-pages" },
|
|
62
|
-
personas: [
|
|
63
|
-
active_persona: "
|
|
67
|
+
personas: builtins.length > 0 ? builtins : [getDefaultPersona()],
|
|
68
|
+
active_persona: builtins[0]?.name || "default",
|
|
64
69
|
};
|
|
65
70
|
}
|
|
66
71
|
|
|
@@ -75,18 +80,19 @@ export function saveConfig(root: string, config: KiwiConfig): void {
|
|
|
75
80
|
|
|
76
81
|
export function loadConfig(root: string): KiwiConfig {
|
|
77
82
|
const content = require("fs").readFileSync(join(root, CONFIG_FILE), "utf-8");
|
|
78
|
-
const raw = parse(content) as
|
|
83
|
+
const raw = parse(content) as Partial<KiwiConfig> & Record<string, unknown>;
|
|
79
84
|
// Migrate old config format
|
|
80
85
|
if (!raw.llm) {
|
|
81
86
|
raw.llm = { provider: "gemini", model: "gemini-2.0-flash-lite", api_key: "", endpoint: "" };
|
|
82
87
|
}
|
|
83
88
|
// Migrate: add default persona if missing
|
|
84
89
|
if (!raw.personas || !raw.personas.length) {
|
|
85
|
-
|
|
86
|
-
raw.
|
|
90
|
+
const builtins = loadBuiltinPersonas();
|
|
91
|
+
raw.personas = builtins.length > 0 ? builtins : [getDefaultPersona()];
|
|
92
|
+
raw.active_persona = raw.personas[0]?.name || "default";
|
|
87
93
|
}
|
|
88
94
|
if (!raw.active_persona) {
|
|
89
|
-
raw.active_persona = raw.personas[0]?.name || "
|
|
95
|
+
raw.active_persona = raw.personas[0]?.name || "default";
|
|
90
96
|
}
|
|
91
97
|
return raw as KiwiConfig;
|
|
92
98
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const DEMO_SOURCES = [
|
|
2
|
+
{
|
|
3
|
+
uri: "demo://quantum-mechanics",
|
|
4
|
+
type: "demo",
|
|
5
|
+
title: "양자역학 입문",
|
|
6
|
+
raw_content: "양자역학의 기초 개념을 소개합니다..."
|
|
7
|
+
}
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export const DEMO_PAGES = [
|
|
11
|
+
{
|
|
12
|
+
slug: "양자역학-입문",
|
|
13
|
+
title: "양자역학 입문",
|
|
14
|
+
content: `# 양자역학 입문\n\n양자역학은 원자와 아원자 입자의 행동을 설명하는 물리학의 분야입니다.\n\n## 핵심 개념\n\n### 파동-입자 이중성\n입자는 파동과 입자의 성질을 동시에 가집니다. ~~이것만 이해하면 양자역학 마스터~~\n\n### 불확정성 원리\n[[하이젠베르크]]가 제안한 원리로, 위치와 운동량을 동시에 정확히 측정할 수 없습니다. (아인슈타인도 처음엔 못 믿었다고 합니다)\n\n### 슈뢰딩거 방정식\n$$i\\hbar\\frac{\\partial}{\\partial t}|\\psi(t)\\rangle = \\hat{H}|\\psi(t)\\rangle$$\n\n양자계의 시간 변화를 기술하는 기본 방정식입니다.\n\n## 관련 개념\n- [[슈뢰딩거-방정식]]\n- [[하이젠베르크]]\n- [[양자-중첩]]\n- [[양자-얽힘]]`,
|
|
15
|
+
page_type: "source",
|
|
16
|
+
source_id: 1
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
slug: "슈뢰딩거-방정식",
|
|
20
|
+
title: "슈뢰딩거 방정식",
|
|
21
|
+
content: `# 슈뢰딩거 방정식\n\n**슈뢰딩거 방정식**은 양자역학에서 양자계의 [[파동함수]]를 기술하는 편미분 방정식입니다.\n\n## 시간 의존 방정식\n$$i\\hbar\\frac{\\partial}{\\partial t}\\Psi(\\mathbf{r},t) = \\hat{H}\\Psi(\\mathbf{r},t)$$\n\n## 시간 독립 방정식\n$$\\hat{H}|\\psi\\rangle = E|\\psi\\rangle$$\n\n여기서 $E$는 에너지 고유값입니다. ~~사실 이 방정식만 풀면 세상 모든 화학 문제가 풀린다는 건 비밀~~\n\n## 역사\n1926년 에르빈 슈뢰딩거가 발표했습니다. [[하이젠베르크]]의 행렬역학과 동등하다는 것이 나중에 증명되었습니다.`,
|
|
22
|
+
page_type: "concept",
|
|
23
|
+
source_id: 1
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
slug: "하이젠베르크",
|
|
27
|
+
title: "하이젠베르크의 불확정성 원리",
|
|
28
|
+
content: `# 하이젠베르크의 불확정성 원리\n\n**불확정성 원리**는 1927년 베르너 하이젠베르크가 발표한 [[양자역학-입문|양자역학]]의 핵심 원리입니다.\n\n## 수학적 표현\n$$\\Delta x \\cdot \\Delta p \\geq \\frac{\\hbar}{2}$$\n\n위치($x$)의 불확정성과 운동량($p$)의 불확정성의 곱은 항상 $\\frac{\\hbar}{2}$ 이상입니다.\n\n## 의미\n(이건 측정 기술의 한계가 아니라 자연의 근본적인 성질입니다!)\n\n이 원리는 고전물리학의 결정론적 세계관에 근본적인 도전을 제기했습니다.`,
|
|
29
|
+
page_type: "concept",
|
|
30
|
+
source_id: 1
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
slug: "양자-중첩",
|
|
34
|
+
title: "양자 중첩",
|
|
35
|
+
content: `# 양자 중첩\n\n**양자 중첩**(superposition)은 양자계가 여러 상태의 선형 결합으로 존재할 수 있는 [[양자역학-입문|양자역학]]의 원리입니다.\n\n## 슈뢰딩거의 고양이\n가장 유명한 사고 실험으로, 고양이가 살아있는 상태와 죽어있는 상태의 중첩에 놓인다는 역설입니다. ~~고양이한테는 미안하지만~~\n\n$$|\\psi\\rangle = \\alpha|살아있음\\rangle + \\beta|죽어있음\\rangle$$\n\n## 측정 문제\n관측하면 중첩이 붕괴하여 하나의 상태만 남습니다. [[하이젠베르크|불확정성 원리]]와도 깊이 연결됩니다.`,
|
|
36
|
+
page_type: "concept",
|
|
37
|
+
source_id: 1
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
slug: "양자-얽힘",
|
|
41
|
+
title: "양자 얽힘",
|
|
42
|
+
content: `# 양자 얽힘\n\n**양자 얽힘**(entanglement)은 두 입자가 서로의 상태에 즉각적으로 영향을 주는 [[양자역학-입문|양자역학]] 현상입니다.\n\n## EPR 역설\n아인슈타인, 포돌스키, 로젠이 1935년에 제기한 사고 실험입니다. 아인슈타인은 이를 "으스스한 원격 작용"이라 불렀습니다.\n\n## 벨 부등식\n1964년 존 벨이 증명한 부등식으로, 실험으로 양자역학의 예측이 맞다는 것이 확인되었습니다. (2022년 노벨 물리학상!)\n\n## 응용\n- 양자 컴퓨팅\n- 양자 암호\n- 양자 텔레포테이션`,
|
|
43
|
+
page_type: "concept",
|
|
44
|
+
source_id: 1
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
export const DEMO_QUIZZES = [
|
|
49
|
+
{ page_slug: "하이젠베르크", question: "___은 위치와 운동량을 동시에 정확히 측정할 수 없다는 양자역학의 원리이다.", answer: "불확정성 원리", quiz_type: "fill_blank" },
|
|
50
|
+
{ page_slug: "슈뢰딩거-방정식", question: "슈뢰딩거 방정식은 1926년에 발표되었다.", answer: "O", quiz_type: "ox" },
|
|
51
|
+
{ page_slug: "양자-중첩", question: "양자 중첩에서 관측하면 어떤 현상이 일어나는가?", answer: "중첩이 붕괴하여 하나의 상태만 남는다", quiz_type: "short_answer" },
|
|
52
|
+
{ page_slug: "양자-얽힘", question: "아인슈타인은 양자 얽힘을 '___한 원격 작용'이라 불렀다.", answer: "으스스", quiz_type: "fill_blank" },
|
|
53
|
+
{ page_slug: "양자-중첩", question: "슈뢰딩거의 고양이 사고실험에서 고양이는 살아있는 상태와 죽어있는 상태의 ___에 놓인다.", answer: "중첩", quiz_type: "fill_blank" },
|
|
54
|
+
{ page_slug: "양자-얽힘", question: "2022년 노벨 물리학상은 양자 얽힘의 벨 부등식 실험 검증과 관련이 있다.", answer: "O", quiz_type: "ox" },
|
|
55
|
+
{ page_slug: "슈뢰딩거-방정식", question: "시간 독립 슈뢰딩거 방정식에서 E는 무엇을 나타내는가?", answer: "에너지 고유값", quiz_type: "short_answer" },
|
|
56
|
+
{ page_slug: "하이젠베르크", question: "불확정성 원리에서 Δx·Δp ≥ ℏ/2 이다.", answer: "O", quiz_type: "ox" },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const DEMO_LINKS = [
|
|
60
|
+
// source_slug -> target_slug
|
|
61
|
+
{ from: "양자역학-입문", to: "슈뢰딩거-방정식" },
|
|
62
|
+
{ from: "양자역학-입문", to: "하이젠베르크" },
|
|
63
|
+
{ from: "양자역학-입문", to: "양자-중첩" },
|
|
64
|
+
{ from: "양자역학-입문", to: "양자-얽힘" },
|
|
65
|
+
{ from: "슈뢰딩거-방정식", to: "하이젠베르크" },
|
|
66
|
+
{ from: "하이젠베르크", to: "양자역학-입문" },
|
|
67
|
+
{ from: "양자-중첩", to: "양자역학-입문" },
|
|
68
|
+
{ from: "양자-중첩", to: "하이젠베르크" },
|
|
69
|
+
{ from: "양자-얽힘", to: "양자역학-입문" },
|
|
70
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DEMO_SOURCES, DEMO_PAGES, DEMO_LINKS, DEMO_QUIZZES } from "./sample-data";
|
|
2
|
+
import type { Store } from "../store";
|
|
3
|
+
|
|
4
|
+
export async function setupDemo(store: Store): Promise<void> {
|
|
5
|
+
// 1. Insert demo source
|
|
6
|
+
for (const source of DEMO_SOURCES) {
|
|
7
|
+
store.addSource(source.uri, source.type, source.title, source.raw_content);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// 2. Insert demo pages
|
|
11
|
+
for (const page of DEMO_PAGES) {
|
|
12
|
+
store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 3. Insert demo links
|
|
16
|
+
for (const link of DEMO_LINKS) {
|
|
17
|
+
const fromPage = store.getPage(link.from);
|
|
18
|
+
const toPage = store.getPage(link.to);
|
|
19
|
+
if (fromPage && toPage) {
|
|
20
|
+
store.addLink(fromPage.id, toPage.id, toPage.title);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 4. Insert demo quizzes
|
|
25
|
+
for (const quiz of DEMO_QUIZZES) {
|
|
26
|
+
const page = store.getPage(quiz.page_slug);
|
|
27
|
+
if (page) {
|
|
28
|
+
store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/expand/llm.ts
CHANGED
|
@@ -35,7 +35,7 @@ export async function expandWithApi(page: Page, context: Page[], provider: strin
|
|
|
35
35
|
max_tokens: 4096,
|
|
36
36
|
messages: [{ role: "user", content: prompt }],
|
|
37
37
|
});
|
|
38
|
-
return (resp.content[0] as
|
|
38
|
+
return (resp.content[0] as { type: string; text: string }).text;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
if (provider === "openai") {
|