@aigne/doc-smith 0.8.12-beta.7 → 0.8.12-beta.8
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/.aigne/doc-smith/config.yaml +1 -1
- package/.aigne/doc-smith/media-description.yaml +91 -0
- package/.aigne/doc-smith/upload-cache.yaml +6 -69
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +12 -0
- package/agents/clear/choose-contents.mjs +14 -1
- package/agents/clear/clear-media-description.mjs +129 -0
- package/agents/clear/index.yaml +3 -1
- package/agents/evaluate/code-snippet.mjs +28 -24
- package/agents/evaluate/document-structure.yaml +0 -4
- package/agents/evaluate/document.yaml +1 -5
- package/agents/generate/index.yaml +1 -0
- package/agents/init/index.mjs +10 -0
- package/agents/media/batch-generate-media-description.yaml +44 -0
- package/agents/media/generate-media-description.yaml +47 -0
- package/agents/media/load-media-description.mjs +238 -0
- package/agents/update/index.yaml +1 -0
- package/agents/utils/load-sources.mjs +103 -53
- package/aigne.yaml +6 -0
- package/assets/report-template/report.html +34 -34
- package/docs/configuration-initial-setup.md +74 -55
- package/docs/configuration.ja.md +59 -86
- package/docs/configuration.md +59 -86
- package/docs/configuration.zh-TW.md +59 -86
- package/docs/configuration.zh.md +59 -86
- package/docs/getting-started.ja.md +43 -24
- package/docs/getting-started.md +29 -10
- package/docs/getting-started.zh-TW.md +42 -23
- package/docs/getting-started.zh.md +39 -20
- package/docs/guides-cleaning-up.md +19 -18
- package/docs/guides-evaluating-documents.md +70 -29
- package/docs/guides-generating-documentation.md +59 -121
- package/docs/guides-interactive-chat.md +34 -26
- package/docs/guides-managing-history.md +18 -13
- package/docs/guides-publishing-your-docs.md +40 -35
- package/docs/guides-translating-documentation.md +39 -34
- package/docs/guides-updating-documentation.md +11 -9
- package/docs/overview.md +2 -2
- package/docs/release-notes.md +60 -27
- package/package.json +2 -1
- package/prompts/evaluate/document-structure.md +6 -7
- package/prompts/evaluate/document.md +16 -25
- package/prompts/media/media-description/system-prompt.md +35 -0
- package/prompts/media/media-description/user-prompt.md +8 -0
- package/utils/constants/index.mjs +0 -107
- package/utils/file-utils.mjs +41 -0
- package/media.md +0 -19
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
type: team
|
|
2
|
+
name: batchGenerateMediaDescription
|
|
3
|
+
description: Batch generate media (image/video) descriptions with concurrency
|
|
4
|
+
skills:
|
|
5
|
+
- url: ./generate-media-description.yaml
|
|
6
|
+
task_render_mode: collapse
|
|
7
|
+
task_title: Generate Media Description
|
|
8
|
+
input_schema:
|
|
9
|
+
type: object
|
|
10
|
+
properties:
|
|
11
|
+
mediaToDescribe:
|
|
12
|
+
type: array
|
|
13
|
+
items:
|
|
14
|
+
type: object
|
|
15
|
+
properties:
|
|
16
|
+
name:
|
|
17
|
+
type: string
|
|
18
|
+
width:
|
|
19
|
+
type: number
|
|
20
|
+
height:
|
|
21
|
+
type: number
|
|
22
|
+
hash:
|
|
23
|
+
type: string
|
|
24
|
+
path:
|
|
25
|
+
type: string
|
|
26
|
+
type:
|
|
27
|
+
type: string
|
|
28
|
+
mediaFile:
|
|
29
|
+
type: array
|
|
30
|
+
items:
|
|
31
|
+
type: object
|
|
32
|
+
properties:
|
|
33
|
+
type:
|
|
34
|
+
type: string
|
|
35
|
+
path:
|
|
36
|
+
type: string
|
|
37
|
+
filename:
|
|
38
|
+
type: string
|
|
39
|
+
mimeType:
|
|
40
|
+
type: string
|
|
41
|
+
description: Array of media files (images/videos) that need descriptions
|
|
42
|
+
iterate_on: mediaToDescribe
|
|
43
|
+
concurrency: 5
|
|
44
|
+
mode: sequential
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: generateMediaDescription
|
|
2
|
+
description: Generate description for a single media file (image/video)
|
|
3
|
+
model: "google/gemini-2.5-flash"
|
|
4
|
+
modalities: ["text", "image"]
|
|
5
|
+
instructions:
|
|
6
|
+
- role: system
|
|
7
|
+
url: ../../prompts/media/media-description/system-prompt.md
|
|
8
|
+
- role: user
|
|
9
|
+
url: ../../prompts/media/media-description/user-prompt.md
|
|
10
|
+
input_file_key: mediaFile
|
|
11
|
+
include_input_in_output: true
|
|
12
|
+
input_schema:
|
|
13
|
+
type: object
|
|
14
|
+
properties:
|
|
15
|
+
name:
|
|
16
|
+
type: string
|
|
17
|
+
description: Media file name
|
|
18
|
+
width:
|
|
19
|
+
type: number
|
|
20
|
+
description: Media width in pixels
|
|
21
|
+
height:
|
|
22
|
+
type: number
|
|
23
|
+
description: Media height in pixels
|
|
24
|
+
hash:
|
|
25
|
+
type: string
|
|
26
|
+
path:
|
|
27
|
+
type: string
|
|
28
|
+
description: Media path
|
|
29
|
+
type:
|
|
30
|
+
type: string
|
|
31
|
+
description: Media type (image/video)
|
|
32
|
+
mediaFile:
|
|
33
|
+
type: array
|
|
34
|
+
items:
|
|
35
|
+
type: object
|
|
36
|
+
properties:
|
|
37
|
+
type:
|
|
38
|
+
type: string
|
|
39
|
+
path:
|
|
40
|
+
type: string
|
|
41
|
+
filename:
|
|
42
|
+
type: string
|
|
43
|
+
mimeType:
|
|
44
|
+
type: string
|
|
45
|
+
required:
|
|
46
|
+
- name
|
|
47
|
+
output_key: description
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { parse, stringify } from "yaml";
|
|
6
|
+
import { getMediaDescriptionCachePath } from "../../utils/file-utils.mjs";
|
|
7
|
+
|
|
8
|
+
const SIZE_THRESHOLD = 10 * 1024 * 1024; // 10MB
|
|
9
|
+
|
|
10
|
+
// Supported MIME types for Gemini AI
|
|
11
|
+
const SUPPORTED_IMAGE_TYPES = new Set([
|
|
12
|
+
"image/png",
|
|
13
|
+
"image/jpeg",
|
|
14
|
+
"image/webp",
|
|
15
|
+
"image/heic",
|
|
16
|
+
"image/heif",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const SUPPORTED_VIDEO_TYPES = new Set([
|
|
20
|
+
"video/mp4",
|
|
21
|
+
"video/mpeg",
|
|
22
|
+
"video/mov",
|
|
23
|
+
"video/avi",
|
|
24
|
+
"video/x-flv",
|
|
25
|
+
"video/mpg",
|
|
26
|
+
"video/webm",
|
|
27
|
+
"video/wmv",
|
|
28
|
+
"video/3gpp",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Calculate hash for a media file
|
|
33
|
+
* For files < 10MB: use file content
|
|
34
|
+
* For files >= 10MB: use path + size + mtime to avoid memory issues
|
|
35
|
+
* @param {string} absolutePath - The absolute path to the media file
|
|
36
|
+
* @returns {Promise<string>} - The hash of the file
|
|
37
|
+
*/
|
|
38
|
+
async function calculateMediaHash(absolutePath) {
|
|
39
|
+
const stats = await stat(absolutePath);
|
|
40
|
+
|
|
41
|
+
if (stats.size < SIZE_THRESHOLD) {
|
|
42
|
+
// Small file: use full content
|
|
43
|
+
const content = await readFile(absolutePath);
|
|
44
|
+
return createHash("sha256").update(content).digest("hex");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Large file: use path + size + mtime
|
|
48
|
+
const hashInput = `${absolutePath}:${stats.size}:${stats.mtimeMs}`;
|
|
49
|
+
return createHash("sha256").update(hashInput).digest("hex");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Load media descriptions from cache and generate new ones if needed
|
|
54
|
+
* @param {Object} input - Input parameters
|
|
55
|
+
* @param {Array} input.mediaFiles - Array of media file objects from load-sources
|
|
56
|
+
* @param {string} input.docsDir - Base directory for documentation
|
|
57
|
+
* @param {Object} options - Agent options
|
|
58
|
+
* @returns {Promise<Object>} - Updated assetsContent with media descriptions
|
|
59
|
+
*/
|
|
60
|
+
export default async function loadMediaDescription(input, options) {
|
|
61
|
+
const { mediaFiles = [], docsDir } = input;
|
|
62
|
+
|
|
63
|
+
// Filter to get image and video files with supported MIME types
|
|
64
|
+
const mediaFilesToProcess = mediaFiles.filter((file) => {
|
|
65
|
+
if (file.type === "image") {
|
|
66
|
+
return SUPPORTED_IMAGE_TYPES.has(file.mimeType);
|
|
67
|
+
}
|
|
68
|
+
if (file.type === "video") {
|
|
69
|
+
return SUPPORTED_VIDEO_TYPES.has(file.mimeType);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Path to media description cache file
|
|
75
|
+
const cacheFilePath = getMediaDescriptionCachePath();
|
|
76
|
+
|
|
77
|
+
// Load existing cache
|
|
78
|
+
let cache = {};
|
|
79
|
+
if (existsSync(cacheFilePath)) {
|
|
80
|
+
try {
|
|
81
|
+
const cacheContent = await readFile(cacheFilePath, "utf8");
|
|
82
|
+
const parsedCache = parse(cacheContent);
|
|
83
|
+
cache = parsedCache?.descriptions || {};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn("Failed to read media description cache:", error.message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Find media files without descriptions
|
|
90
|
+
const mediaToDescribe = [];
|
|
91
|
+
const mediaHashMap = new Map();
|
|
92
|
+
|
|
93
|
+
const absoluteDocsDir = path.resolve(process.cwd(), docsDir);
|
|
94
|
+
|
|
95
|
+
// Only process media files that need AI description
|
|
96
|
+
for (const mediaFile of mediaFilesToProcess) {
|
|
97
|
+
// Convert relative path to absolute path for consistent hashing
|
|
98
|
+
// mediaFiles.path is relative to docsDir
|
|
99
|
+
const absolutePath = path.join(absoluteDocsDir, mediaFile.path);
|
|
100
|
+
const mediaHash = await calculateMediaHash(absolutePath);
|
|
101
|
+
mediaHashMap.set(mediaFile.path, mediaHash);
|
|
102
|
+
|
|
103
|
+
if (!cache[mediaHash]) {
|
|
104
|
+
mediaToDescribe.push({
|
|
105
|
+
...mediaFile,
|
|
106
|
+
hash: mediaHash,
|
|
107
|
+
path: mediaFile.path,
|
|
108
|
+
mediaFile: [
|
|
109
|
+
{
|
|
110
|
+
type: "local",
|
|
111
|
+
path: absolutePath,
|
|
112
|
+
filename: mediaFile.name,
|
|
113
|
+
mimeType: mediaFile.mimeType,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Generate descriptions for media files without cache - use team agent for concurrent processing
|
|
121
|
+
const newDescriptions = {};
|
|
122
|
+
if (mediaToDescribe.length > 0) {
|
|
123
|
+
try {
|
|
124
|
+
// Use batch team agent for concurrent processing
|
|
125
|
+
const results = await options.context.invoke(
|
|
126
|
+
options.context.agents["batchGenerateMediaDescription"],
|
|
127
|
+
{
|
|
128
|
+
mediaToDescribe,
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Process results - results is an array of individual results
|
|
133
|
+
if (Array.isArray(results?.mediaToDescribe)) {
|
|
134
|
+
for (const result of results.mediaToDescribe) {
|
|
135
|
+
if (result?.hash && result?.description) {
|
|
136
|
+
newDescriptions[result.hash] = {
|
|
137
|
+
path: result.path,
|
|
138
|
+
description: result.description,
|
|
139
|
+
generatedAt: new Date().toISOString(),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Merge new descriptions into cache
|
|
146
|
+
Object.assign(cache, newDescriptions);
|
|
147
|
+
|
|
148
|
+
// Save updated cache
|
|
149
|
+
await mkdir(path.dirname(cacheFilePath), { recursive: true });
|
|
150
|
+
const cacheYaml = stringify({
|
|
151
|
+
descriptions: cache,
|
|
152
|
+
lastUpdated: new Date().toISOString(),
|
|
153
|
+
});
|
|
154
|
+
await writeFile(cacheFilePath, cacheYaml, "utf8");
|
|
155
|
+
|
|
156
|
+
console.log(`Generated descriptions for ${Object.keys(newDescriptions).length} media files`);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Failed to generate media descriptions:", error.message);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Build enhanced assetsContent with descriptions
|
|
163
|
+
let enhancedAssetsContent = "# Available Media Assets for Documentation\n\n";
|
|
164
|
+
|
|
165
|
+
if (mediaFiles.length > 0) {
|
|
166
|
+
enhancedAssetsContent += "```yaml\n";
|
|
167
|
+
enhancedAssetsContent += "assets:\n";
|
|
168
|
+
|
|
169
|
+
for (const asset of mediaFiles) {
|
|
170
|
+
enhancedAssetsContent += ` - name: "${asset.name}"\n`;
|
|
171
|
+
enhancedAssetsContent += ` path: "${asset.path}"\n`;
|
|
172
|
+
enhancedAssetsContent += ` type: "${asset.type}"\n`;
|
|
173
|
+
|
|
174
|
+
// Add description for images and videos
|
|
175
|
+
if (asset.type === "image" || asset.type === "video") {
|
|
176
|
+
const mediaHash = mediaHashMap.get(asset.path);
|
|
177
|
+
const cachedDesc = cache[mediaHash];
|
|
178
|
+
if (cachedDesc?.description) {
|
|
179
|
+
enhancedAssetsContent += ` description: "${cachedDesc.description}"\n`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Add dimensions for images and videos
|
|
184
|
+
if (asset.width && asset.height) {
|
|
185
|
+
enhancedAssetsContent += ` width: ${asset.width}\n`;
|
|
186
|
+
enhancedAssetsContent += ` height: ${asset.height}\n`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
enhancedAssetsContent += "```\n";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
...input,
|
|
195
|
+
assetsContent: enhancedAssetsContent,
|
|
196
|
+
mediaFiles,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
loadMediaDescription.input_schema = {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
mediaFiles: {
|
|
204
|
+
type: "array",
|
|
205
|
+
items: {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {
|
|
208
|
+
name: { type: "string" },
|
|
209
|
+
path: { type: "string" },
|
|
210
|
+
type: { type: "string" },
|
|
211
|
+
width: { type: "number" },
|
|
212
|
+
height: { type: "number" },
|
|
213
|
+
mimeType: { type: "string" },
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
description: "Array of media file objects (images/videos)",
|
|
217
|
+
},
|
|
218
|
+
docsDir: {
|
|
219
|
+
type: "string",
|
|
220
|
+
description: "Base directory for documentation",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
required: ["mediaFiles", "docsDir"],
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
loadMediaDescription.output_schema = {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: {
|
|
229
|
+
assetsContent: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: "Enhanced assets content with media descriptions",
|
|
232
|
+
},
|
|
233
|
+
mediaFiles: {
|
|
234
|
+
type: "array",
|
|
235
|
+
description: "Array of media file objects",
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
};
|
package/agents/update/index.yaml
CHANGED
|
@@ -30,6 +30,7 @@ skills:
|
|
|
30
30
|
default_input:
|
|
31
31
|
requiredFeedback: false
|
|
32
32
|
- ../utils/format-document-structure.mjs
|
|
33
|
+
- ../media/load-media-description.mjs
|
|
33
34
|
- ../update/check-update-is-single.mjs
|
|
34
35
|
- ../update/save-and-translate-document.mjs
|
|
35
36
|
- url: ../utils/action-success.mjs
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import imageSize from "image-size";
|
|
3
4
|
import {
|
|
4
5
|
buildSourcesContent,
|
|
5
6
|
calculateFileStats,
|
|
6
7
|
loadFilesFromPaths,
|
|
7
8
|
readFileContents,
|
|
9
|
+
getMimeType,
|
|
8
10
|
} from "../../utils/file-utils.mjs";
|
|
9
11
|
import {
|
|
10
12
|
getCurrentGitHead,
|
|
@@ -29,8 +31,10 @@ export default async function loadSources({
|
|
|
29
31
|
useDefaultPatterns = true,
|
|
30
32
|
lastGitHead,
|
|
31
33
|
reset = false,
|
|
34
|
+
media,
|
|
32
35
|
} = {}) {
|
|
33
36
|
let files = Array.isArray(sources) ? [...sources] : [];
|
|
37
|
+
const { minImageWidth } = media || { minImageWidth: 800 };
|
|
34
38
|
|
|
35
39
|
if (sourcesPath) {
|
|
36
40
|
const allFiles = await loadFilesFromPaths(sourcesPath, {
|
|
@@ -58,36 +62,102 @@ export default async function loadSources({
|
|
|
58
62
|
".bmp",
|
|
59
63
|
".webp",
|
|
60
64
|
".svg",
|
|
65
|
+
".heic",
|
|
66
|
+
".heif",
|
|
61
67
|
".mp4",
|
|
68
|
+
".mpeg",
|
|
69
|
+
".mpg",
|
|
62
70
|
".mov",
|
|
63
71
|
".avi",
|
|
72
|
+
".flv",
|
|
64
73
|
".mkv",
|
|
65
74
|
".webm",
|
|
75
|
+
".wmv",
|
|
66
76
|
".m4v",
|
|
77
|
+
".3gpp",
|
|
67
78
|
];
|
|
68
79
|
|
|
69
80
|
// Separate source files from media files
|
|
70
81
|
const sourceFilesPaths = [];
|
|
71
82
|
const mediaFiles = [];
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
// Helper function to determine file type from extension
|
|
85
|
+
const getFileType = (filePath) => {
|
|
86
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
87
|
+
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".heic", ".heif"];
|
|
88
|
+
const videoExts = [
|
|
89
|
+
".mp4",
|
|
90
|
+
".mpeg",
|
|
91
|
+
".mpg",
|
|
92
|
+
".mov",
|
|
93
|
+
".avi",
|
|
94
|
+
".flv",
|
|
95
|
+
".mkv",
|
|
96
|
+
".webm",
|
|
97
|
+
".wmv",
|
|
98
|
+
".m4v",
|
|
99
|
+
".3gpp",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
if (imageExts.includes(ext)) return "image";
|
|
103
|
+
if (videoExts.includes(ext)) return "video";
|
|
104
|
+
return "media";
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
let filteredImageCount = 0;
|
|
108
|
+
|
|
109
|
+
await Promise.all(
|
|
110
|
+
files.map(async (file) => {
|
|
111
|
+
const ext = path.extname(file).toLowerCase();
|
|
112
|
+
|
|
113
|
+
if (mediaExtensions.includes(ext)) {
|
|
114
|
+
// This is a media file
|
|
115
|
+
const relativePath = path.relative(docsDir, file);
|
|
116
|
+
const fileName = path.basename(file);
|
|
117
|
+
const description = path.parse(fileName).name;
|
|
118
|
+
|
|
119
|
+
const mediaItem = {
|
|
120
|
+
name: fileName,
|
|
121
|
+
path: relativePath,
|
|
122
|
+
type: getFileType(relativePath),
|
|
123
|
+
description,
|
|
124
|
+
mimeType: getMimeType(file),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// For image files, get dimensions and filter by width
|
|
128
|
+
if (mediaItem.type === "image") {
|
|
129
|
+
try {
|
|
130
|
+
const buffer = await readFile(file);
|
|
131
|
+
const dimensions = imageSize(buffer);
|
|
132
|
+
mediaItem.width = dimensions.width;
|
|
133
|
+
mediaItem.height = dimensions.height;
|
|
134
|
+
|
|
135
|
+
// Filter out images with width less than minImageWidth
|
|
136
|
+
if (dimensions.width < minImageWidth) {
|
|
137
|
+
filteredImageCount++;
|
|
138
|
+
console.log(
|
|
139
|
+
`Filtered image: ${fileName} (${dimensions.width}x${dimensions.height}px < ${minImageWidth}px minimum)`,
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.warn(`⚠️ Failed to get dimensions for ${fileName}: ${err.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
mediaFiles.push(mediaItem);
|
|
149
|
+
} else {
|
|
150
|
+
// This is a source file
|
|
151
|
+
sourceFilesPaths.push(file);
|
|
152
|
+
}
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Log summary of filtered images
|
|
157
|
+
if (filteredImageCount > 0) {
|
|
158
|
+
console.log(
|
|
159
|
+
`\nTotal ${filteredImageCount} low-resolution image(s) filtered for better documentation quality (minimum width: ${minImageWidth}px)\n`,
|
|
160
|
+
);
|
|
91
161
|
}
|
|
92
162
|
|
|
93
163
|
// Read all source files using the utility function
|
|
@@ -179,37 +249,6 @@ export default async function loadSources({
|
|
|
179
249
|
}
|
|
180
250
|
}
|
|
181
251
|
|
|
182
|
-
// Generate assets content from media files
|
|
183
|
-
let assetsContent = "# Available Media Assets for Documentation\n\n";
|
|
184
|
-
|
|
185
|
-
if (mediaFiles.length > 0) {
|
|
186
|
-
// Helper function to determine file type from extension
|
|
187
|
-
const getFileType = (filePath) => {
|
|
188
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
189
|
-
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"];
|
|
190
|
-
const videoExts = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
|
|
191
|
-
|
|
192
|
-
if (imageExts.includes(ext)) return "image";
|
|
193
|
-
if (videoExts.includes(ext)) return "video";
|
|
194
|
-
return "media";
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const mediaYaml = mediaFiles.map((file) => ({
|
|
198
|
-
name: file.name,
|
|
199
|
-
path: file.path,
|
|
200
|
-
type: getFileType(file.path),
|
|
201
|
-
}));
|
|
202
|
-
|
|
203
|
-
assetsContent += "```yaml\n";
|
|
204
|
-
assetsContent += "assets:\n";
|
|
205
|
-
mediaYaml.forEach((asset) => {
|
|
206
|
-
assetsContent += ` - name: "${asset.name}"\n`;
|
|
207
|
-
assetsContent += ` path: "${asset.path}"\n`;
|
|
208
|
-
assetsContent += ` type: "${asset.type}"\n`;
|
|
209
|
-
});
|
|
210
|
-
assetsContent += "```\n";
|
|
211
|
-
}
|
|
212
|
-
|
|
213
252
|
return {
|
|
214
253
|
datasources: allSources,
|
|
215
254
|
content,
|
|
@@ -218,7 +257,7 @@ export default async function loadSources({
|
|
|
218
257
|
modifiedFiles,
|
|
219
258
|
totalTokens,
|
|
220
259
|
totalLines,
|
|
221
|
-
|
|
260
|
+
mediaFiles,
|
|
222
261
|
isLargeContext,
|
|
223
262
|
allFilesPaths,
|
|
224
263
|
};
|
|
@@ -280,9 +319,20 @@ loadSources.output_schema = {
|
|
|
280
319
|
items: { type: "string" },
|
|
281
320
|
description: "Array of modified files since last generation",
|
|
282
321
|
},
|
|
283
|
-
|
|
284
|
-
type: "
|
|
285
|
-
|
|
322
|
+
mediaFiles: {
|
|
323
|
+
type: "array",
|
|
324
|
+
items: {
|
|
325
|
+
type: "object",
|
|
326
|
+
properties: {
|
|
327
|
+
name: { type: "string" },
|
|
328
|
+
path: { type: "string" },
|
|
329
|
+
type: { type: "string" },
|
|
330
|
+
width: { type: "number" },
|
|
331
|
+
height: { type: "number" },
|
|
332
|
+
mimeType: { type: "string" },
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
description: "Array of media file objects (images/videos)",
|
|
286
336
|
},
|
|
287
337
|
},
|
|
288
338
|
};
|
package/aigne.yaml
CHANGED
|
@@ -49,6 +49,11 @@ agents:
|
|
|
49
49
|
- ./agents/publish/publish-docs.mjs
|
|
50
50
|
- ./agents/publish/index.yaml
|
|
51
51
|
|
|
52
|
+
# Media
|
|
53
|
+
- ./agents/media/load-media-description.mjs
|
|
54
|
+
- ./agents/media/batch-generate-media-description.yaml
|
|
55
|
+
- ./agents/media/generate-media-description.yaml
|
|
56
|
+
|
|
52
57
|
# Clear/Cleanup
|
|
53
58
|
- ./agents/clear/choose-contents.mjs
|
|
54
59
|
- ./agents/clear/clear-document-structure.mjs
|
|
@@ -56,6 +61,7 @@ agents:
|
|
|
56
61
|
- ./agents/clear/clear-document-config.mjs
|
|
57
62
|
- ./agents/clear/clear-auth-tokens.mjs
|
|
58
63
|
- ./agents/clear/clear-deployment-config.mjs
|
|
64
|
+
- ./agents/clear/clear-media-description.mjs
|
|
59
65
|
|
|
60
66
|
# Utilities
|
|
61
67
|
- ./agents/utils/load-sources.mjs
|