Merge branch 'openai_responses_api' into v3.8.0

This commit is contained in:
Mic 2025-12-22 23:55:00 +01:00
commit 9921632e6e
5 changed files with 49 additions and 49 deletions

View file

@ -49,7 +49,7 @@ let worker = null;
switch (llm) {
case "chatgpt_api":
worker = new Worker('../js/workers/model-worker-openai.js', { type: 'module' });
worker = new Worker('../js/workers/model-worker-openai_responses.js', { type: 'module' });
break;
case "google_gemini_api":
worker = new Worker('../js/workers/model-worker-google_gemini.js', { type: 'module' });

View file

@ -27,13 +27,13 @@ export class OpenAI {
stream = false;
store = false;
constructor({
constructor(
apiKey = '',
model = '',
developer_messages = '',
stream = false,
store = false
} = {}) {
) {
this.apiKey = apiKey;
this.model = model;
this.developer_messages = developer_messages;
@ -78,52 +78,46 @@ export class OpenAI {
}
}
fetchResponse = async (messages, maxTokens = 0) => {
fetchResponse = async (messages, maxTokens = 0, previous_response_id = null) => {
const input = messages.map(msg => ({
role: msg.role,
content: [{ type: "input_text", text: msg.content }]
}));
let request_body = {
model: this.model,
input: input,
stream: this.stream,
store: this.store,
...(maxTokens > 0 ? { 'max_output_tokens': parseInt(maxTokens) } : {}),
...(previous_response_id && this.store ? { 'previous_response_id': previous_response_id } : {})
}
if(this.developer_messages !== ''){
messages.push({role: "developer", content: [{"type": "text", "text": this.developer_messages}]});
request_body.instructions = this.developer_messages;
}
// console.log(">>>>>>>>>>> OpenAI API request: " + JSON.stringify(messages));
try {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
const response = await fetch("https://api.openai.com/v1/responses", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer "+ this.apiKey
},
body: JSON.stringify({
model: this.model,
messages: messages,
stream: this.stream,
store: this.store,
...(maxTokens > 0 ? { 'max_tokens': parseInt(maxTokens) } : {})
}),
body: JSON.stringify(request_body),
});
return response;
}catch (error) {
console.error("[ThunderAI] OpenAI API request failed: " + error);
console.error("[ThunderAI] OpenAI Responses API request failed: " + error);
let output = {};
output.is_exception = true;
output.ok = false;
output.error = "OpenAI API request failed: " + error;
output.error = "OpenAI Responses API request failed: " + error;
return output;
}
}
async countTokensUsingAPI(model, text) {
const response = await fetch('https://api.openai.com/v1/engines/'+model+'/tokenizer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.apiKey
},
body: JSON.stringify({ text })
});
const data = await response.json();
return data.token_count;
}
}
}

View file

@ -44,7 +44,7 @@
this.do_debug = do_debug;
switch (this.llm) {
case "chatgpt_api":
this.worker = new Worker(new URL('./workers/model-worker-openai.js', import.meta.url), { type: 'module' });
this.worker = new Worker(new URL('./workers/model-worker-openai_responses.js', import.meta.url), { type: 'module' });
break;
case "google_gemini_api":
this.worker = new Worker(new URL('./workers/model-worker-google_gemini.js', import.meta.url), { type: 'module' });

View file

@ -20,7 +20,7 @@
* The original code has been released under the Apache License, Version 2.0.
*/
import { OpenAI } from '../api/openai.js';
import { OpenAI } from '../api/openai_responses.js';
import { taLogger } from '../mzta-logger.js';
let chatgpt_api_key = null;
@ -33,25 +33,29 @@ let taLog = null;
let conversationHistory = [];
let assistantResponseAccumulator = '';
let previous_response_id = null;
self.onmessage = async function(event) {
if (event.data.type === 'init') {
chatgpt_api_key = event.data.chatgpt_api_key;
chatgpt_model = event.data.chatgpt_model;
openai = new OpenAI({
apiKey: chatgpt_api_key,
model: chatgpt_model,
developer_messages: event.data.chatgpt_developer_messages,
stream: true,
store: event.data.chatgpt_api_store
});
openai = new OpenAI(chatgpt_api_key, chatgpt_model, event.data.chatgpt_developer_messages, true, event.data.chatgpt_api_store);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
taLog = new taLogger('model-worker-openai', do_debug);
taLog = new taLogger('model-worker-openai_responses', do_debug);
previous_response_id = null;
} else if (event.data.type === 'chatMessage') {
conversationHistory.push({ role: 'user', content: event.data.message });
const response = await openai.fetchResponse(conversationHistory); //4096);
let messagesToSend = conversationHistory;
if (previous_response_id) {
messagesToSend = [conversationHistory[conversationHistory.length - 1]];
taLog.log("previous_response_id: " + previous_response_id);
} else {
taLog.log("no previous_response_id");
}
const response = await openai.fetchResponse(messagesToSend, 0, previous_response_id);
postMessage({ type: 'messageSent' });
if (!response.ok) {
@ -102,6 +106,8 @@ self.onmessage = async function(event) {
let parsedLines = [];
try{
parsedLines = lines
.map((line) => line.trim())
.filter((line) => line.startsWith("data:"))
.map((line) => line.replace(/^data: /, "").trim()) // Remove the "data: " prefix
.filter((line) => line !== "" && line !== "[DONE]") // Remove empty lines and "[DONE]"
// .map((line) => JSON.parse(line)); // Parse the JSON string
@ -120,13 +126,13 @@ self.onmessage = async function(event) {
}
for (const parsedLine of parsedLines) {
const { choices } = parsedLine;
const { delta } = choices[0];
const { content } = delta;
// Update the UI with the new content
if (content) {
assistantResponseAccumulator += content;
postMessage({ type: 'newToken', payload: { token: content } });
if (parsedLine.type === 'response.created' && parsedLine.response && parsedLine.response.id){
previous_response_id = parsedLine.response.id;
} else if (parsedLine.type === 'response.output_text.delta' && parsedLine.delta) {
assistantResponseAccumulator += parsedLine.delta;
postMessage({ type: 'newToken', payload: { token: parsedLine.delta } });
// } else if (parsedLine.type === 'response.completed' && parsedLine.response && parsedLine.response.id) {
// previous_response_id = parsedLine.response.id;
}
}
}

View file

@ -17,7 +17,7 @@
*/
import { prefs_default } from '../../options/mzta-options-default.js';
import { OpenAI } from '../../js/api/openai.js';
import { OpenAI } from '../../js/api/openai_responses.js';
import { Ollama } from '../../js/api/ollama.js';
import { OpenAIComp } from '../../js/api/openai_comp.js'
import { GoogleGemini } from '../../js/api/google_gemini.js';