Fix open-ai early termination & memory improvements
This commit is contained in:
+43
-10
@@ -36,8 +36,10 @@ export class OpenAi extends LLMProvider {
|
||||
private toWire(history: LLMMessage[], system?: string): any[] {
|
||||
const wire: any[] = [];
|
||||
if(system) wire.push({role: 'system', content: system});
|
||||
|
||||
for(let i = 0; i < history.length; i++) {
|
||||
const h = history[i];
|
||||
|
||||
if(h.role !== 'tool') {
|
||||
wire.push({role: h.role, content: this.toWireContent(h.content)});
|
||||
continue;
|
||||
@@ -45,24 +47,34 @@ export class OpenAi extends LLMProvider {
|
||||
|
||||
const calls: any[] = [];
|
||||
const results: any[] = [];
|
||||
|
||||
while(i < history.length && history[i].role === 'tool') {
|
||||
const tool = <any>history[i];
|
||||
const tool: any = history[i];
|
||||
|
||||
calls.push({
|
||||
id: tool.id,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: tool.name,
|
||||
arguments: JSON.stringify(tool.args)
|
||||
arguments: JSON.stringify(tool.args || {})
|
||||
}
|
||||
});
|
||||
|
||||
results.push({
|
||||
role: 'tool',
|
||||
tool_call_id: tool.id,
|
||||
content: tool.error || tool.content || ''
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
wire.push({role: 'assistant', content: null, tool_calls: calls});
|
||||
|
||||
wire.push({
|
||||
role: 'assistant',
|
||||
content: null,
|
||||
tool_calls: calls
|
||||
});
|
||||
|
||||
wire.push(...results);
|
||||
i--;
|
||||
}
|
||||
@@ -76,13 +88,12 @@ export class OpenAi extends LLMProvider {
|
||||
if(!options.history) options.history = [];
|
||||
const history = options.history;
|
||||
if(message) history.push({role: 'user', content: message, timestamp: Date.now()});
|
||||
|
||||
const tools = options.tools || this.ai.options.llm?.tools || [];
|
||||
const requestParams: any = {
|
||||
model: options.model || this.model,
|
||||
stream: !!options.stream,
|
||||
max_completion_tokens: options.maxTokens || this.ai.options.llm?.maxTokens || undefined,
|
||||
temperature: options.temperature || this.ai.options.llm?.temperature || undefined,
|
||||
max_completion_tokens: options.maxTokens ?? this.ai.options.llm?.maxTokens,
|
||||
temperature: options.temperature ?? this.ai.options.llm?.temperature,
|
||||
tools: tools.map(t => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -112,7 +123,10 @@ export class OpenAi extends LLMProvider {
|
||||
|
||||
try {
|
||||
let terminal = false;
|
||||
let iteration = 0;
|
||||
|
||||
do {
|
||||
iteration++;
|
||||
requestParams.messages = this.toWire(history.filter(h => h.role !== 'system'), options.system);
|
||||
|
||||
const callStart = Date.now();
|
||||
@@ -126,36 +140,46 @@ export class OpenAi extends LLMProvider {
|
||||
let usage: any;
|
||||
let finishReason: string | undefined;
|
||||
let msg: any = {content: '', tool_calls: []};
|
||||
let streamedChars = 0;
|
||||
|
||||
if(options.stream) {
|
||||
let streamCompleted = false;
|
||||
try {
|
||||
for await (const chunk of resp) {
|
||||
if(controller.signal.aborted) break;
|
||||
if(chunk.usage) usage = chunk.usage;
|
||||
|
||||
const choice = chunk.choices?.[0];
|
||||
if(choice?.finish_reason) finishReason = choice.finish_reason;
|
||||
|
||||
if(choice?.delta?.content) {
|
||||
msg.content += choice.delta.content;
|
||||
streamedChars += choice.delta.content.length;
|
||||
options.stream({text: choice.delta.content});
|
||||
}
|
||||
|
||||
if(choice?.delta?.tool_calls) {
|
||||
for(const deltaTC of choice.delta.tool_calls) {
|
||||
const index = deltaTC.index ?? msg.tool_calls.length;
|
||||
let existing = msg.tool_calls.find((tc: any) => tc.index === index);
|
||||
|
||||
if(!existing) {
|
||||
existing = {index, id: '', function: {name: '', arguments: ''}};
|
||||
msg.tool_calls.push(existing);
|
||||
}
|
||||
|
||||
if(deltaTC.id) existing.id = deltaTC.id;
|
||||
if(deltaTC.function?.name) existing.function.name = deltaTC.function.name;
|
||||
if(deltaTC.function?.arguments) existing.function.arguments += deltaTC.function.arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
streamCompleted = true;
|
||||
} catch(err) {
|
||||
if(!controller.signal.aborted) throw err;
|
||||
}
|
||||
|
||||
if(streamCompleted && !finishReason) finishReason = msg.tool_calls.length ? 'tool_calls' : 'stop';
|
||||
} else {
|
||||
usage = resp.usage;
|
||||
@@ -164,9 +188,7 @@ export class OpenAi extends LLMProvider {
|
||||
}
|
||||
|
||||
const duration = Date.now() - callStart;
|
||||
const tps = usage?.completion_tokens && duration > 0
|
||||
? usage.completion_tokens / (duration / 1000)
|
||||
: 0;
|
||||
const tps = usage?.completion_tokens && duration > 0 ? usage.completion_tokens / (duration / 1000) : 0;
|
||||
|
||||
if(finishReason === 'length' && !controller.signal.aborted) {
|
||||
if(msg.content?.trim()) history.push({role: 'assistant', content: msg.content.trim(), timestamp: Date.now(), duration, tps});
|
||||
@@ -178,10 +200,20 @@ export class OpenAi extends LLMProvider {
|
||||
}
|
||||
|
||||
const toolCalls = msg.tool_calls || [];
|
||||
|
||||
if(toolCalls.length && !controller.signal.aborted) {
|
||||
if(msg.content?.trim()) history.push({role: 'assistant', content: msg.content.trim(), timestamp: Date.now(), duration, tps});
|
||||
|
||||
const entries = toolCalls.map((tc: any) => {
|
||||
const entry: any = {role: 'tool', id: tc.id, name: tc.function.name, args: JSONAttemptParse(tc.function.arguments, {}), content: undefined, timestamp: Date.now()};
|
||||
const entry: any = {
|
||||
role: 'tool',
|
||||
id: tc.id,
|
||||
name: tc.function.name,
|
||||
args: JSONAttemptParse(tc.function.arguments, {}),
|
||||
content: undefined,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
history.push(entry);
|
||||
return {tc, entry};
|
||||
});
|
||||
@@ -195,6 +227,7 @@ export class OpenAi extends LLMProvider {
|
||||
if(chunk.done) return;
|
||||
options.stream!(chunk);
|
||||
});
|
||||
|
||||
const result = await tool.fn(entry.args, toolStream, this.ai, tc.id);
|
||||
entry.content = typeof result === 'object' ? JSONSanitize(result) : result;
|
||||
} catch(err: any) {
|
||||
|
||||
Reference in New Issue
Block a user