mulmocast-preprocessor 0.1.0 → 0.1.2
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 +11 -0
- package/lib/cli/commands/process.d.ts +2 -0
- package/lib/cli/commands/process.js +2 -0
- package/lib/cli/commands/summarize.d.ts +17 -0
- package/lib/cli/commands/summarize.js +67 -0
- package/lib/cli/index.js +84 -0
- package/lib/core/filter.d.ts +4 -4
- package/lib/core/filter.js +10 -6
- package/lib/core/process.d.ts +1 -1
- package/lib/core/process.js +5 -9
- package/lib/core/summarize/index.d.ts +8 -0
- package/lib/core/summarize/index.js +114 -0
- package/lib/core/summarize/prompts.d.ts +18 -0
- package/lib/core/summarize/prompts.js +87 -0
- package/lib/core/summarize/provider.d.ts +14 -0
- package/lib/core/summarize/provider.js +43 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/types/summarize.d.ts +62 -0
- package/lib/types/summarize.js +32 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -25,6 +25,15 @@ mulmocast-preprocessor script.json --profile summary -o summary.json
|
|
|
25
25
|
# Output to stdout (for piping)
|
|
26
26
|
mulmocast-preprocessor script.json --profile teaser
|
|
27
27
|
|
|
28
|
+
# Filter by section
|
|
29
|
+
mulmocast-preprocessor script.json --section chapter1
|
|
30
|
+
|
|
31
|
+
# Filter by tags
|
|
32
|
+
mulmocast-preprocessor script.json --tags concept,demo
|
|
33
|
+
|
|
34
|
+
# Combine profile and filters
|
|
35
|
+
mulmocast-preprocessor script.json --profile summary --section chapter1
|
|
36
|
+
|
|
28
37
|
# List available profiles
|
|
29
38
|
mulmocast-preprocessor profiles script.json
|
|
30
39
|
```
|
|
@@ -35,6 +44,8 @@ mulmocast-preprocessor profiles script.json
|
|
|
35
44
|
|--------|-------|-------------|
|
|
36
45
|
| `--profile <name>` | `-p` | Profile name to apply (default: "default") |
|
|
37
46
|
| `--output <path>` | `-o` | Output file path (default: stdout) |
|
|
47
|
+
| `--section <name>` | `-s` | Filter by section name |
|
|
48
|
+
| `--tags <tags>` | `-t` | Filter by tags (comma-separated) |
|
|
38
49
|
| `--help` | `-h` | Show help |
|
|
39
50
|
| `--version` | `-v` | Show version |
|
|
40
51
|
|
|
@@ -10,6 +10,8 @@ export const processCommand = (scriptPath, options) => {
|
|
|
10
10
|
const script = JSON.parse(content);
|
|
11
11
|
const result = processScript(script, {
|
|
12
12
|
profile: options.profile,
|
|
13
|
+
section: options.section,
|
|
14
|
+
tags: options.tags,
|
|
13
15
|
});
|
|
14
16
|
const output = JSON.stringify(result, null, 2);
|
|
15
17
|
if (options.output) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LLMProvider, SummarizeFormat } from "../../types/summarize.js";
|
|
2
|
+
interface SummarizeCommandOptions {
|
|
3
|
+
provider?: LLMProvider;
|
|
4
|
+
model?: string;
|
|
5
|
+
format?: SummarizeFormat;
|
|
6
|
+
lang?: string;
|
|
7
|
+
targetLength?: number;
|
|
8
|
+
systemPrompt?: string;
|
|
9
|
+
verbose?: boolean;
|
|
10
|
+
section?: string;
|
|
11
|
+
tags?: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Summarize command handler - outputs summary to stdout
|
|
15
|
+
*/
|
|
16
|
+
export declare const summarizeCommand: (scriptPath: string, options: SummarizeCommandOptions) => Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { GraphAILogger } from "graphai";
|
|
3
|
+
import { summarizeScript } from "../../core/summarize/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Check if input is a URL
|
|
6
|
+
*/
|
|
7
|
+
const isUrl = (input) => {
|
|
8
|
+
return input.startsWith("http://") || input.startsWith("https://");
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Fetch JSON from URL with timeout
|
|
12
|
+
*/
|
|
13
|
+
const fetchJson = async (url) => {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timeout_ms = 30000;
|
|
16
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout_ms);
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
return (await response.json());
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
clearTimeout(timeoutId);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Load script from file path or URL
|
|
30
|
+
*/
|
|
31
|
+
const loadScript = async (input) => {
|
|
32
|
+
if (isUrl(input)) {
|
|
33
|
+
return fetchJson(input);
|
|
34
|
+
}
|
|
35
|
+
const content = readFileSync(input, "utf-8");
|
|
36
|
+
return JSON.parse(content);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Summarize command handler - outputs summary to stdout
|
|
40
|
+
*/
|
|
41
|
+
export const summarizeCommand = async (scriptPath, options) => {
|
|
42
|
+
try {
|
|
43
|
+
const script = await loadScript(scriptPath);
|
|
44
|
+
const result = await summarizeScript(script, {
|
|
45
|
+
provider: options.provider ?? "openai",
|
|
46
|
+
model: options.model,
|
|
47
|
+
format: options.format ?? "text",
|
|
48
|
+
lang: options.lang,
|
|
49
|
+
targetLengthChars: options.targetLength,
|
|
50
|
+
systemPrompt: options.systemPrompt,
|
|
51
|
+
verbose: options.verbose ?? false,
|
|
52
|
+
section: options.section,
|
|
53
|
+
tags: options.tags,
|
|
54
|
+
});
|
|
55
|
+
// Output summary to stdout
|
|
56
|
+
process.stdout.write(result.summary + "\n");
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
GraphAILogger.error(`Error: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
GraphAILogger.error("Unknown error occurred");
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
};
|
package/lib/cli/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import yargs from "yargs";
|
|
|
3
3
|
import { hideBin } from "yargs/helpers";
|
|
4
4
|
import { processCommand } from "./commands/process.js";
|
|
5
5
|
import { profilesCommand } from "./commands/profiles.js";
|
|
6
|
+
import { summarizeCommand } from "./commands/summarize.js";
|
|
6
7
|
yargs(hideBin(process.argv))
|
|
7
8
|
.command("$0 <script>", "Process MulmoScript with profile", (builder) => builder
|
|
8
9
|
.positional("script", {
|
|
@@ -20,10 +21,23 @@ yargs(hideBin(process.argv))
|
|
|
20
21
|
alias: "o",
|
|
21
22
|
describe: "Output file path (default: stdout)",
|
|
22
23
|
type: "string",
|
|
24
|
+
})
|
|
25
|
+
.option("section", {
|
|
26
|
+
alias: "s",
|
|
27
|
+
describe: "Filter by section name",
|
|
28
|
+
type: "string",
|
|
29
|
+
})
|
|
30
|
+
.option("tags", {
|
|
31
|
+
alias: "t",
|
|
32
|
+
describe: "Filter by tags (comma-separated)",
|
|
33
|
+
type: "string",
|
|
23
34
|
}), (argv) => {
|
|
35
|
+
const tags = argv.tags ? argv.tags.split(",").map((t) => t.trim()) : undefined;
|
|
24
36
|
processCommand(argv.script, {
|
|
25
37
|
profile: argv.profile,
|
|
26
38
|
output: argv.output,
|
|
39
|
+
section: argv.section,
|
|
40
|
+
tags,
|
|
27
41
|
});
|
|
28
42
|
})
|
|
29
43
|
.command("profiles <script>", "List available profiles in script", (builder) => builder.positional("script", {
|
|
@@ -32,10 +46,80 @@ yargs(hideBin(process.argv))
|
|
|
32
46
|
demandOption: true,
|
|
33
47
|
}), (argv) => {
|
|
34
48
|
profilesCommand(argv.script);
|
|
49
|
+
})
|
|
50
|
+
.command("summarize <script>", "Generate a summary of the script content", (builder) => builder
|
|
51
|
+
.positional("script", {
|
|
52
|
+
describe: "Path or URL to MulmoScript JSON file",
|
|
53
|
+
type: "string",
|
|
54
|
+
demandOption: true,
|
|
55
|
+
})
|
|
56
|
+
.option("provider", {
|
|
57
|
+
describe: "LLM provider (openai, anthropic, groq, gemini)",
|
|
58
|
+
type: "string",
|
|
59
|
+
default: "openai",
|
|
60
|
+
})
|
|
61
|
+
.option("model", {
|
|
62
|
+
alias: "m",
|
|
63
|
+
describe: "Model name",
|
|
64
|
+
type: "string",
|
|
65
|
+
})
|
|
66
|
+
.option("format", {
|
|
67
|
+
alias: "f",
|
|
68
|
+
describe: "Output format (text, markdown)",
|
|
69
|
+
type: "string",
|
|
70
|
+
default: "text",
|
|
71
|
+
})
|
|
72
|
+
.option("lang", {
|
|
73
|
+
alias: "l",
|
|
74
|
+
describe: "Output language (e.g., ja, en, zh)",
|
|
75
|
+
type: "string",
|
|
76
|
+
})
|
|
77
|
+
.option("target-length", {
|
|
78
|
+
describe: "Target summary length in characters",
|
|
79
|
+
type: "number",
|
|
80
|
+
})
|
|
81
|
+
.option("system-prompt", {
|
|
82
|
+
describe: "Custom system prompt",
|
|
83
|
+
type: "string",
|
|
84
|
+
})
|
|
85
|
+
.option("verbose", {
|
|
86
|
+
describe: "Show detailed progress",
|
|
87
|
+
type: "boolean",
|
|
88
|
+
default: false,
|
|
89
|
+
})
|
|
90
|
+
.option("section", {
|
|
91
|
+
alias: "s",
|
|
92
|
+
describe: "Filter by section name",
|
|
93
|
+
type: "string",
|
|
94
|
+
})
|
|
95
|
+
.option("tags", {
|
|
96
|
+
alias: "t",
|
|
97
|
+
describe: "Filter by tags (comma-separated)",
|
|
98
|
+
type: "string",
|
|
99
|
+
}), (argv) => {
|
|
100
|
+
const tags = argv.tags ? argv.tags.split(",").map((t) => t.trim()) : undefined;
|
|
101
|
+
summarizeCommand(argv.script, {
|
|
102
|
+
provider: argv.provider,
|
|
103
|
+
model: argv.model,
|
|
104
|
+
format: argv.format,
|
|
105
|
+
lang: argv.lang,
|
|
106
|
+
targetLength: argv.targetLength,
|
|
107
|
+
systemPrompt: argv.systemPrompt,
|
|
108
|
+
verbose: argv.verbose,
|
|
109
|
+
section: argv.section,
|
|
110
|
+
tags,
|
|
111
|
+
});
|
|
35
112
|
})
|
|
36
113
|
.example("$0 script.json --profile summary -o summary.json", "Apply summary profile and save to file")
|
|
37
114
|
.example("$0 script.json -p teaser", "Apply teaser profile and output to stdout")
|
|
115
|
+
.example("$0 script.json --section chapter1", "Filter by section")
|
|
116
|
+
.example("$0 script.json --tags concept,demo", "Filter by tags")
|
|
117
|
+
.example("$0 script.json -p summary -s chapter1", "Combine profile and section filter")
|
|
38
118
|
.example("$0 profiles script.json", "List all available profiles")
|
|
119
|
+
.example("$0 summarize script.json", "Generate text summary with OpenAI")
|
|
120
|
+
.example("$0 summarize script.json --format markdown", "Generate markdown summary")
|
|
121
|
+
.example("$0 summarize script.json -l ja", "Output summary in Japanese")
|
|
122
|
+
.example("$0 summarize https://example.com/script.json", "Summarize from URL")
|
|
39
123
|
.help()
|
|
40
124
|
.alias("h", "help")
|
|
41
125
|
.version()
|
package/lib/core/filter.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { MulmoScript } from "mulmocast";
|
|
2
2
|
import type { ExtendedScript } from "../types/index.js";
|
|
3
3
|
/**
|
|
4
|
-
* Filter beats by section
|
|
4
|
+
* Filter beats by section (preserves meta for chaining)
|
|
5
5
|
*/
|
|
6
|
-
export declare const filterBySection: (script: ExtendedScript, section: string) =>
|
|
6
|
+
export declare const filterBySection: (script: ExtendedScript, section: string) => ExtendedScript;
|
|
7
7
|
/**
|
|
8
|
-
* Filter beats by tags (
|
|
8
|
+
* Filter beats by tags (preserves meta for chaining)
|
|
9
9
|
*/
|
|
10
|
-
export declare const filterByTags: (script: ExtendedScript, tags: string[]) =>
|
|
10
|
+
export declare const filterByTags: (script: ExtendedScript, tags: string[]) => ExtendedScript;
|
|
11
11
|
/**
|
|
12
12
|
* Strip variants and meta fields, converting to standard MulmoScript
|
|
13
13
|
*/
|
package/lib/core/filter.js
CHANGED
|
@@ -2,25 +2,29 @@ const stripBeatExtendedFields = (beat) => {
|
|
|
2
2
|
const { variants: __variants, meta: __meta, ...baseBeat } = beat;
|
|
3
3
|
return baseBeat;
|
|
4
4
|
};
|
|
5
|
-
const
|
|
5
|
+
const filterBeatsToMulmoScript = (script, predicate) => {
|
|
6
6
|
const { outputProfiles: __outputProfiles, ...baseScript } = script;
|
|
7
7
|
return {
|
|
8
8
|
...baseScript,
|
|
9
9
|
beats: script.beats.filter(predicate).map(stripBeatExtendedFields),
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
|
+
const filterBeatsPreservingMeta = (script, predicate) => ({
|
|
13
|
+
...script,
|
|
14
|
+
beats: script.beats.filter(predicate),
|
|
15
|
+
});
|
|
12
16
|
/**
|
|
13
|
-
* Filter beats by section
|
|
17
|
+
* Filter beats by section (preserves meta for chaining)
|
|
14
18
|
*/
|
|
15
|
-
export const filterBySection = (script, section) =>
|
|
19
|
+
export const filterBySection = (script, section) => filterBeatsPreservingMeta(script, (beat) => beat.meta?.section === section);
|
|
16
20
|
/**
|
|
17
|
-
* Filter beats by tags (
|
|
21
|
+
* Filter beats by tags (preserves meta for chaining)
|
|
18
22
|
*/
|
|
19
23
|
export const filterByTags = (script, tags) => {
|
|
20
24
|
const tagSet = new Set(tags);
|
|
21
|
-
return
|
|
25
|
+
return filterBeatsPreservingMeta(script, (beat) => (beat.meta?.tags ?? []).some((tag) => tagSet.has(tag)));
|
|
22
26
|
};
|
|
23
27
|
/**
|
|
24
28
|
* Strip variants and meta fields, converting to standard MulmoScript
|
|
25
29
|
*/
|
|
26
|
-
export const stripExtendedFields = (script) =>
|
|
30
|
+
export const stripExtendedFields = (script) => filterBeatsToMulmoScript(script, () => true);
|
package/lib/core/process.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ import type { MulmoScript } from "mulmocast";
|
|
|
2
2
|
import type { ExtendedScript, ProcessOptions } from "../types/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Main processing function
|
|
5
|
-
* Applies
|
|
5
|
+
* Applies filters first (while meta exists), then profile
|
|
6
6
|
*/
|
|
7
7
|
export declare const processScript: (script: ExtendedScript, options?: ProcessOptions) => MulmoScript;
|
package/lib/core/process.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { applyProfile } from "./variant.js";
|
|
2
2
|
import { filterBySection, filterByTags, stripExtendedFields } from "./filter.js";
|
|
3
|
-
const toExtendedScript = (script) => ({
|
|
4
|
-
...script,
|
|
5
|
-
beats: script.beats.map((beat) => ({ ...beat })),
|
|
6
|
-
});
|
|
7
3
|
/**
|
|
8
4
|
* Main processing function
|
|
9
|
-
* Applies
|
|
5
|
+
* Applies filters first (while meta exists), then profile
|
|
10
6
|
*/
|
|
11
7
|
export const processScript = (script, options = {}) => {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
return
|
|
8
|
+
const afterSection = options.section ? filterBySection(script, options.section) : script;
|
|
9
|
+
const afterTags = options.tags && options.tags.length > 0 ? filterByTags(afterSection, options.tags) : afterSection;
|
|
10
|
+
const afterProfile = options.profile && options.profile !== "default" ? applyProfile(afterTags, options.profile) : stripExtendedFields(afterTags);
|
|
11
|
+
return afterProfile;
|
|
16
12
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExtendedScript } from "../../types/index.js";
|
|
2
|
+
import type { SummarizeOptions, SummarizeResult } from "../../types/summarize.js";
|
|
3
|
+
/**
|
|
4
|
+
* Main summarize function - generates a summary of the entire script
|
|
5
|
+
*/
|
|
6
|
+
export declare const summarizeScript: (script: ExtendedScript, options?: Partial<SummarizeOptions>) => Promise<SummarizeResult>;
|
|
7
|
+
export type { SummarizeOptions, SummarizeResult, LLMProvider, SummarizeFormat } from "../../types/summarize.js";
|
|
8
|
+
export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "../../types/summarize.js";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import { GraphAI, GraphAILogger } from "graphai";
|
|
3
|
+
import * as vanillaAgents from "@graphai/vanilla";
|
|
4
|
+
import { openAIAgent } from "@graphai/openai_agent";
|
|
5
|
+
import { anthropicAgent } from "@graphai/anthropic_agent";
|
|
6
|
+
import { groqAgent } from "@graphai/groq_agent";
|
|
7
|
+
import { geminiAgent } from "@graphai/gemini_agent";
|
|
8
|
+
import { summarizeOptionsSchema } from "../../types/summarize.js";
|
|
9
|
+
import { getProviderConfig, getProviderApiKey } from "./provider.js";
|
|
10
|
+
import { buildUserPrompt, getSystemPrompt } from "./prompts.js";
|
|
11
|
+
import { filterBySection, filterByTags } from "../filter.js";
|
|
12
|
+
dotenv.config({ quiet: true });
|
|
13
|
+
const agents = vanillaAgents.default ?? vanillaAgents;
|
|
14
|
+
/**
|
|
15
|
+
* Create GraphAI graph for summarizing script
|
|
16
|
+
*/
|
|
17
|
+
const createSummarizeGraph = (agentName) => ({
|
|
18
|
+
version: 0.5,
|
|
19
|
+
nodes: {
|
|
20
|
+
systemPrompt: {},
|
|
21
|
+
userPrompt: {},
|
|
22
|
+
model: {},
|
|
23
|
+
temperature: {},
|
|
24
|
+
maxTokens: {},
|
|
25
|
+
llmCall: {
|
|
26
|
+
agent: agentName,
|
|
27
|
+
inputs: {
|
|
28
|
+
system: ":systemPrompt",
|
|
29
|
+
prompt: ":userPrompt",
|
|
30
|
+
model: ":model",
|
|
31
|
+
temperature: ":temperature",
|
|
32
|
+
max_tokens: ":maxTokens",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
result: {
|
|
36
|
+
isResult: true,
|
|
37
|
+
agent: "copyAgent",
|
|
38
|
+
inputs: {
|
|
39
|
+
summary: ":llmCall.text",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Filter script based on options (section, tags)
|
|
46
|
+
*/
|
|
47
|
+
const filterScript = (script, options) => {
|
|
48
|
+
const afterSection = options.section ? filterBySection(script, options.section) : script;
|
|
49
|
+
const afterTags = options.tags && options.tags.length > 0 ? filterByTags(afterSection, options.tags) : afterSection;
|
|
50
|
+
return afterTags;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Main summarize function - generates a summary of the entire script
|
|
54
|
+
*/
|
|
55
|
+
export const summarizeScript = async (script, options = {}) => {
|
|
56
|
+
// Validate and apply defaults
|
|
57
|
+
const validatedOptions = summarizeOptionsSchema.parse(options);
|
|
58
|
+
const providerConfig = getProviderConfig(validatedOptions.provider);
|
|
59
|
+
const apiKey = getProviderApiKey(validatedOptions.provider);
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
throw new Error(`API key not found for provider "${validatedOptions.provider}". ` + `Please set the ${providerConfig.keyName} environment variable.`);
|
|
62
|
+
}
|
|
63
|
+
// Filter script if section/tags specified
|
|
64
|
+
const filteredScript = filterScript(script, validatedOptions);
|
|
65
|
+
const scriptTitle = script.title || "Untitled";
|
|
66
|
+
if (filteredScript.beats.length === 0) {
|
|
67
|
+
return {
|
|
68
|
+
summary: "No content to summarize.",
|
|
69
|
+
format: validatedOptions.format,
|
|
70
|
+
scriptTitle,
|
|
71
|
+
beatCount: 0,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Build GraphAI config
|
|
75
|
+
const config = {
|
|
76
|
+
openAIAgent: { apiKey: process.env.OPENAI_API_KEY },
|
|
77
|
+
anthropicAgent: { apiKey: process.env.ANTHROPIC_API_KEY },
|
|
78
|
+
groqAgent: { apiKey: process.env.GROQ_API_KEY },
|
|
79
|
+
geminiAgent: { apiKey: process.env.GEMINI_API_KEY },
|
|
80
|
+
};
|
|
81
|
+
// Create GraphAI instance
|
|
82
|
+
const graph = new GraphAI(createSummarizeGraph(providerConfig.agentName), {
|
|
83
|
+
...agents,
|
|
84
|
+
openAIAgent,
|
|
85
|
+
anthropicAgent,
|
|
86
|
+
groqAgent,
|
|
87
|
+
geminiAgent,
|
|
88
|
+
}, { config });
|
|
89
|
+
// Build prompts
|
|
90
|
+
const systemPrompt = getSystemPrompt(validatedOptions);
|
|
91
|
+
const userPrompt = buildUserPrompt(filteredScript, validatedOptions);
|
|
92
|
+
if (validatedOptions.verbose) {
|
|
93
|
+
GraphAILogger.info(`Summarizing script "${script.title}" with ${validatedOptions.provider}...`);
|
|
94
|
+
GraphAILogger.info(`Beats: ${filteredScript.beats.length}, Format: ${validatedOptions.format}`);
|
|
95
|
+
}
|
|
96
|
+
// Inject values
|
|
97
|
+
graph.injectValue("systemPrompt", systemPrompt);
|
|
98
|
+
graph.injectValue("userPrompt", userPrompt);
|
|
99
|
+
graph.injectValue("model", validatedOptions.model ?? providerConfig.defaultModel);
|
|
100
|
+
graph.injectValue("temperature", validatedOptions.temperature ?? 0.7);
|
|
101
|
+
graph.injectValue("maxTokens", validatedOptions.maxTokens ?? providerConfig.maxTokens ?? 2048);
|
|
102
|
+
// Run graph
|
|
103
|
+
const graphResult = await graph.run();
|
|
104
|
+
// Extract summary from result node
|
|
105
|
+
const resultNode = graphResult.result;
|
|
106
|
+
const summary = resultNode?.summary || "";
|
|
107
|
+
return {
|
|
108
|
+
summary,
|
|
109
|
+
format: validatedOptions.format,
|
|
110
|
+
scriptTitle,
|
|
111
|
+
beatCount: filteredScript.beats.length,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "../../types/summarize.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SummarizeOptions } from "../../types/summarize.js";
|
|
2
|
+
import type { ExtendedScript } from "../../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default system prompt for text summary
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_SYSTEM_PROMPT_TEXT = "You are creating a summary based on the content provided.\n- Extract and explain the actual information and knowledge from the content\n- Do NOT describe what the presentation/script is about (avoid phrases like \"this presentation explains...\" or \"the script describes...\")\n- Write as if you are directly explaining the topic to the reader\n- Be concise and informative\n- Output plain text only";
|
|
7
|
+
/**
|
|
8
|
+
* Default system prompt for markdown summary
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_SYSTEM_PROMPT_MARKDOWN = "You are creating a summary based on the content provided.\n- Extract and explain the actual information and knowledge from the content\n- Do NOT describe what the presentation/script is about (avoid phrases like \"this presentation explains...\" or \"the script describes...\")\n- Write as if you are directly explaining the topic to the reader\n- Use markdown formatting (headers, bullet points, etc.)\n- Include a title, key points, and conclusion\n- Output well-formatted markdown";
|
|
11
|
+
/**
|
|
12
|
+
* Build user prompt from entire script
|
|
13
|
+
*/
|
|
14
|
+
export declare const buildUserPrompt: (script: ExtendedScript, options: SummarizeOptions) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Get system prompt based on format and language
|
|
17
|
+
*/
|
|
18
|
+
export declare const getSystemPrompt: (options: SummarizeOptions) => string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default system prompt for text summary
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_SYSTEM_PROMPT_TEXT = `You are creating a summary based on the content provided.
|
|
5
|
+
- Extract and explain the actual information and knowledge from the content
|
|
6
|
+
- Do NOT describe what the presentation/script is about (avoid phrases like "this presentation explains..." or "the script describes...")
|
|
7
|
+
- Write as if you are directly explaining the topic to the reader
|
|
8
|
+
- Be concise and informative
|
|
9
|
+
- Output plain text only`;
|
|
10
|
+
/**
|
|
11
|
+
* Default system prompt for markdown summary
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_SYSTEM_PROMPT_MARKDOWN = `You are creating a summary based on the content provided.
|
|
14
|
+
- Extract and explain the actual information and knowledge from the content
|
|
15
|
+
- Do NOT describe what the presentation/script is about (avoid phrases like "this presentation explains..." or "the script describes...")
|
|
16
|
+
- Write as if you are directly explaining the topic to the reader
|
|
17
|
+
- Use markdown formatting (headers, bullet points, etc.)
|
|
18
|
+
- Include a title, key points, and conclusion
|
|
19
|
+
- Output well-formatted markdown`;
|
|
20
|
+
/**
|
|
21
|
+
* Build user prompt from entire script
|
|
22
|
+
*/
|
|
23
|
+
export const buildUserPrompt = (script, options) => {
|
|
24
|
+
const parts = [];
|
|
25
|
+
// Add script metadata
|
|
26
|
+
parts.push(`# Script: ${script.title}`);
|
|
27
|
+
parts.push(`Language: ${script.lang}`);
|
|
28
|
+
parts.push("");
|
|
29
|
+
// Collect all text from beats
|
|
30
|
+
const sections = new Map();
|
|
31
|
+
script.beats.forEach((beat, index) => {
|
|
32
|
+
const text = beat.text || "";
|
|
33
|
+
if (!text.trim())
|
|
34
|
+
return;
|
|
35
|
+
const section = beat.meta?.section || "main";
|
|
36
|
+
if (!sections.has(section)) {
|
|
37
|
+
sections.set(section, []);
|
|
38
|
+
}
|
|
39
|
+
sections.get(section).push(`[${index}] ${text}`);
|
|
40
|
+
});
|
|
41
|
+
// Output by section
|
|
42
|
+
sections.forEach((texts, section) => {
|
|
43
|
+
parts.push(`## Section: ${section}`);
|
|
44
|
+
texts.forEach((t) => parts.push(t));
|
|
45
|
+
parts.push("");
|
|
46
|
+
});
|
|
47
|
+
// Add target length if specified
|
|
48
|
+
if (options.targetLengthChars) {
|
|
49
|
+
parts.push(`Target summary length: approximately ${options.targetLengthChars} characters`);
|
|
50
|
+
}
|
|
51
|
+
parts.push("");
|
|
52
|
+
parts.push("Based on the above content, explain the topic directly to the reader:");
|
|
53
|
+
return parts.join("\n");
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Get language name from code
|
|
57
|
+
*/
|
|
58
|
+
const getLanguageName = (langCode) => {
|
|
59
|
+
const langMap = {
|
|
60
|
+
ja: "Japanese",
|
|
61
|
+
en: "English",
|
|
62
|
+
zh: "Chinese",
|
|
63
|
+
ko: "Korean",
|
|
64
|
+
fr: "French",
|
|
65
|
+
de: "German",
|
|
66
|
+
es: "Spanish",
|
|
67
|
+
it: "Italian",
|
|
68
|
+
pt: "Portuguese",
|
|
69
|
+
ru: "Russian",
|
|
70
|
+
};
|
|
71
|
+
return langMap[langCode] || langCode;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Get system prompt based on format and language
|
|
75
|
+
*/
|
|
76
|
+
export const getSystemPrompt = (options) => {
|
|
77
|
+
if (options.systemPrompt) {
|
|
78
|
+
return options.systemPrompt;
|
|
79
|
+
}
|
|
80
|
+
const basePrompt = options.format === "markdown" ? DEFAULT_SYSTEM_PROMPT_MARKDOWN : DEFAULT_SYSTEM_PROMPT_TEXT;
|
|
81
|
+
// Add language instruction if specified
|
|
82
|
+
if (options.lang) {
|
|
83
|
+
const langName = getLanguageName(options.lang);
|
|
84
|
+
return `${basePrompt}\n- IMPORTANT: Write the output in ${langName}`;
|
|
85
|
+
}
|
|
86
|
+
return basePrompt;
|
|
87
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProviderConfig, LLMProvider } from "../../types/summarize.js";
|
|
2
|
+
/**
|
|
3
|
+
* Provider to LLM agent configuration mapping
|
|
4
|
+
* Following mulmocast-cli provider2agent pattern
|
|
5
|
+
*/
|
|
6
|
+
export declare const provider2SummarizeAgent: Record<LLMProvider, ProviderConfig>;
|
|
7
|
+
/**
|
|
8
|
+
* Get provider config by provider name
|
|
9
|
+
*/
|
|
10
|
+
export declare const getProviderConfig: (provider: LLMProvider) => ProviderConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Get API key from environment for provider
|
|
13
|
+
*/
|
|
14
|
+
export declare const getProviderApiKey: (provider: LLMProvider) => string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider to LLM agent configuration mapping
|
|
3
|
+
* Following mulmocast-cli provider2agent pattern
|
|
4
|
+
*/
|
|
5
|
+
export const provider2SummarizeAgent = {
|
|
6
|
+
openai: {
|
|
7
|
+
agentName: "openAIAgent",
|
|
8
|
+
defaultModel: "gpt-4o-mini",
|
|
9
|
+
keyName: "OPENAI_API_KEY",
|
|
10
|
+
maxTokens: 8192,
|
|
11
|
+
},
|
|
12
|
+
anthropic: {
|
|
13
|
+
agentName: "anthropicAgent",
|
|
14
|
+
defaultModel: "claude-sonnet-4-20250514",
|
|
15
|
+
keyName: "ANTHROPIC_API_KEY",
|
|
16
|
+
maxTokens: 8192,
|
|
17
|
+
},
|
|
18
|
+
groq: {
|
|
19
|
+
agentName: "groqAgent",
|
|
20
|
+
defaultModel: "llama-3.1-8b-instant",
|
|
21
|
+
keyName: "GROQ_API_KEY",
|
|
22
|
+
maxTokens: 4096,
|
|
23
|
+
},
|
|
24
|
+
gemini: {
|
|
25
|
+
agentName: "geminiAgent",
|
|
26
|
+
defaultModel: "gemini-2.0-flash",
|
|
27
|
+
keyName: "GEMINI_API_KEY",
|
|
28
|
+
maxTokens: 8192,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Get provider config by provider name
|
|
33
|
+
*/
|
|
34
|
+
export const getProviderConfig = (provider) => {
|
|
35
|
+
return provider2SummarizeAgent[provider];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get API key from environment for provider
|
|
39
|
+
*/
|
|
40
|
+
export const getProviderApiKey = (provider) => {
|
|
41
|
+
const config = provider2SummarizeAgent[provider];
|
|
42
|
+
return process.env[config.keyName];
|
|
43
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -2,5 +2,8 @@ export { processScript } from "./core/process.js";
|
|
|
2
2
|
export { applyProfile } from "./core/variant.js";
|
|
3
3
|
export { filterBySection, filterByTags, stripExtendedFields } from "./core/filter.js";
|
|
4
4
|
export { listProfiles } from "./core/profiles.js";
|
|
5
|
+
export { summarizeScript } from "./core/summarize/index.js";
|
|
5
6
|
export type { BeatVariant, BeatMeta, ExtendedBeat, ExtendedScript, OutputProfile, ProcessOptions, ProfileInfo } from "./types/index.js";
|
|
7
|
+
export type { SummarizeOptions, SummarizeResult, LLMProvider, SummarizeFormat, ProviderConfig } from "./types/summarize.js";
|
|
6
8
|
export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";
|
|
9
|
+
export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "./types/summarize.js";
|
package/lib/index.js
CHANGED
|
@@ -3,5 +3,8 @@ export { processScript } from "./core/process.js";
|
|
|
3
3
|
export { applyProfile } from "./core/variant.js";
|
|
4
4
|
export { filterBySection, filterByTags, stripExtendedFields } from "./core/filter.js";
|
|
5
5
|
export { listProfiles } from "./core/profiles.js";
|
|
6
|
+
// Summarize API
|
|
7
|
+
export { summarizeScript } from "./core/summarize/index.js";
|
|
6
8
|
// Schemas (for validation)
|
|
7
9
|
export { beatVariantSchema, beatMetaSchema, extendedBeatSchema, extendedScriptSchema, outputProfileSchema } from "./types/index.js";
|
|
10
|
+
export { summarizeOptionsSchema, llmProviderSchema, summarizeFormatSchema } from "./types/summarize.js";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Provider - supported providers for summarization
|
|
4
|
+
*/
|
|
5
|
+
export declare const llmProviderSchema: z.ZodEnum<{
|
|
6
|
+
openai: "openai";
|
|
7
|
+
anthropic: "anthropic";
|
|
8
|
+
groq: "groq";
|
|
9
|
+
gemini: "gemini";
|
|
10
|
+
}>;
|
|
11
|
+
export type LLMProvider = z.infer<typeof llmProviderSchema>;
|
|
12
|
+
/**
|
|
13
|
+
* Output format for summary
|
|
14
|
+
*/
|
|
15
|
+
export declare const summarizeFormatSchema: z.ZodEnum<{
|
|
16
|
+
text: "text";
|
|
17
|
+
markdown: "markdown";
|
|
18
|
+
}>;
|
|
19
|
+
export type SummarizeFormat = z.infer<typeof summarizeFormatSchema>;
|
|
20
|
+
/**
|
|
21
|
+
* Summarize Options - configuration for summarization
|
|
22
|
+
*/
|
|
23
|
+
export declare const summarizeOptionsSchema: z.ZodObject<{
|
|
24
|
+
provider: z.ZodDefault<z.ZodEnum<{
|
|
25
|
+
openai: "openai";
|
|
26
|
+
anthropic: "anthropic";
|
|
27
|
+
groq: "groq";
|
|
28
|
+
gemini: "gemini";
|
|
29
|
+
}>>;
|
|
30
|
+
model: z.ZodOptional<z.ZodString>;
|
|
31
|
+
temperature: z.ZodOptional<z.ZodNumber>;
|
|
32
|
+
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
33
|
+
format: z.ZodDefault<z.ZodEnum<{
|
|
34
|
+
text: "text";
|
|
35
|
+
markdown: "markdown";
|
|
36
|
+
}>>;
|
|
37
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
38
|
+
targetLengthChars: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
40
|
+
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
section: z.ZodOptional<z.ZodString>;
|
|
42
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
export type SummarizeOptions = z.infer<typeof summarizeOptionsSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* Summarize Result - the generated summary
|
|
47
|
+
*/
|
|
48
|
+
export interface SummarizeResult {
|
|
49
|
+
summary: string;
|
|
50
|
+
format: SummarizeFormat;
|
|
51
|
+
scriptTitle: string;
|
|
52
|
+
beatCount: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Provider Config - configuration for LLM provider
|
|
56
|
+
*/
|
|
57
|
+
export interface ProviderConfig {
|
|
58
|
+
agentName: string;
|
|
59
|
+
defaultModel: string;
|
|
60
|
+
keyName: string;
|
|
61
|
+
maxTokens?: number;
|
|
62
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Provider - supported providers for summarization
|
|
4
|
+
*/
|
|
5
|
+
export const llmProviderSchema = z.enum(["openai", "anthropic", "groq", "gemini"]);
|
|
6
|
+
/**
|
|
7
|
+
* Output format for summary
|
|
8
|
+
*/
|
|
9
|
+
export const summarizeFormatSchema = z.enum(["text", "markdown"]);
|
|
10
|
+
/**
|
|
11
|
+
* Summarize Options - configuration for summarization
|
|
12
|
+
*/
|
|
13
|
+
export const summarizeOptionsSchema = z.object({
|
|
14
|
+
// LLM settings
|
|
15
|
+
provider: llmProviderSchema.default("openai"),
|
|
16
|
+
model: z.string().optional(),
|
|
17
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
18
|
+
maxTokens: z.number().positive().optional(),
|
|
19
|
+
// Output format
|
|
20
|
+
format: summarizeFormatSchema.default("text"),
|
|
21
|
+
// Output language (e.g., "ja", "en", "fr")
|
|
22
|
+
lang: z.string().optional(),
|
|
23
|
+
// Target length (optional)
|
|
24
|
+
targetLengthChars: z.number().positive().optional(),
|
|
25
|
+
// Custom prompt
|
|
26
|
+
systemPrompt: z.string().optional(),
|
|
27
|
+
// Processing options
|
|
28
|
+
verbose: z.boolean().default(false),
|
|
29
|
+
// Beat filtering
|
|
30
|
+
section: z.string().optional(),
|
|
31
|
+
tags: z.array(z.string()).optional(),
|
|
32
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast-preprocessor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Preprocessor for MulmoScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/receptron/mulmocast-plus#readme",
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@graphai/anthropic_agent": "^2.0.12",
|
|
38
|
+
"@graphai/gemini_agent": "^2.0.5",
|
|
39
|
+
"@graphai/groq_agent": "^2.0.2",
|
|
40
|
+
"@graphai/openai_agent": "^2.0.9",
|
|
41
|
+
"@graphai/vanilla": "^2.0.12",
|
|
42
|
+
"dotenv": "^17.2.4",
|
|
37
43
|
"graphai": "^2.0.16",
|
|
38
44
|
"mulmocast": "^2.1.35",
|
|
39
45
|
"yargs": "^18.0.0",
|