fix: change md files to md folder

This commit is contained in:
2026-07-21 18:22:39 +07:00
parent 20bf2bd5d8
commit d5143b440a
34 changed files with 6962 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
// SonicForge Studio - AI Gateway & Function Routing
// LLM Gateway with Function Calling / Structured Outputs (28_AI_PANEL.md §2)
const AIGateway = (function() {
const DEFAULT_TOOLS = [{
name: 'create_track',
description: 'Tạo một track mới trong dự án',
parameters: {
type: 'object',
properties: {
name: { type: 'string', description: 'Tên track' },
type: { type: 'string', enum: ['audio', 'midi'], description: 'Loại track' }
},
required: ['name', 'type']
}
}, {
name: 'add_midi_item',
description: 'Thêm một MIDI item/clip vào track',
parameters: {
type: 'object',
properties: {
track_id: { type: 'string', description: 'ID của track đích' },
start_bar: { type: 'number', description: 'Vị trí bắt đầu (tính bằng bar)' },
length_bars: { type: 'number', description: 'Độ dài item (tính bằng bar)' }
},
required: ['track_id', 'start_bar', 'length_bars']
}
}, {
name: 'modify_midi_notes',
description: 'Thêm, chỉnh sửa hoặc xóa các note MIDI trong item',
parameters: {
type: 'object',
properties: {
item_id: { type: 'string', description: 'ID của MIDI item' },
notes: {
type: 'array',
description: 'Danh sách các note MIDI',
items: {
type: 'object',
properties: {
pitch: { type: 'string', description: 'VD: C4, D#3, F5' },
start_time: { type: 'number', description: 'Thời điểm bắt đầu (bar hoặc giây)' },
duration: { type: 'number', description: 'Độ dài note' },
velocity: { type: 'integer', minimum: 0, maximum: 127, description: 'Độ mạnh 0-127' }
},
required: ['pitch', 'start_time', 'duration']
}
}
},
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']
}
}];
async function callLLM({ provider, model, apiKey, baseUrl, messages, tools, toolChoice }) {
const url = `${baseUrl.replace(/\/$/, '')}/chat/completions`;
const headers = {
'Content-Type': 'application/json',
...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {})
};
const body = {
model,
messages,
...(tools && tools.length > 0 ? { tools: tools.map(t => ({ type: 'function', function: t })) } : {}),
...(toolChoice ? { tool_choice: toolChoice } : {})
};
const response = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(body)
});
if (!response.ok) {
const errText = await response.text();
throw new Error(`LLM API error ${response.status}: ${errText}`);
}
return await response.json();
}
function extractFunctionCalls(completion) {
const calls = [];
const choice = completion.choices && completion.choices[0];
if (!choice) return calls;
const msg = choice.message;
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
for (const tc of msg.tool_calls) {
if (tc.type === 'function' && tc.function) {
let args = {};
try { args = JSON.parse(tc.function.arguments || '{}'); } catch (e) { args = { raw: tc.function.arguments }; }
calls.push({
id: tc.id,
name: tc.function.name,
arguments: args
});
}
}
} else if (msg.function_call) {
let args = {};
try { args = JSON.parse(msg.function_call.arguments || '{}'); } catch (e) { args = { raw: msg.function_call.arguments }; }
calls.push({
id: 'call_' + Date.now(),
name: msg.function_call.name,
arguments: args
});
}
return calls;
}
function buildUserMessage(prompt, context) {
const contextStr = JSON.stringify(context, null, 2);
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: 'user', content: `Ngữ cảnh DAW hiện tại:\n${contextStr}\n\nYêu cầu người dùng: ${prompt}` }
];
}
async function executePrompt({ prompt, provider, model, apiKey, baseUrl, dawContext, tools }) {
const messages = buildUserMessage(prompt, dawContext);
const toolList = tools || DEFAULT_TOOLS;
const completion = await callLLM({
provider,
model,
apiKey,
baseUrl,
messages,
tools: toolList,
toolChoice: 'auto'
});
const functionCalls = extractFunctionCalls(completion);
const textResponse = completion.choices && completion.choices[0] && completion.choices[0].message && completion.choices[0].message.content
? completion.choices[0].message.content
: '';
return {
functionCalls,
textResponse,
raw: completion
};
}
return {
DEFAULT_TOOLS,
callLLM,
extractFunctionCalls,
buildUserMessage,
executePrompt
};
})();
window.AIGateway = AIGateway;