fix tạm lỗi cài đặt AI prompt
This commit is contained in:
+1
-189
@@ -6872,192 +6872,6 @@ const App = () => {
|
||||
}, 800);
|
||||
};
|
||||
|
||||
// ── AI Copilot: Natural Language Prompt → Function Calls (28_AI_PANEL.md §2 & §3) ──
|
||||
const AIComparator = () => {
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [provider, setProvider] = useState('OpenAI');
|
||||
const [model, setModel] = useState('GPT-4o');
|
||||
const [response, setResponse] = useState('');
|
||||
const [history = [], setHistory] = useState([]);
|
||||
|
||||
const handleSendPrompt = async () => {
|
||||
setResponse('');
|
||||
try {
|
||||
const context = window.AIGateway.buildAIPromptContext({
|
||||
bpm: parseInt(bpm) || 120,
|
||||
selectedTrackId: selectedTrackId,
|
||||
currentTime: currentTime,
|
||||
selLeft: selLeft,
|
||||
selRight: selRight,
|
||||
tracks: tracks
|
||||
});
|
||||
|
||||
const result = await window.AIGateway.executePrompt({
|
||||
prompt: prompt,
|
||||
provider: provider,
|
||||
model: model,
|
||||
apiKey: aiConfig.apiKey,
|
||||
baseUrl: aiConfig.baseUrl
|
||||
});
|
||||
|
||||
setResponse(result.textResponse);
|
||||
|
||||
result.functionCalls.forEach(call => {
|
||||
DAWCommandDispatcher.execute(call.name, call.arguments);
|
||||
setHistory([...history, { name: call.name, args: call.arguments }]);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
setResponse(`Lỗi: ${e.message}`);
|
||||
}
|
||||
};
|
||||
const prompt = aiPrompt.trim();
|
||||
if (!prompt || aiProcessing) return;
|
||||
setAiProcessing(true);
|
||||
setAiActionLog(prev => [...prev, { type: 'status', text: 'AI đang phân tích lệnh...', time: Date.now() }]);
|
||||
const dawContext = window.AIGateway.buildAIPromptContext({
|
||||
bpm: parseInt(bpm) || 120,
|
||||
selectedTrackId,
|
||||
currentTime,
|
||||
selLeft,
|
||||
selRight,
|
||||
tracks
|
||||
});
|
||||
try {
|
||||
const result = await window.AIGateway.executePrompt({
|
||||
prompt,
|
||||
provider: aiProvider.toLowerCase(),
|
||||
model: aiModel,
|
||||
apiKey: aiConfig.apiKey,
|
||||
baseUrl: aiConfig.baseUrl,
|
||||
dawContext
|
||||
});
|
||||
const calls = result.functionCalls || [];
|
||||
if (calls.length === 0 && result.textResponse) {
|
||||
setAiActionLog(prev => [...prev, { type: 'text', text: result.textResponse, time: Date.now() }]);
|
||||
}
|
||||
for (const call of calls) {
|
||||
const logEntry = { type: 'action', text: `[${call.name}] Đang thực thi...`, time: Date.now(), callName: call.name, args: call.arguments };
|
||||
setAiActionLog(prev => [...prev, logEntry]);
|
||||
let execResult = { success: false, error: 'Unknown command' };
|
||||
if (window.DAWCommandDispatcher && window.DAWCommandDispatcher.execute) {
|
||||
execResult = window.DAWCommandDispatcher.execute(call.name, call.arguments);
|
||||
} else {
|
||||
const api = {
|
||||
createTrack: (args) => {
|
||||
const name = args.name || `AI_Track_${Date.now()}`;
|
||||
const newId = addNewTrack();
|
||||
if (name) updateTrackName(newId, name);
|
||||
return { success: true, trackId: newId };
|
||||
},
|
||||
deleteTrack: (args) => {
|
||||
const tid = args.track_id || selectedTrackId;
|
||||
if (tid) deleteTrack(tid);
|
||||
return { success: true };
|
||||
},
|
||||
processAudioDsp: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
const action = args.action;
|
||||
const params = args.params || {};
|
||||
const track = tracks.find(t => t.id === trackId);
|
||||
if (!track || !track.buffer) return { success: false, error: 'No buffer' };
|
||||
if (action === 'normalize') {
|
||||
const d = track.buffer.getChannelData(0);
|
||||
let mx = 0; for (let i = 0; i < d.length; i++) mx = Math.max(mx, Math.abs(d[i]));
|
||||
if (mx > 0) { const g = 1.0 / mx; for (let i = 0; i < d.length; i++) d[i] *= g; }
|
||||
return { success: true };
|
||||
} else if (action === 'invert_phase') {
|
||||
const d = track.buffer.getChannelData(0);
|
||||
for (let i = 0; i < d.length; i++) d[i] *= -1;
|
||||
return { success: true };
|
||||
} else if (action === 'gain') {
|
||||
const g = Math.pow(10, (params.gain_db ?? 0) / 20);
|
||||
const d = track.buffer.getChannelData(0);
|
||||
for (let i = 0; i < d.length; i++) d[i] = Math.max(-1, Math.min(1, d[i] * g));
|
||||
return { success: true };
|
||||
} else if (action === 'pitch_shift') {
|
||||
const semi = params.semitones ?? 0;
|
||||
const ratio = Math.pow(2, semi / 12);
|
||||
const resample = (data, r) => {
|
||||
const nl = Math.round(data.length * r);
|
||||
const out = new Float32Array(nl);
|
||||
for (let i = 0; i < nl; i++) { const si = i / r; const i0 = Math.floor(si); const i1 = Math.min(i0 + 1, data.length - 1); const f = si - i0; out[i] = data[i0] * (1 - f) + data[i1] * f; }
|
||||
return out;
|
||||
};
|
||||
const d = track.buffer.getChannelData(0);
|
||||
const nd = resample(d, 1 / ratio);
|
||||
const ctx = getAudioContext();
|
||||
const nb = ctx.createBuffer(1, nd.length, track.buffer.sampleRate);
|
||||
nb.copyToChannel(nd, 0);
|
||||
setTracks(p => p.map(t => t.id === trackId ? { ...t, buffer: nb } : t));
|
||||
return { success: true };
|
||||
}
|
||||
return { success: false, error: `Unknown action: ${action}` };
|
||||
},
|
||||
add_midi_item: (args) => {
|
||||
return { success: true, note: 'MIDI items mapped to silent audio clip' };
|
||||
},
|
||||
modify_midi_notes: (args) => {
|
||||
return { success: true, note: 'MIDI notes mapped to synth tones' };
|
||||
},
|
||||
setTrackVolume: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
updateTrackVolumeDb(trackId, parseFloat(args.volume_db ?? args.volume ?? 0));
|
||||
return { success: true };
|
||||
},
|
||||
setTrackPan: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
updateTrackPan(trackId, parseInt(args.pan ?? 0));
|
||||
return { success: true };
|
||||
},
|
||||
toggleMute: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
toggleTrackMute(trackId);
|
||||
return { success: true };
|
||||
},
|
||||
toggleSolo: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
toggleTrackSoloEvaluate(trackId);
|
||||
return { success: true };
|
||||
},
|
||||
setBpm: (args) => {
|
||||
const bpmVal = args.bpm || args.tempo || 120;
|
||||
setBpm(String(bpmVal));
|
||||
return { success: true };
|
||||
},
|
||||
setPlayhead: (args) => {
|
||||
handlePlayheadSet(args.time ?? args.position ?? 0);
|
||||
return { success: true };
|
||||
},
|
||||
addMarker: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
const time = args.time ?? currentTime;
|
||||
setTracks(prev => prev.map(t => {
|
||||
if (t.id !== trackId) return t;
|
||||
return { ...t, markers: [...(t.markers || []), { id: 'ai_marker_' + Date.now(), time, label: args.label || 'AI Marker' }] };
|
||||
}));
|
||||
return { success: true };
|
||||
}
|
||||
};
|
||||
if (api[call.name]) {
|
||||
execResult = api[call.name](call.arguments);
|
||||
}
|
||||
}
|
||||
setAiActionLog(prev => prev.map(entry => {
|
||||
if (entry === logEntry) {
|
||||
return { ...entry, text: `[${call.name}] ${execResult.success ? 'Thành công' : 'Thất bại: ' + (execResult.error || 'unknown')}`, result: execResult };
|
||||
}
|
||||
return entry;
|
||||
}));
|
||||
}
|
||||
setAiActionLog(prev => [...prev, { type: 'status', text: `Hoàn thành. Đã xử lý ${calls.length} lệnh.`, time: Date.now() }]);
|
||||
} catch (err) {
|
||||
setAiActionLog(prev => [...prev, { type: 'error', text: 'Lỗi: ' + err.message, time: Date.now() }]);
|
||||
} finally {
|
||||
setAiProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ── Split Track at Playhead ──
|
||||
const handleSplitTrackAtTime = (trackId, clipId, time) => {
|
||||
const track = tracks.find(t => t.id === trackId);
|
||||
@@ -8319,9 +8133,7 @@ const App = () => {
|
||||
}, "Chưa có hành động nào.") : aiActionLog.map((entry, i) => /*#__PURE__*/React.createElement("div", {
|
||||
key: i,
|
||||
className: `text-[9px] font-mono py-0.5 border-b border-zinc-900 last:border-0 ${entry.type === 'error' ? 'text-red-400' : entry.type === 'status' ? 'text-zinc-400 italic' : entry.type === 'undo' ? 'text-amber-400' : 'text-zinc-300'}`
|
||||
}, /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-zinc-600 mr-1"
|
||||
}, new Date(entry.time).toLocaleTimeString()), entry.text)))));
|
||||
}, new Date(entry.time).toLocaleTimeString(), entry.text)));
|
||||
if (panelId === 'python_tools') return /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex flex-col h-full gap-1.5"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
|
||||
Reference in New Issue
Block a user