aquanet-bot-lib
Version:
A library for building aquaculture chatbots with LLM integration
156 lines (155 loc) • 6.26 kB
JavaScript
import axios from 'axios';
import { ConfigSchema } from '../types';
import { AquacultureConfigSchema } from '../validators/aquaculture';
export class AquanetBot {
constructor(options) {
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "defaultSystemPrompt", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// Validate base config
const validatedConfig = ConfigSchema.parse(options.config);
// Validate aquaculture config if provided
if (options.config.aquacultureConfig) {
AquacultureConfigSchema.parse(options.config.aquacultureConfig);
}
this.config = validatedConfig;
this.client = axios.create({
baseURL: this.config.baseUrl,
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
});
// Build system prompt based on aquaculture config
this.defaultSystemPrompt = this.buildSystemPrompt();
}
buildSystemPrompt() {
const aquaConfig = this.config.aquacultureConfig;
if (!aquaConfig) {
return this.config.defaultPrompt ||
'You are an AI assistant specialized in aquaculture business.';
}
const prompts = [
// Base role definition
'You are an AI assistant specialized in aquaculture business.',
// Knowledge domains
`Your expertise covers: ${aquaConfig.knowledgeDomains.join(', ')}.`,
// Expertise level and language
`Provide ${aquaConfig.expertiseLevel}-level information in ${aquaConfig.language} language.`,
// Industry terms usage
aquaConfig.useIndustryTerms ?
'Use industry-specific terminology and technical language.' :
'Use simplified language accessible to general audience.',
// Source citation requirement
aquaConfig.validation.requireSourceCitation ?
'Include source citations for technical information and data.' : '',
// Species-specific instructions
aquaConfig.customization.speciesSpecific.length > 0 ?
`Specialized in: ${aquaConfig.customization.speciesSpecific.join(', ')}.` : '',
// Regional guidelines
aquaConfig.customization.regionalGuidelines.length > 0 ?
`Follow guidelines for: ${aquaConfig.customization.regionalGuidelines.join(', ')}.` : '',
// Custom system prompt if provided
this.config.defaultPrompt || '',
];
return prompts.filter(Boolean).join('\n');
}
async processStreamResponse(response) {
const reader = response.body?.getReader();
const decoder = new TextDecoder();
let result = '';
if (!reader)
throw new Error('Stream reader not available');
try {
while (true) {
const { done, value } = await reader.read();
if (done)
break;
const chunk = decoder.decode(value);
const lines = chunk
.split('\n')
.filter(line => line.trim().startsWith('data: '))
.map(line => line.replace('data: ', '').trim())
.filter(line => line !== '[DONE]' && line);
for (const line of lines) {
try {
const parsed = JSON.parse(line);
const content = parsed.choices[0]?.delta?.content || '';
if (content) {
result += content;
this.config.onStream?.(content);
}
}
catch (e) {
console.warn('Failed to parse stream chunk:', line);
}
}
}
}
finally {
reader.releaseLock();
}
return result;
}
async chat(messages, systemPrompt) {
const isStreaming = this.config.responseFormat === 'stream';
const requestBody = {
model: "deepseek-chat",
messages: [
{ role: 'system', content: systemPrompt || this.defaultSystemPrompt },
...messages,
],
temperature: this.config.temperature,
max_tokens: this.config.maxTokens,
stream: isStreaming
};
try {
if (isStreaming) {
const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await this.processStreamResponse(response);
}
else {
const response = await this.client.post('/chat/completions', requestBody);
return response.data;
}
}
catch (error) {
if (error.response?.data?.error) {
throw new Error(`API request failed: ${error.response.data.error}`);
}
throw new Error(`API request failed: ${error.message}`);
}
}
async query(prompt) {
const response = await this.chat([{ role: 'user', content: prompt }]);
if (typeof response === 'string') {
return response;
}
return response.choices[0]?.message?.content || '';
}
}