ForgeTotal.js 5
it

Total.js 5

Parla con i modelli linguistici con AIMODEL(), NEWAIMODEL() e AIPARSER()

Total.js 5 · total5@0.0.19 aillmintegrations Eseguita con Total.js 5

total5 non spedisce una connessione a nessun provider. Ti dà tre pezzi che lavorano insieme:

  • NEWAIMODEL('name', config, driver) registra il driver di uno o più modelli ('a,b' registra lo stesso driver per due nomi). Il driver è function(ai, done): ai.payload ha model, messages, tools, prompt e think, ai.config è la configurazione e ai.options.stream è la funzione chiamante passato a stream() (o undefined). Rispondi con done(error, response).
  • AIMODEL('name') avvia una richiesta per un modello registrato e restituisce un costruttore. system(text), user(text) e assistant(text) aggiungono un messaggio, prompt(text), tool(definition) e think() riempiono il resto del payload e configure({ ... }) aggiunge a ai.config solo per questa chiamata. Finiscilo con callback(fn), await promise($) o stream(fn); l'autista corre dietro alla svolta attuale, quindi puoi prima concatenare tutto.
  • AIPARSER(provider) restituisce un parser per la risposta streamed di un provider (la fonte di total5 conosce openai, claude/anthropic, gemini/google, ollama e openai_responses, e legge qualsiasi altro nome come formato generico; di seguito viene eseguito solo openai). write(chunk) prende pezzi del flusso (mantiene le righe incomplete fino al pezzo successivo), end() lo termina ed entrambi restituiscono { content, thinking, tools, reasoning }. parser.on('content', fn) viene chiamato con ogni parte del testo man mano che arriva.

Un modello non registrato risponde con l'errore AI model not found.. L'esempio registra un driver locale che restituisce una risposta dai messaggi forniti.

definitions/ai.js
NEWAIMODEL('echo', { prefix: 'Echo' }, function(ai, done) {

	const last = ai.payload.messages.filter(n => n.role === 'user').pop();
	const text = ai.config.prefix + ': ' + (last ? last.content : '');

	// A real driver would send ai.payload to the provider and send the pieces to ai.options.stream
	if (ai.options.stream) {
		for (const word of text.split(' '))
			ai.options.stream(word + ' ');
	}

	done(null, { content: text, roles: ai.payload.messages.map(n => n.role), tools: ai.payload.tools.length, think: !!ai.payload.think });
});
controllers/ai.js
exports.install = function() {
	ROUTE('POST /ask/', ask);
	ROUTE('GET /ask-config/', askconfig);
	ROUTE('GET /ask-stream/', askstream);
	ROUTE('GET /ask-missing/', missing);
	ROUTE('GET /parse/', parse);
};

async function ask($) {
	const response = await AIMODEL('echo').system('You are brief.').user($.body.question).tool({ name: 'weather' }).think().promise($);
	$.json(response);
}

function askconfig($) {
	AIMODEL('echo').user('hello').configure({ prefix: 'Custom' }).callback($.callback());
}

function askstream($) {
	const parts = [];
	AIMODEL('echo').user('one two three').stream(part => parts.push(part)).callback(function(err, response) {
		$.json({ parts: parts, content: response.content });
	});
}

function missing($) {
	AIMODEL('nobody').user('hi').callback($.callback());
}

function parse($) {
	const parser = AIPARSER('openai');
	const events = [];
	parser.on('content', e => events.push(e.delta));

	// the second piece starts in the middle of a line
	parser.write('data: {"choices":[{"delta":{"content":"Hel"}}]}\n\ndata: {"choices":[{"delta":{"con');
	parser.write('tent":"lo"}}]}\n\ndata: [DONE]\n');
	const output = parser.end();

	$.json({ output: output, events: events });
}