Skip to content
Snippets Groups Projects
Commit c20da9cf authored by Kevin Quillen's avatar Kevin Quillen
Browse files

Issue #3361577 by kevinquillen: Implement streamed responses where applicable

parent 15f99b20
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
......@@ -18,24 +18,29 @@ export default class CompletionCommand extends Command {
const editor = this.editor;
const formView = new FormView(editor.locale);
this.listenTo( formView, 'submit', () => {
this.listenTo( formView, 'submit', () => {
const prompt = formView.promptInputView.fieldView.element.value;
this._hideUI();
// @todo Need to have an AJAX indicator while the API waits for a response.
// @todo add error handling
editor.model.change( writer => {
fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
editor.model.change(async writer => {
const response = await fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({'prompt': prompt, 'options': this._config}),
})
.then((response) => response.json())
.then((answer) => editor.model.insertContent(
writer.createText(answer.text)
))
.then(() => this._hideUI()
)
});
const reader = response.body.getReader();
while (true) {
const {value, done} = await reader.read();
const text = new TextDecoder().decode(value);
if (done) break;
editor.model.insertContent(
writer.createText(text)
);
}
} );
} );
......
......@@ -85,38 +85,38 @@ export default class OpenAIUI extends Plugin {
} )
});
items.add( {
type: 'button',
model: new Model( {
isEnabled: false,
label: 'Ask ChatGPT',
withText: true,
command: '',
group: {}
} )
});
items.add( {
type: 'button',
model: new Model( {
isEnabled: false,
label: 'Generate image',
withText: true,
command: '',
group: {}
} )
});
items.add( {
type: 'button',
model: new Model( {
isEnabled: false,
label: 'Sentiment analysis',
withText: true,
command: '',
group: {}
} )
});
// items.add( {
// type: 'button',
// model: new Model( {
// isEnabled: false,
// label: 'Ask ChatGPT',
// withText: true,
// command: '',
// group: {}
// } )
// });
//
// items.add( {
// type: 'button',
// model: new Model( {
// isEnabled: false,
// label: 'Generate image',
// withText: true,
// command: '',
// group: {}
// } )
// });
//
// items.add( {
// type: 'button',
// model: new Model( {
// isEnabled: false,
// label: 'Sentiment analysis',
// withText: true,
// command: '',
// group: {}
// } )
// });
items.add( {
type: 'button',
......
......@@ -27,8 +27,8 @@ export default class ReformatHTMLCommand extends Command {
credentials: 'same-origin',
body: JSON.stringify({'prompt': prompt, 'options': this._config}),
})
.then((response) => response.json())
.then((answer) => this._writeHTML(answer.text, range))
.then((response) => response.text())
.then((answer) => this._writeHTML(answer, range))
} );
}
......
......@@ -16,22 +16,28 @@ export default class SummarizeCommand extends Command {
selectedText = item.data;
}
const prompt = 'Summarize the following text into something more compact: ' + selectedText;
const prompt = 'Summarize the following text into something more compact, in the same language as the following: ' + selectedText;
// @todo Need to have an AJAX indicator while the API waits for a response.
// @todo add error handling
editor.model.change( writer => {
fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
editor.model.change(async writer => {
const response = await fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({'prompt': prompt, 'options': this._config}),
})
.then((response) => response.json())
.then((answer) => editor.model.insertContent(
editor.model.insertContent( writer.createText(answer.text), range )
)
)
});
const reader = response.body.getReader();
while (true) {
const {value, done} = await reader.read();
const text = new TextDecoder().decode(value);
if (done) break;
editor.model.insertContent(
editor.model.insertContent( writer.createText(text) )
);
}
} );
}
}
......@@ -28,22 +28,29 @@ export default class ToneCommand extends Command {
}
const prompt = 'Change the tone of the following text to be more ' + formView.toneInputView.fieldView.element.value + ': ' + selectedText;
this._hideUI();
// @todo Need to have an AJAX indicator while the API waits for a response.
// @todo add error handling
editor.model.change(async writer => {
editor.model.change( writer => {
fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
const response = await fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({'prompt': prompt, 'options': this._config}),
})
.then((response) => response.json())
.then((answer) => editor.model.insertContent(
editor.model.insertContent( writer.createText(answer.text), range )
))
.then(() => this._hideUI()
)
});
const reader = response.body.getReader();
while (true) {
const {value, done} = await reader.read();
const text = new TextDecoder().decode(value);
if (done) break;
editor.model.insertContent(
writer.createText(text)
);
}
} );
} );
......
......@@ -29,22 +29,28 @@ export default class TranslateCommand extends Command {
}
const prompt = 'Translate the selected text into ' + formView.languageInputView.fieldView.element.value + ': ' + selectedText;
this._hideUI();
// @todo Need to have an AJAX indicator while the API waits for a response.
// @todo add error handling
editor.model.change( writer => {
fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
editor.model.change(async writer => {
const response = await fetch(drupalSettings.path.baseUrl + 'api/openai-ckeditor/completion', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({'prompt': prompt, 'options': this._config}),
})
.then((response) => response.json())
.then((answer) => editor.model.insertContent(
editor.model.insertContent( writer.createText(answer.text), range )
))
.then(() => this._hideUI()
)
});
const reader = response.body.getReader();
while (true) {
const {value, done} = await reader.read();
const text = new TextDecoder().decode(value);
if (done) break;
editor.model.insertContent(
writer.createText(text)
);
}
} );
} );
......
......@@ -5,8 +5,8 @@ namespace Drupal\openai_ckeditor\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use OpenAI\Client;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Returns responses for CKEditor integration routes.
......@@ -42,10 +42,10 @@ class Completion implements ContainerInjectionInterface {
/**
* Builds the response.
*/
public function generate(Request $request): JsonResponse {
public function generate(Request $request) {
$data = json_decode($request->getContent());
$response = $this->client->completions()->create(
$stream = $this->client->completions()->createStreamed(
[
'model' => $data->options->model ?? 'text-davinci-003',
'prompt' => trim($data->prompt),
......@@ -54,15 +54,13 @@ class Completion implements ContainerInjectionInterface {
]
);
$response = $response->toArray();
// @todo: could we have a setting to 'save' prompt responses like the log analyzer does?
return new JsonResponse(
[
'text' => trim($response["choices"][0]["text"]),
],
);
return new StreamedResponse(function () use ($stream) {
foreach ($stream as $data) {
echo $data->choices[0]->text;
ob_flush();
flush();
}
}, 200, ['Content-Type' => 'text/plain']);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment