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
};
@@ -0,0 +1,248 @@
const axios = require('axios');
function providerErrorMessage(error) {
const data = error.response?.data;
if (data && typeof data === 'object' && !data.readable) {
return data.error?.message || data.message || error.message;
}
return error.response?.status
? `HTTP ${error.response.status}: ${error.response.statusText || error.message}`
: error.message;
}
/**
* Base LLM Provider class
*/
class BaseLLMProvider {
constructor(config, apiKey) {
this.config = config;
this.apiKey = apiKey;
}
async createChatCompletion(messages, options = {}) {
throw new Error('createChatCompletion method must be implemented by subclass');
}
/**
* Prepare headers for API request
* @returns {Object} Headers object
*/
getHeaders() {
return {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
};
}
/**
* Prepare request payload
* @param {Array} messages - Chat messages
* @param {Object} options - Additional options
* @returns {Object} Request payload
*/
getRequestPayload(messages, options = {}) {
return {
model: this.config.model,
messages: messages,
stream: true,
max_tokens: options.max_tokens || 4096,
...options
};
}
}
/**
* OpenAI LLM Provider
*/
class OpenAILLMProvider extends BaseLLMProvider {
async createChatCompletion(messages, options = {}) {
try {
const payload = this.getRequestPayload(messages, options);
const headers = this.getHeaders();
console.log('OpenAI LLM Request:', {
model: payload.model,
messagesCount: messages.length,
stream: payload.stream
});
const response = await axios.post(
this.config.apiUrl,
payload,
{
headers: headers,
cancelToken: options.cancelToken,
responseType: 'stream'
}
);
return {
success: true,
response: response,
provider: 'openai'
};
} catch (error) {
const message = providerErrorMessage(error);
console.error('OpenAI LLM error:', message);
return {
success: false,
error: message,
provider: 'openai'
};
}
}
}
/**
* OpenRouter LLM Provider (supports both GPT-4o and Gemini)
*/
class OpenRouterLLMProvider extends BaseLLMProvider {
getHeaders() {
return {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://live-audio-chat.local',
'X-Title': 'Live Audio Chat'
};
}
async createChatCompletion(messages, options = {}) {
try {
const payload = this.getRequestPayload(messages, options);
const headers = this.getHeaders();
console.log('OpenRouter LLM Request:', {
model: payload.model,
messagesCount: messages.length,
stream: payload.stream
});
const response = await axios.post(
this.config.apiUrl,
payload,
{
headers: headers,
cancelToken: options.cancelToken,
responseType: 'stream'
}
);
return {
success: true,
response: response,
provider: 'openrouter'
};
} catch (error) {
const message = providerErrorMessage(error);
console.error('OpenRouter LLM error:', message);
return {
success: false,
error: message,
provider: 'openrouter'
};
}
}
}
/**
* ARK (Doubao) LLM Provider
*/
class ARKLLMProvider extends BaseLLMProvider {
getHeaders() {
return {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
};
}
getRequestPayload(messages, options = {}) {
// ARK API format is similar to OpenAI but may have slight differences
return {
model: this.config.model,
messages: messages,
stream: true,
max_tokens: options.max_tokens || 4096,
temperature: options.temperature || 0.7,
...options
};
}
async createChatCompletion(messages, options = {}) {
try {
const payload = this.getRequestPayload(messages, options);
const headers = this.getHeaders();
console.log('ARK (Doubao) LLM Request:', {
model: payload.model,
messagesCount: messages.length,
stream: payload.stream
});
const response = await axios.post(
this.config.apiUrl,
payload,
{
headers: headers,
cancelToken: options.cancelToken,
responseType: 'stream'
}
);
return {
success: true,
response: response,
provider: 'ark'
};
} catch (error) {
const message = providerErrorMessage(error);
console.error('ARK (Doubao) LLM error:', message);
return {
success: false,
error: message,
provider: 'ark'
};
}
}
}
/**
* LLM Provider Factory
*/
class LLMProviderFactory {
static createProvider(providerName, config, globalConfig) {
const providerConfig = config.LLM_PROVIDERS[providerName];
if (!providerConfig) {
throw new Error(`LLM 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 OpenAILLMProvider(providerConfig, apiKey);
case 'openrouter':
case 'openrouter-gpt':
case 'openrouter-gemini':
return new OpenRouterLLMProvider(providerConfig, apiKey);
case 'ark':
return new ARKLLMProvider(providerConfig, apiKey);
default:
throw new Error(`Unsupported LLM provider: ${providerName}`);
}
}
}
module.exports = {
BaseLLMProvider,
OpenAILLMProvider,
OpenRouterLLMProvider,
ARKLLMProvider,
LLMProviderFactory
};