diff --git a/package.json b/package.json index 87c9a49..89d7709 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qiskit-code-assistant-jupyterlab", - "version": "0.4.0", + "version": "0.5.0", "description": "AI Autocomplete JupyterLab extension for Qiskit Code Assistant (Beta)", "keywords": [ "jupyter", diff --git a/qiskit_code_assistant_jupyterlab/handlers.py b/qiskit_code_assistant_jupyterlab/handlers.py index eabf053..f8aa211 100644 --- a/qiskit_code_assistant_jupyterlab/handlers.py +++ b/qiskit_code_assistant_jupyterlab/handlers.py @@ -88,7 +88,10 @@ def convert_openai(model): class ServiceUrlHandler(APIHandler): @tornado.web.authenticated def get(self): - self.finish(json.dumps({"url": runtime_configs["service_url"]})) + self.finish(json.dumps({ + "url": runtime_configs["service_url"], + "is_openai": runtime_configs["is_openai"] + })) @tornado.web.authenticated def post(self): @@ -102,7 +105,10 @@ def post(self): except (requests.exceptions.JSONDecodeError, KeyError): runtime_configs["is_openai"] = True finally: - self.finish(json.dumps({"url": runtime_configs["service_url"]})) + self.finish(json.dumps({ + "url": runtime_configs["service_url"], + "is_openai": runtime_configs["is_openai"] + })) class TokenHandler(APIHandler): @@ -185,8 +191,7 @@ class DisclaimerHandler(APIHandler): @tornado.web.authenticated def get(self, id): if runtime_configs["is_openai"]: - self.set_status(501, "Not implemented") - self.finish() + self.finish(json.dumps({"accepted": "true"})) else: url = url_path_join(runtime_configs["service_url"], "model", id, "disclaimer") @@ -204,8 +209,7 @@ class DisclaimerAcceptanceHandler(APIHandler): @tornado.web.authenticated def post(self, id): if runtime_configs["is_openai"]: - self.set_status(501, "Not implemented") - self.finish() + self.finish(json.dumps({"success": "true"})) else: url = url_path_join( runtime_configs["service_url"], "disclaimer", id, "acceptance" @@ -283,16 +287,19 @@ def post(self, id): class FeedbackHandler(APIHandler): @tornado.web.authenticated def post(self): - url = url_path_join(runtime_configs["service_url"], "feedback") - - try: - r = requests.post(url, headers=get_header(), json=self.get_json_body()) - r.raise_for_status() - except requests.exceptions.HTTPError as err: - self.set_status(err.response.status_code) - self.finish(json.dumps(err.response.json())) + if runtime_configs["is_openai"]: + self.finish(json.dumps({"message": "Feedback not supported for this service"})) else: - self.finish(json.dumps(r.json())) + url = url_path_join(runtime_configs["service_url"], "feedback") + + try: + r = requests.post(url, headers=get_header(), json=self.get_json_body()) + r.raise_for_status() + except requests.exceptions.HTTPError as err: + self.set_status(err.response.status_code) + self.finish(json.dumps(err.response.json())) + else: + self.finish(json.dumps(r.json())) def setup_handlers(web_app): diff --git a/src/QiskitCompletionProvider.ts b/src/QiskitCompletionProvider.ts index 3f54d0a..a150ffe 100644 --- a/src/QiskitCompletionProvider.ts +++ b/src/QiskitCompletionProvider.ts @@ -39,6 +39,8 @@ const FEEDBACK_COMMAND = 'qiskit-code-assistant:prompt-feedback'; export let lastPrompt: ICompletionReturn | undefined = undefined; +export const wipeLastPrompt = () => (lastPrompt = undefined); + function getInputText(text: string, widget: Widget): string { const cellsContents: string[] = []; diff --git a/src/StatusBarWidget.ts b/src/StatusBarWidget.ts index 7959c7e..dcf69cb 100644 --- a/src/StatusBarWidget.ts +++ b/src/StatusBarWidget.ts @@ -19,6 +19,7 @@ import { Message } from '@lumino/messaging'; import { refreshIcon } from '@jupyterlab/ui-components'; import { Widget } from '@lumino/widgets'; +import { wipeLastPrompt } from './QiskitCompletionProvider'; import { showDisclaimer } from './service/disclaimer'; import { getCurrentModel, @@ -70,9 +71,11 @@ export class StatusBarWidget extends Widget { async onClick() { await checkAPIToken().then(() => { const modelsList = getModelsList(); + const dropDownList = [...modelsList.map(m => m.display_name)]; InputDialog.getItem({ title: 'Select a Model', - items: [...modelsList.map(m => m.display_name)] + items: dropDownList, + current: dropDownList.indexOf(getCurrentModel()?.display_name || '') }).then(result => { if (result.button.accept) { const model = modelsList.find(m => m.display_name === result.value); @@ -80,6 +83,7 @@ export class StatusBarWidget extends Widget { if (model) { showDisclaimer(model._id).then(accepted => { if (accepted) { + wipeLastPrompt(); setCurrentModel(model); } }); diff --git a/src/index.ts b/src/index.ts index 82f0c8c..414f90f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,7 +28,8 @@ import { StatusBarWidget } from './StatusBarWidget'; import { lastPrompt, QiskitCompletionProvider, - QiskitInlineCompletionProvider + QiskitInlineCompletionProvider, + wipeLastPrompt } from './QiskitCompletionProvider'; import { postServiceUrl } from './service/api'; import { getFeedbackStatusBarWidget, getFeedback } from './service/feedback'; @@ -73,10 +74,21 @@ const plugin: JupyterFrontEndPlugin = { const settings = await settingRegistry.load(plugin.id); console.debug(EXTENSION_ID + ' settings loaded:', settings.composite); - postServiceUrl(settings.composite['serviceUrl'] as string); + let is_openai = false; + + postServiceUrl(settings.composite['serviceUrl'] as string).then( + response => { + is_openai = response.is_openai; + wipeLastPrompt(); + } + ); settings.changed.connect(() => - postServiceUrl(settings.composite['serviceUrl'] as string).then(() => - refreshModelsList() + postServiceUrl(settings.composite['serviceUrl'] as string).then( + response => { + is_openai = response.is_openai; + wipeLastPrompt(); + refreshModelsList(); + } ) ); @@ -87,7 +99,8 @@ const plugin: JupyterFrontEndPlugin = { statusBar.registerStatusItem(EXTENSION_ID + ':feedback', { item: getFeedbackStatusBarWidget(), - align: 'left' + align: 'left', + isActive: () => !is_openai }); const statusBarWidget = new StatusBarWidget(); @@ -104,11 +117,13 @@ const plugin: JupyterFrontEndPlugin = { label: 'Give feedback for the Qiskit Code Assistant', icon: feedbackIcon, execute: () => getFeedback(), - isEnabled: () => lastPrompt !== undefined, + isEnabled: () => !is_openai && lastPrompt !== undefined, isVisible: () => + !is_openai && ['code', 'markdown'].includes( notebookTracker.activeCell?.model.type || '' - ) && lastPrompt !== undefined + ) && + lastPrompt !== undefined }); app.commands.addCommand(CommandIDs.updateApiToken, { diff --git a/src/service/api.ts b/src/service/api.ts index 1fc6c1e..95abaab 100644 --- a/src/service/api.ts +++ b/src/service/api.ts @@ -22,7 +22,8 @@ import { IModelDisclaimer, IModelInfo, IModelPromptResponse, - IResponseMessage + IResponseMessage, + IServiceResponse } from '../utils/schema'; const AUTH_ERROR_CODES = [401, 403, 422]; @@ -40,14 +41,17 @@ async function notifyInvalid(response: Response): Promise { } // POST /service -export async function postServiceUrl(newUrl: string): Promise { +export async function postServiceUrl( + newUrl: string +): Promise { return await requestAPI('service', { method: 'POST', body: JSON.stringify({ url: newUrl }) }).then(response => { if (response.ok) { - response.json().then(json => { + return response.json().then(json => { console.debug('Updated service URL:', json.url); + return json; }); } else { console.error( diff --git a/src/utils/schema.ts b/src/utils/schema.ts index 87df69e..c9b2833 100644 --- a/src/utils/schema.ts +++ b/src/utils/schema.ts @@ -97,3 +97,8 @@ export interface IFeedbackForm { input?: string; output?: string; } + +export interface IServiceResponse { + url: string; + is_openai: boolean; +} diff --git a/yarn.lock b/yarn.lock index 219a752..03d8dde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1506,8 +1506,8 @@ __metadata: linkType: hard "@rjsf/core@npm:^5.13.4": - version: 5.22.3 - resolution: "@rjsf/core@npm:5.22.3" + version: 5.22.4 + resolution: "@rjsf/core@npm:5.22.4" dependencies: lodash: ^4.17.21 lodash-es: ^4.17.21 @@ -1517,13 +1517,13 @@ __metadata: peerDependencies: "@rjsf/utils": ^5.22.x react: ^16.14.0 || >=17 - checksum: 79aba03d150bd5cd1ee7051aa7036e4bdbc80528974bd88e8360f912233cdb64e5fb64f377175eb76894ce3602e8121cbe082a7a0c82d3834f3f1ffe0041d7e4 + checksum: cc2d6b51959be277f727a0c8398c699bc4068cb63f3f096ddd051c35e5ba685beb5745c281d81e3906f1c92329e9468bc35f81244e48026794318c98b884dac7 languageName: node linkType: hard "@rjsf/utils@npm:^5.13.4": - version: 5.22.3 - resolution: "@rjsf/utils@npm:5.22.3" + version: 5.22.4 + resolution: "@rjsf/utils@npm:5.22.4" dependencies: json-schema-merge-allof: ^0.8.1 jsonpointer: ^5.0.1 @@ -1532,7 +1532,7 @@ __metadata: react-is: ^18.2.0 peerDependencies: react: ^16.14.0 || >=17 - checksum: 3acf008c7e655f7b88aa01285e4157289da274c9a39415b8c3dcfd87bd17c1c520660bd4439c6c2ceb7c0914932a8197bd54c042a1ddbb7c4649a7c42a9be778 + checksum: 7dddc74b910fb7b87ebddc564879126b100f14eee853c0fbc390faa6c5f944a70023f34925a360aa92915f4e9c56bccf57141db9fe7b3c6a97348d886794bab8 languageName: node linkType: hard @@ -2278,9 +2278,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001669": - version: 1.0.30001678 - resolution: "caniuse-lite@npm:1.0.30001678" - checksum: b1690df8b306b99a1ec84dc29cbdc893b569fcdc232324a9d3ebd67ce184a353ffdea93270bb6c20e3372bec8b5f6922c04a464fbd1cdeff662d459639f22a53 + version: 1.0.30001680 + resolution: "caniuse-lite@npm:1.0.30001680" + checksum: 2641d2b18c5ab0a6663cb350c5adc81e5ede1a7677d1c7518a8053ada87bf6f206419e1820a2608f76fa5e4f7bea327cbe47df423783e571569a88c0ea645270 languageName: node linkType: hard @@ -2724,9 +2724,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.41": - version: 1.5.53 - resolution: "electron-to-chromium@npm:1.5.53" - checksum: 4d46bc8a61527fe591b887cb4953c2119709eebe19ae4957293104ce6441134cebe6f20995d813b7b4b3226e5d0f5714cb22e8b28f86431fc4e6bb0b06215dc3 + version: 1.5.56 + resolution: "electron-to-chromium@npm:1.5.56" + checksum: ef8213e3531715d48ca7c61e4b70532d57616271b56642d212297c72ba984bf57621c32d04eb56c19bfb90cf17d421875e6f334deddd191edf28515bebfb9061 languageName: node linkType: hard @@ -4532,9 +4532,9 @@ __metadata: linkType: hard "object-inspect@npm:^1.13.1": - version: 1.13.2 - resolution: "object-inspect@npm:1.13.2" - checksum: 9f850b3c045db60e0e97746e809ee4090d6ce62195af17dd1e9438ac761394a7d8ec4f7906559aea5424eaf61e35d3e53feded2ccd5f62fcc7d9670d3c8eb353 + version: 1.13.3 + resolution: "object-inspect@npm:1.13.3" + checksum: 8c962102117241e18ea403b84d2521f78291b774b03a29ee80a9863621d88265ffd11d0d7e435c4c2cea0dc2a2fbf8bbc92255737a05536590f2df2e8756f297 languageName: node linkType: hard @@ -4736,7 +4736,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0, picocolors@npm:^1.1.0": +"picocolors@npm:^1.0.0, picocolors@npm:^1.1.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 @@ -4792,26 +4792,26 @@ __metadata: linkType: hard "postcss-modules-local-by-default@npm:^4.0.5": - version: 4.0.5 - resolution: "postcss-modules-local-by-default@npm:4.0.5" + version: 4.1.0 + resolution: "postcss-modules-local-by-default@npm:4.1.0" dependencies: icss-utils: ^5.0.0 - postcss-selector-parser: ^6.0.2 + postcss-selector-parser: ^7.0.0 postcss-value-parser: ^4.1.0 peerDependencies: postcss: ^8.1.0 - checksum: ca9b01f4a0a3dfb33e016299e2dfb7e85c3123292f7aec2efc0c6771b9955648598bfb4c1561f7ee9732fb27fb073681233661b32eef98baab43743f96735452 + checksum: 64ac4803c21dd82e227179cf0a8489c645ea99a8c514475da028c9afe5d5b915485d00d8efbe94295d688a23a172965cc15f20d550168d1fed272dbdbbe053f0 languageName: node linkType: hard "postcss-modules-scope@npm:^3.2.0": - version: 3.2.0 - resolution: "postcss-modules-scope@npm:3.2.0" + version: 3.2.1 + resolution: "postcss-modules-scope@npm:3.2.1" dependencies: - postcss-selector-parser: ^6.0.4 + postcss-selector-parser: ^7.0.0 peerDependencies: postcss: ^8.1.0 - checksum: 2ffe7e98c1fa993192a39c8dd8ade93fc4f59fbd1336ce34fcedaee0ee3bafb29e2e23fb49189256895b30e4f21af661c6a6a16ef7b17ae2c859301e4a4459ae + checksum: 085f65863bb7d8bf08209a979ceb22b2b07bb466574e0e698d34aaad832d614957bb05f2418348a14e4035f65e23b2be2951369d26ea429dd5762c6a020f0f7c languageName: node linkType: hard @@ -4842,7 +4842,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.13, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": +"postcss-selector-parser@npm:^6.0.13": version: 6.1.2 resolution: "postcss-selector-parser@npm:6.1.2" dependencies: @@ -4852,6 +4852,16 @@ __metadata: languageName: node linkType: hard +"postcss-selector-parser@npm:^7.0.0": + version: 7.0.0 + resolution: "postcss-selector-parser@npm:7.0.0" + dependencies: + cssesc: ^3.0.0 + util-deprecate: ^1.0.2 + checksum: f906b7449fcbe9fa6ae739b6fc324ee3c6201aaf5224f26da27de64ccba68d878d734dd182a467881e463f7ede08972d0129b0cc4d6b671d78c6492cddcef154 + languageName: node + linkType: hard + "postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -4860,13 +4870,13 @@ __metadata: linkType: hard "postcss@npm:^8.3.11, postcss@npm:^8.4.28, postcss@npm:^8.4.33": - version: 8.4.47 - resolution: "postcss@npm:8.4.47" + version: 8.4.49 + resolution: "postcss@npm:8.4.49" dependencies: nanoid: ^3.3.7 - picocolors: ^1.1.0 + picocolors: ^1.1.1 source-map-js: ^1.2.1 - checksum: f78440a9d8f97431dd2ab1ab8e1de64f12f3eff38a3d8d4a33919b96c381046a314658d2de213a5fa5eb296b656de76a3ec269fdea27f16d5ab465b916a0f52c + checksum: eb5d6cbdca24f50399aafa5d2bea489e4caee4c563ea1edd5a2485bc5f84e9ceef3febf170272bc83a99c31d23a316ad179213e853f34c2a7a8ffa534559d63a languageName: node linkType: hard