tuna-agent 0.1.126 → 0.1.128
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.
|
@@ -105,6 +105,30 @@ Voiceover at this moment: "${voiceoverText || '(none)'}"` },
|
|
|
105
105
|
const data = await res.json();
|
|
106
106
|
return data.choices?.[0]?.message?.content?.trim() || '';
|
|
107
107
|
}
|
|
108
|
+
async function visionExtractStyle(frames) {
|
|
109
|
+
if (!OPENAI_KEY || frames.length === 0)
|
|
110
|
+
return '';
|
|
111
|
+
try {
|
|
112
|
+
const content = [
|
|
113
|
+
{ type: 'text', text: 'Analyze these frames from a video and extract a concise visual style description (1-2 sentences). Focus on: animation style (cartoon, realistic, anime, etc.), color palette, lighting, character design approach (anthropomorphized objects, real people, etc.), and overall aesthetic.\n\nReturn ONLY the style description, nothing else.' },
|
|
114
|
+
];
|
|
115
|
+
for (const b64 of frames) {
|
|
116
|
+
content.push({ type: 'image_url', image_url: { url: `data:image/jpeg;base64,${b64}` } });
|
|
117
|
+
}
|
|
118
|
+
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${OPENAI_KEY}` },
|
|
121
|
+
body: JSON.stringify({ model: 'gpt-4o-mini', max_tokens: 200, messages: [{ role: 'user', content }] }),
|
|
122
|
+
});
|
|
123
|
+
if (!res.ok)
|
|
124
|
+
return '';
|
|
125
|
+
const data = await res.json();
|
|
126
|
+
return (data.choices?.[0]?.message?.content || '').trim().replace(/\*\*/g, '');
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return '';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
108
132
|
export async function analyzeVideo(url, onProgress) {
|
|
109
133
|
const progress = onProgress || (() => { });
|
|
110
134
|
const tmpDir = path.join(os.tmpdir(), 'tuna-analyze-' + crypto.randomBytes(6).toString('hex'));
|
|
@@ -218,10 +242,16 @@ export async function analyzeVideo(url, onProgress) {
|
|
|
218
242
|
sceneResults.push(...results.filter((r) => r !== null));
|
|
219
243
|
}
|
|
220
244
|
const scenes = sceneResults.sort((a, b) => a.scene_number - b.scene_number);
|
|
245
|
+
// Extract video style from 3 sample frames
|
|
246
|
+
progress('Đang phân tích video style...');
|
|
247
|
+
const styleSamples = frameBuffers.slice(0, 3).map(f => f.buf.toString('base64'));
|
|
248
|
+
const video_style = await visionExtractStyle(styleSamples);
|
|
249
|
+
console.log('[analyze_video] Video style:', video_style.substring(0, 100));
|
|
221
250
|
return {
|
|
222
251
|
duration_sec: Math.round(durationSec),
|
|
223
252
|
language: transcript.language || 'unknown',
|
|
224
253
|
transcript: transcript.text || '',
|
|
254
|
+
video_style,
|
|
225
255
|
segments: segments.map((s) => ({ start: s.start, end: s.end, text: s.text })),
|
|
226
256
|
scenes,
|
|
227
257
|
};
|
package/dist/utils/claude-cli.js
CHANGED
|
@@ -226,10 +226,31 @@ export function runClaude(options) {
|
|
|
226
226
|
continue;
|
|
227
227
|
try {
|
|
228
228
|
const parsed = JSON.parse(line);
|
|
229
|
-
if (
|
|
230
|
-
firstStreamEventTime = Date.now();
|
|
229
|
+
if (parsed.type === 'stream_event') {
|
|
231
230
|
const evt = parsed.event;
|
|
232
|
-
|
|
231
|
+
if (!firstStreamEventTime) {
|
|
232
|
+
firstStreamEventTime = Date.now();
|
|
233
|
+
console.log(`[claude-cli] ⏱ first stream_event (${evt?.type}): ${firstStreamEventTime - spawnTime}ms after spawn`);
|
|
234
|
+
}
|
|
235
|
+
// Log phase transitions
|
|
236
|
+
if (evt?.type === 'content_block_start') {
|
|
237
|
+
const contentBlock = evt.content_block;
|
|
238
|
+
console.log(`[claude-cli] ⏱ content_block_start (${contentBlock?.type}): ${Date.now() - spawnTime}ms after spawn`);
|
|
239
|
+
}
|
|
240
|
+
if (evt?.type === 'content_block_delta') {
|
|
241
|
+
const delta = evt.delta;
|
|
242
|
+
if (delta?.type === 'thinking_delta' && !proc._loggedThinking) {
|
|
243
|
+
proc._loggedThinking = true;
|
|
244
|
+
console.log(`[claude-cli] ⏱ thinking started: ${Date.now() - spawnTime}ms after spawn`);
|
|
245
|
+
}
|
|
246
|
+
if (delta?.type === 'text_delta' && !proc._loggedTextStart) {
|
|
247
|
+
proc._loggedTextStart = true;
|
|
248
|
+
console.log(`[claude-cli] ⏱ text output started: ${Date.now() - spawnTime}ms after spawn`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (parsed.type === 'result') {
|
|
253
|
+
console.log(`[claude-cli] ⏱ result received: ${Date.now() - spawnTime}ms after spawn`);
|
|
233
254
|
}
|
|
234
255
|
options.onStreamLine(parsed);
|
|
235
256
|
}
|