ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s

This commit is contained in:
2026-08-20 13:12:50 +00:00
commit b119135836
10275 changed files with 3284984 additions and 0 deletions
@@ -0,0 +1,276 @@
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
/**
* Base ASR Provider class
*/
class BaseASRProvider {
constructor(config, apiKey) {
this.config = config;
this.apiKey = apiKey;
}
/**
* Convert raw audio buffer to WAV format
* @param {Buffer} audioBuffer - Raw PCM audio data
* @param {Object} options - Audio format options
* @returns {Buffer} WAV formatted audio data
*/
createWavBuffer(audioBuffer, options = {}) {
const sampleRate = options.sampleRate || 16000;
const channels = options.channels || 1;
const bitsPerSample = options.bitsPerSample || 16;
const byteRate = sampleRate * channels * bitsPerSample / 8;
const blockAlign = channels * bitsPerSample / 8;
const dataSize = audioBuffer.length;
const fileSize = 36 + dataSize;
const header = Buffer.alloc(44);
// RIFF header
header.write('RIFF', 0);
header.writeUInt32LE(fileSize, 4);
header.write('WAVE', 8);
// fmt chunk
header.write('fmt ', 12);
header.writeUInt32LE(16, 16); // PCM format chunk size
header.writeUInt16LE(1, 20); // PCM format
header.writeUInt16LE(channels, 22);
header.writeUInt32LE(sampleRate, 24);
header.writeUInt32LE(byteRate, 28);
header.writeUInt16LE(blockAlign, 32);
header.writeUInt16LE(bitsPerSample, 34);
// data chunk
header.write('data', 36);
header.writeUInt32LE(dataSize, 40);
return Buffer.concat([header, audioBuffer]);
}
async transcribe(audioBuffer, options = {}) {
throw new Error('transcribe method must be implemented by subclass');
}
}
/**
* OpenAI Whisper ASR Provider
*/
class OpenAIASRProvider extends BaseASRProvider {
async transcribe(audioBuffer, tempDir, options = {}) {
try {
// Create WAV buffer
const wavBuffer = this.createWavBuffer(audioBuffer, {
sampleRate: 16000,
channels: 1,
bitsPerSample: 16
});
// Create temporary file
const tempFileName = `audio_${Date.now()}_${Math.random().toString(36).substring(2)}.wav`;
const tempFilePath = path.join(tempDir, tempFileName);
// Write audio to temporary file
fs.writeFileSync(tempFilePath, wavBuffer);
try {
// Create form data
const formData = new FormData();
formData.append('file', fs.createReadStream(tempFilePath));
formData.append('model', this.config.model);
formData.append('response_format', 'json');
if (options.language) {
formData.append('language', options.language);
}
if (options.prompt) {
formData.append('prompt', options.prompt);
}
// Make API request
const response = await axios({
method: 'post',
url: this.config.apiUrl,
data: formData,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
...formData.getHeaders()
},
timeout: 30000 // 30 second timeout
});
const result = {
success: true,
text: response.data.text || '',
language: response.data.language || 'unknown',
duration: response.data.duration || 0,
confidence: response.data.confidence || 1.0,
requestId: response.headers?.['x-request-id'] || response.headers?.['request-id'] || null,
responseModel: response.data.model || this.config.model,
timestamp: Date.now(),
provider: 'openai'
};
console.log('OpenAI ASR Result:', {
text: result.text,
language: result.language,
duration: result.duration
});
return result;
} finally {
// Clean up temporary file
try {
fs.unlinkSync(tempFilePath);
} catch (cleanupError) {
console.warn('Failed to cleanup temp file:', cleanupError.message);
}
}
} catch (error) {
console.error('OpenAI ASR error:', error.response?.data || error.message);
return {
success: false,
text: '',
error: error.response?.data?.error?.message || error.message,
timestamp: Date.now(),
provider: 'openai'
};
}
}
}
/**
* SenseVoice via Siliconflow ASR Provider
*/
class SiliconflowASRProvider extends BaseASRProvider {
async transcribe(audioBuffer, tempDir, options = {}) {
try {
// Create WAV buffer
const wavBuffer = this.createWavBuffer(audioBuffer, {
sampleRate: 16000,
channels: 1,
bitsPerSample: 16
});
// Create temporary file
const tempFileName = `audio_${Date.now()}_${Math.random().toString(36).substring(2)}.wav`;
const tempFilePath = path.join(tempDir, tempFileName);
// Write audio to temporary file
fs.writeFileSync(tempFilePath, wavBuffer);
try {
// Create form data
const formData = new FormData();
formData.append('file', fs.createReadStream(tempFilePath));
formData.append('model', this.config.model);
formData.append('response_format', 'json');
// SenseVoice specific parameters
if (options.language) {
formData.append('language', options.language);
} else {
// SenseVoice supports auto language detection
formData.append('language', 'auto');
}
if (options.prompt) {
formData.append('prompt', options.prompt);
}
// Make API request
const response = await axios({
method: 'post',
url: this.config.apiUrl,
data: formData,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
...formData.getHeaders()
},
timeout: 30000 // 30 second timeout
});
const result = {
success: true,
text: response.data.text || '',
language: response.data.language || 'unknown',
duration: response.data.duration || 0,
confidence: response.data.confidence || 1.0,
requestId: response.headers?.['x-request-id'] || response.headers?.['request-id'] || null,
responseModel: response.data.model || this.config.model,
timestamp: Date.now(),
provider: 'siliconflow'
};
console.log('SenseVoice ASR Result:', {
text: result.text,
language: result.language,
duration: result.duration
});
return result;
} finally {
// Clean up temporary file
try {
fs.unlinkSync(tempFilePath);
} catch (cleanupError) {
console.warn('Failed to cleanup temp file:', cleanupError.message);
}
}
} catch (error) {
console.error('SenseVoice ASR error:', error.response?.data || error.message);
return {
success: false,
text: '',
error: error.response?.data?.error?.message || error.message,
timestamp: Date.now(),
provider: 'siliconflow'
};
}
}
}
/**
* ASR Provider Factory
*/
class ASRProviderFactory {
static createProvider(providerName, config, globalConfig) {
const providerConfig = config.ASR_PROVIDERS[providerName];
if (!providerConfig) {
throw new Error(`ASR provider ${providerName} not found in configuration`);
}
// Get API key from global config
const apiKey = globalConfig[providerConfig.apiKey];
if (!apiKey) {
throw new Error(`API key ${providerConfig.apiKey} not found in configuration`);
}
switch (providerName) {
case 'openai':
return new OpenAIASRProvider(providerConfig, apiKey);
case 'siliconflow':
return new SiliconflowASRProvider(providerConfig, apiKey);
default:
throw new Error(`Unsupported ASR provider: ${providerName}`);
}
}
}
module.exports = {
BaseASRProvider,
OpenAIASRProvider,
SiliconflowASRProvider,
ASRProviderFactory
};