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 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
 
@@ -1,6 +1,8 @@
1
1
  interface ProcessOptions {
2
2
  profile?: string;
3
3
  output?: string;
4
+ section?: string;
5
+ tags?: string[];
4
6
  }
5
7
  /**
6
8
  * Process script with profile and output result
@@ -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()
@@ -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) => MulmoScript;
6
+ export declare const filterBySection: (script: ExtendedScript, section: string) => ExtendedScript;
7
7
  /**
8
- * Filter beats by tags (extract beats that have any of the specified tags)
8
+ * Filter beats by tags (preserves meta for chaining)
9
9
  */
10
- export declare const filterByTags: (script: ExtendedScript, tags: string[]) => MulmoScript;
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
  */
@@ -2,25 +2,29 @@ const stripBeatExtendedFields = (beat) => {
2
2
  const { variants: __variants, meta: __meta, ...baseBeat } = beat;
3
3
  return baseBeat;
4
4
  };
5
- const filterBeats = (script, predicate) => {
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) => filterBeats(script, (beat) => beat.meta?.section === section);
19
+ export const filterBySection = (script, section) => filterBeatsPreservingMeta(script, (beat) => beat.meta?.section === section);
16
20
  /**
17
- * Filter beats by tags (extract beats that have any of the specified 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 filterBeats(script, (beat) => (beat.meta?.tags ?? []).some((tag) => tagSet.has(tag)));
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) => filterBeats(script, () => true);
30
+ export const stripExtendedFields = (script) => filterBeatsToMulmoScript(script, () => true);
@@ -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 profile and filters in a single call
5
+ * Applies filters first (while meta exists), then profile
6
6
  */
7
7
  export declare const processScript: (script: ExtendedScript, options?: ProcessOptions) => MulmoScript;
@@ -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 profile and filters in a single call
5
+ * Applies filters first (while meta exists), then profile
10
6
  */
11
7
  export const processScript = (script, options = {}) => {
12
- const afterProfile = options.profile && options.profile !== "default" ? applyProfile(script, options.profile) : stripExtendedFields(script);
13
- const afterSection = options.section ? filterBySection(toExtendedScript(afterProfile), options.section) : afterProfile;
14
- const afterTags = options.tags && options.tags.length > 0 ? filterByTags(toExtendedScript(afterSection), options.tags) : afterSection;
15
- return afterTags;
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.0",
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",