fix: đã sửa lỗi AI gửi prompt và thêm các tool để AI thực hiện
This commit is contained in:
@@ -4,17 +4,145 @@
|
||||
const AIGateway = (function() {
|
||||
const DEFAULT_TOOLS = [{
|
||||
name: 'create_track',
|
||||
description: 'Tạo một track mới trong dự án',
|
||||
description: 'Tạo một track âm thanh mới trong dự án',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Tên track' },
|
||||
name: { type: 'string', description: 'Tên cho track mới (VD: Beat, Vocal, Guitar)' },
|
||||
type: { type: 'string', enum: ['audio', 'midi'], description: 'Loại track' }
|
||||
},
|
||||
required: ['name', 'type']
|
||||
required: ['name']
|
||||
}
|
||||
}, {
|
||||
name: 'add_midi_item',
|
||||
name: 'delete_track',
|
||||
description: 'Xóa một track khỏi dự án',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track cần xóa. Nếu không có thì xóa track đang chọn.' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'add_clip',
|
||||
description: 'Thêm một clip âm thanh rỗng vào track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track đích' },
|
||||
start_time: { type: 'number', description: 'Vị trí bắt đầu (giây)' },
|
||||
duration_seconds: { type: 'number', description: 'Độ dài clip (giây)' },
|
||||
start_bar: { type: 'number', description: 'Vị trí bắt đầu (bar). Bar 0 = bar đầu tiên. Dùng thay cho start_time.' },
|
||||
length_bars: { type: 'number', description: 'Độ dài (bar). Dùng thay cho duration_seconds.' },
|
||||
name: { type: 'string', description: 'Tên clip' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'remove_clip',
|
||||
description: 'Xóa một clip khỏi track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track' },
|
||||
clip_id: { type: 'string', description: 'ID của clip cần xóa' }
|
||||
},
|
||||
required: ['clip_id']
|
||||
}
|
||||
}, {
|
||||
name: 'set_track_volume',
|
||||
description: 'Điều chỉnh âm lượng của track (dB)',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track' },
|
||||
volume_db: { type: 'number', description: 'Âm lượng tính bằng dB (VD: -6, 0, +3)' }
|
||||
},
|
||||
required: ['volume_db']
|
||||
}
|
||||
}, {
|
||||
name: 'set_track_pan',
|
||||
description: 'Điều chỉnh cân bằng trái/phải (pan) của track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track' },
|
||||
pan: { type: 'integer', description: 'Pan value: -100 (trái), 0 (trung tâm), 100 (phải)' }
|
||||
},
|
||||
required: ['pan']
|
||||
}
|
||||
}, {
|
||||
name: 'toggle_mute',
|
||||
description: 'Bật/tắt mute (tắt tiếng) của track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track cần mute/unmute' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'toggle_solo',
|
||||
description: 'Bật/tắt solo (chỉ nghe track này) của track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track cần solo/unsolo' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'rename_track',
|
||||
description: 'Đổi tên của một track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track cần đổi tên' },
|
||||
name: { type: 'string', description: 'Tên mới cho track' }
|
||||
},
|
||||
required: ['track_id', 'name']
|
||||
}
|
||||
}, {
|
||||
name: 'set_bpm',
|
||||
description: 'Thay đổi tempo (BPM) của dự án',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
bpm: { type: 'number', description: 'Tempo mới tính bằng BPM (VD: 120, 128, 140)' }
|
||||
},
|
||||
required: ['bpm']
|
||||
}
|
||||
}, {
|
||||
name: 'set_playhead',
|
||||
description: 'Di chuyển playhead (con trỏ phát) đến vị trí chỉ định',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
time: { type: 'number', description: 'Vị trí thời gian tính bằng giây' },
|
||||
bar: { type: 'number', description: 'Vị trí bar (0 = bar đầu tiên). Dùng thay cho time.' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'add_marker',
|
||||
description: 'Thêm một marker (đánh dấu) vào track tại vị trí chỉ định',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track' },
|
||||
time: { type: 'number', description: 'Vị trí thời gian (giây). Mặc định là vị trí playhead hiện tại.' },
|
||||
label: { type: 'string', description: 'Nhãn cho marker' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'process_audio_dsp',
|
||||
description: 'Xử lý hiệu ứng âm thanh DSP cho track (chuẩn hóa, đảo phase, gain, pitch shift)',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track âm thanh' },
|
||||
action: { type: 'string', enum: ['normalize', 'invert_phase', 'gain', 'pitch_shift'], description: 'Loại xử lý DSP' },
|
||||
params: { type: 'object', description: 'Tham số bổ sung: gain_db cho gain, semitones cho pitch_shift' }
|
||||
},
|
||||
required: ['track_id', 'action']
|
||||
}
|
||||
}, {
|
||||
name: 'create_midi_item',
|
||||
description: 'Thêm một MIDI item/clip vào track',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
@@ -25,6 +153,28 @@ const AIGateway = (function() {
|
||||
},
|
||||
required: ['track_id', 'start_bar', 'length_bars']
|
||||
}
|
||||
}, {
|
||||
name: 'set_selection',
|
||||
description: 'Chọn một vùng trên timeline (selection range). Dùng để xác định khoảng thời gian trước khi gọi các lệnh khác.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
start_bar: { type: 'number', description: 'Bar bắt đầu (0 = bar đầu tiên của project)' },
|
||||
end_bar: { type: 'number', description: 'Bar kết thúc' },
|
||||
start_time: { type: 'number', description: 'Thời gian bắt đầu (giây). Dùng thay cho start_bar.' },
|
||||
end_time: { type: 'number', description: 'Thời gian kết thúc (giây). Dùng thay cho end_bar.' },
|
||||
length_bars: { type: 'number', description: 'Độ dài vùng chọn (bar). Dùng cùng start_bar thay cho end_bar.' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'scan_track',
|
||||
description: 'Quét và phân tích track âm thanh: phát hiện BPM (tempo), sample rate, số kênh (mono/stereo), bit depth, duration',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track cần quét. Nếu không có thì quét track đang chọn.' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'modify_midi_notes',
|
||||
description: 'Thêm, chỉnh sửa hoặc xóa các note MIDI trong item',
|
||||
@@ -49,18 +199,6 @@ const AIGateway = (function() {
|
||||
},
|
||||
required: ['item_id', 'notes']
|
||||
}
|
||||
}, {
|
||||
name: 'process_audio_dsp',
|
||||
description: 'Gửi yêu cầu chỉnh sửa âm thanh sang Python DSP Backend',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track âm thanh' },
|
||||
action: { type: 'string', enum: ['normalize', 'invert_phase', 'gain', 'pitch_shift'], description: 'Loại xử lý DSP' },
|
||||
params: { type: 'object', description: 'Tham số bổ sung cho hành động' }
|
||||
},
|
||||
required: ['track_id', 'action']
|
||||
}
|
||||
}];
|
||||
|
||||
function parseOrigin(urlStr) {
|
||||
@@ -91,6 +229,7 @@ const AIGateway = (function() {
|
||||
const body = {
|
||||
model,
|
||||
messages,
|
||||
stream: false,
|
||||
...(tools && tools.length > 0 ? { tools: tools.map(t => ({ type: 'function', function: t })) } : {}),
|
||||
...(toolChoice ? { tool_choice: toolChoice } : {})
|
||||
};
|
||||
@@ -155,29 +294,33 @@ const AIGateway = (function() {
|
||||
|
||||
function buildUserMessage(prompt, context) {
|
||||
const contextStr = JSON.stringify(context, null, 2);
|
||||
const toolNames = DEFAULT_TOOLS.map(t => ` - ${t.name}: ${t.description}`).join('\n');
|
||||
return [
|
||||
{ role: 'system', content: 'Bạn là trợ lý AI cho DAW (SonicForge Studio). Hãy phân tích yêu cầu người dùng và phản hồi BẰNG DẠNG FUNCTION CALLS phù hợp. Luôn trả về function call khi có thể thực hiện hành động.' },
|
||||
{ role: 'system', content: `Bạn là trợ lý AI cho DAW (SonicForge Studio). Các lệnh DAW có sẵn:\n${toolNames}\n\nQUAN TRỌNG: Bar được đánh số từ 0 (bar 0 = bar đầu tiên). VD: bar 0-3 = 4 bar đầu tiên.\nPhân tích yêu cầu và trả về FUNCTION CALLS. Có thể gọi nhiều function cùng lúc.` },
|
||||
{ role: 'user', content: `Ngữ cảnh DAW hiện tại:\n${contextStr}\n\nYêu cầu người dùng: ${prompt}` }
|
||||
];
|
||||
}
|
||||
|
||||
function buildAIPromptContext(dawState) {
|
||||
const tracks = (dawState.tracks || []).map(t => ({
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
type: t.buffer ? 'audio' : 'empty',
|
||||
hasBuffer: !!t.buffer,
|
||||
clipsCount: t.clips ? t.clips.length : (t.buffer ? 1 : 0),
|
||||
muted: t.muted,
|
||||
solo: t.solo,
|
||||
volumeDb: t.volumeDb ?? 0,
|
||||
pan: t.pan ?? 0
|
||||
}));
|
||||
const tracks = (dawState.tracks || []).map(t => {
|
||||
const clips = t.clips && t.clips.length > 0 ? t.clips : (t.buffer ? [{ id: 'default_' + t.id, name: t.name, startTime: t.startTime || 0, duration: t.buffer.duration }] : []);
|
||||
return {
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
type: t.buffer ? 'audio' : 'empty',
|
||||
hasBuffer: !!t.buffer,
|
||||
muted: t.muted,
|
||||
solo: t.solo,
|
||||
volumeDb: t.volumeDb ?? 0,
|
||||
pan: t.pan ?? 0,
|
||||
clips: clips.map(c => ({ id: c.id, name: c.name, startTime: parseFloat((c.startTime || 0).toFixed(3)), duration: parseFloat((c.buffer ? c.buffer.duration : 0).toFixed(3)) }))
|
||||
};
|
||||
});
|
||||
return {
|
||||
tempo: dawState.bpm || 120,
|
||||
tempo: parseInt(dawState.bpm || '120'),
|
||||
timeSignature: '4/4',
|
||||
selectedTrackId: dawState.selectedTrackId || null,
|
||||
playheadPosition: dawState.currentTime || 0,
|
||||
playheadPosition: parseFloat((dawState.currentTime || 0).toFixed(3)),
|
||||
selection: (dawState.selLeft !== null && dawState.selRight !== null && dawState.selRight > dawState.selLeft) ? {
|
||||
start: parseFloat(dawState.selLeft.toFixed(3)),
|
||||
end: parseFloat(dawState.selRight.toFixed(3)),
|
||||
|
||||
@@ -57,6 +57,9 @@ const DAWCommandDispatcher = (function() {
|
||||
register('TOGGLE_MUTE', (args) => api.toggleMute(args));
|
||||
register('TOGGLE_SOLO', (args) => api.toggleSolo(args));
|
||||
register('PROCESS_AUDIO_DSP', (args) => api.processAudioDsp(args));
|
||||
register('RENAME_TRACK', (args) => api.renameTrack(args));
|
||||
register('SCAN_TRACK', (args) => api.scanTrack(args));
|
||||
register('SET_SELECTION', (args) => api.setSelection(args));
|
||||
register('SET_BPM', (args) => api.setBpm(args));
|
||||
register('SET_PLAYHEAD', (args) => api.setPlayhead(args));
|
||||
register('ADD_MARKER', (args) => api.addMarker(args));
|
||||
|
||||
Reference in New Issue
Block a user