diff --git a/src/model.ts b/src/model.ts index c03275c3..c8237bc4 100644 --- a/src/model.ts +++ b/src/model.ts @@ -3606,20 +3606,36 @@ class CommandRunner { GlobalModel.submitCommand("history", "viewall", null, kwargs, true); } - telemetryOff() { - GlobalModel.submitCommand("telemetry", "off", null, {"nohist": "1"}, true); + telemetryOff(interactive : boolean) : Promise { + return GlobalModel.submitCommand("telemetry", "off", null, {"nohist": "1"}, interactive); } - telemetryOn() { - GlobalModel.submitCommand("telemetry", "on", null, {"nohist": "1"}, true); + telemetryOn(interactive : boolean) : Promise { + return GlobalModel.submitCommand("telemetry", "on", null, {"nohist": "1"}, interactive); } - setTermFontSize(fsize : number) { + setTermFontSize(fsize : number, interactive : boolean) : Promise { let kwargs = { "nohist": "1", "termfontsize": String(fsize), }; - GlobalModel.submitCommand("client", "set", null, kwargs, true); + return GlobalModel.submitCommand("client", "set", null, kwargs, interactive); + } + + setClientOpenAISettings(opts : {model? : string, apitoken? : string, maxtokens? : string}) : Promise { + let kwargs = { + "nohist": "1", + }; + if (opts.model != null) { + kwargs["openaimodel"] = opts.model; + } + if (opts.apitoken != null) { + kwargs["openaiapitoken"] = opts.apitoken; + } + if (opts.maxtokens != null) { + kwargs["openaimaxtokens"] = opts.maxtokens; + } + return GlobalModel.submitCommand("client", "set", null, kwargs, false); } clientAcceptTos() : void { diff --git a/src/ptydata.ts b/src/ptydata.ts index a4f962ba..0d57cbbe 100644 --- a/src/ptydata.ts +++ b/src/ptydata.ts @@ -97,14 +97,17 @@ class PacketDataBuffer extends PtyDataBuffer { console.log("invalid line packet", line); } } + let packet : any = null; try { - let packet = JSON.parse(packetStr); - this.callback(packet); + packet = JSON.parse(packetStr); } catch (e) { - console.log("invalid line packet (bad json)", line); + console.log("invalid line packet (bad json)", line, e); return; } + if (packet != null) { + this.callback(packet); + } } parseData() { diff --git a/src/settings.tsx b/src/settings.tsx index 59aba90f..c83d5e82 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -18,6 +18,7 @@ type CV = mobx.IComputedValue; const RemotePtyRows = 8; const RemotePtyCols = 80; +const APITokenSentinel = "--apitoken--"; // @ts-ignore const VERSION = __PROMPT_VERSION__; @@ -178,13 +179,6 @@ class ScreenSettingsModal extends React.Component<{sessionId : string, screenId commandRtnHandler(prtn, this.errorMessage); } - @boundMethod - setErrorMessage(msg : string) : void { - mobx.action(() => { - this.errorMessage.set(msg); - })(); - } - @boundMethod dismissError() : void { mobx.action(() => { @@ -407,13 +401,6 @@ class SessionSettingsModal extends React.Component<{sessionId : string}, {}> { }); } - @boundMethod - setErrorMessage(msg : string) : void { - mobx.action(() => { - this.errorMessage.set(msg); - })(); - } - @boundMethod dismissError() : void { mobx.action(() => { @@ -610,14 +597,13 @@ class LineSettingsModal extends React.Component<{linenum : number}, {}> { @mobxReact.observer class ClientSettingsModal extends React.Component<{}, {}> { tempFontSize : OV; - tempTelemetry : OV; fontSizeDropdownActive : OV = mobx.observable.box(false, {name: "clientSettings-fontSizeDropdownActive"}); + errorMessage : OV = mobx.observable.box(null, {name: "ClientSettings-errorMessage"}); constructor(props : any) { super(props); let cdata = GlobalModel.clientData.get(); this.tempFontSize = mobx.observable.box(GlobalModel.termFontSize.get(), {name: "clientSettings-tempFontSize"}); - this.tempTelemetry = mobx.observable.box(!cdata.clientopts.notelemetry, {name: "clientSettings-telemetry"}); } @boundMethod @@ -628,31 +614,19 @@ class ClientSettingsModal extends React.Component<{}, {}> { } @boundMethod - handleOK() : void { + dismissError() : void { mobx.action(() => { - GlobalModel.clientSettingsModal.set(false); + this.errorMessage.set(null); })(); - let cdata = GlobalModel.clientData.get(); - let curTel = !cdata.clientopts.notelemetry; - if (this.tempTelemetry.get() != curTel) { - if (this.tempTelemetry.get()) { - GlobalCommandRunner.telemetryOn(); - } - else { - GlobalCommandRunner.telemetryOff(); - } - } - if (GlobalModel.termFontSize.get() != this.tempFontSize.get()) { - GlobalCommandRunner.setTermFontSize(this.tempFontSize.get()); - } } @boundMethod handleChangeFontSize(newFontSize : number) : void { - mobx.action(() => { - this.fontSizeDropdownActive.set(false); - this.tempFontSize.set(newFontSize); - })(); + if (GlobalModel.termFontSize.get() == newFontSize) { + return; + } + let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false); + commandRtnHandler(prtn, this.errorMessage); } @boundMethod @@ -664,9 +638,14 @@ class ClientSettingsModal extends React.Component<{}, {}> { @boundMethod handleChangeTelemetry(val : boolean) : void { - mobx.action(() => { - this.tempTelemetry.set(val); - })(); + let prtn : Promise = null; + if (val) { + prtn = GlobalCommandRunner.telemetryOn(false); + } + else { + prtn = GlobalCommandRunner.telemetryOff(false); + } + commandRtnHandler(prtn, this.errorMessage); } renderFontSizeDropdown() : any { @@ -693,8 +672,36 @@ class ClientSettingsModal extends React.Component<{}, {}> { ); } + @boundMethod + inlineUpdateOpenAIModel(newModel : string) : void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({model: newModel}); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + inlineUpdateOpenAIToken(newToken : string) : void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({apitoken: newToken}); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + inlineUpdateOpenAIMaxTokens(newMaxTokensStr : string) : void { + let prtn = GlobalCommandRunner.setClientOpenAISettings({maxtokens: newMaxTokensStr}); + commandRtnHandler(prtn, this.errorMessage); + } + + @boundMethod + setErrorMessage(msg : string) : void { + mobx.action(() => { + this.errorMessage.set(msg); + })(); + } + render() { let cdata : ClientDataType = GlobalModel.clientData.get(); + let openAIOpts = cdata.openaiopts ?? {}; + let apiTokenStr = (util.isBlank(openAIOpts.apitoken) ? "(not set)" : "********"); + let maxTokensStr = String(openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens); return (
@@ -743,13 +750,37 @@ class ClientSettingsModal extends React.Component<{}, {}> { Basic Telemetry
- +
+
+
+ OpenAI Token +
+
+ +
+
+
+
+ OpenAI Model +
+
+ +
+
+
+
+ OpenAI MaxTokens +
+
+ +
+
+
-
Cancel
-
OK
+
Close
diff --git a/src/sh2.less b/src/sh2.less index 49a712d1..ac1927f6 100644 --- a/src/sh2.less +++ b/src/sh2.less @@ -285,5 +285,13 @@ input[type=checkbox] { .openai-content-assistant { color: white; } + + .openai-role-error { + color: @term-bright-red; + } + + .openai-content-error { + color: @term-bright-red; + } } } diff --git a/src/types.ts b/src/types.ts index a11d1b4a..d0f3d4db 100644 --- a/src/types.ts +++ b/src/types.ts @@ -450,6 +450,14 @@ type ClientDataType = { cmdstoretype : "session" | "screen"; dbversion : number, migration? : ClientMigrationInfo; + openaiopts? : OpenAIOptsType, +}; + +type OpenAIOptsType = { + model? : string, + apitoken? : string, + maxtokens? : number, + maxchoices? : number, }; type ClientMigrationInfo = { @@ -603,6 +611,7 @@ type OpenAIPacketType = { usage : Record, index : number, text : string, + error : string, }; -export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate, ClientMigrationInfo, WebShareOpts, RemoteStatusTypeStrs, WebFullScreen, WebScreen, WebLine, WebCmd, RemoteTermContext, TermContextUnion, WebRemote, PtyDataUpdate, WebShareWSMessage, LineHeightChangeCallbackType, LineFactoryProps, LineInterface, RendererContainerType, RemoteViewType, CommandRtnType}; +export type {SessionDataType, LineType, RemoteType, RemoteStateType, RemoteInstanceType, HistoryItem, CmdRemoteStateType, FeCmdPacketType, TermOptsType, CmdStartPacketType, CmdDataType, ScreenDataType, ScreenOptsType, PtyDataUpdateType, ModelUpdateType, UpdateMessage, InfoType, CmdLineUpdateType, RemotePtrType, UIContextType, HistoryInfoType, HistoryQueryOpts, WatchScreenPacketType, TermWinSize, FeInputPacketType, RemoteInputPacketType, RemoteEditType, ContextMenuOpts, RendererContext, WindowSize, RendererModel, PtyDataType, BookmarkType, ClientDataType, PlaybookType, PlaybookEntryType, HistoryViewDataType, RenderModeType, AlertMessageType, HistorySearchParams, ScreenLinesType, FocusTypeStrs, HistoryTypeStrs, RendererOpts, RendererPluginType, SimpleBlobRendererComponent, RendererModelContainerApi, RendererModelInitializeParams, RendererOptsUpdate, ClientMigrationInfo, WebShareOpts, RemoteStatusTypeStrs, WebFullScreen, WebScreen, WebLine, WebCmd, RemoteTermContext, TermContextUnion, WebRemote, PtyDataUpdate, WebShareWSMessage, LineHeightChangeCallbackType, LineFactoryProps, LineInterface, RendererContainerType, RemoteViewType, CommandRtnType, OpenAIPacketType}; diff --git a/src/view/openai.tsx b/src/view/openai.tsx index 4e4fd184..a86543db 100644 --- a/src/view/openai.tsx +++ b/src/view/openai.tsx @@ -34,11 +34,13 @@ class OpenAIRendererModel { packetData : PacketDataBuffer; rawCmd : T.WebCmd; output : OV; + version : OV; constructor() { this.updateHeight_debounced = debounce(1000, this.updateHeight.bind(this)); this.packetData = new PacketDataBuffer(this.packetCallback); this.output = mobx.observable.box(null, {name: "openai-output"}); + this.version = mobx.observable.box(0); } initialize(params : T.RendererModelInitializeParams) : void { @@ -50,9 +52,7 @@ class OpenAIRendererModel { this.savedHeight = params.savedHeight; this.ptyDataSource = params.ptyDataSource; this.rawCmd = params.rawCmd; - if (this.isDone.get()) { - setTimeout(() => this.reload(0), 10); - } + setTimeout(() => this.reload(0), 10); } @boundMethod @@ -61,6 +61,15 @@ class OpenAIRendererModel { if (packet == null) { return; } + // console.log("got packet", packet); + if (packet.error != null) { + mobx.action(() => { + this.loadError.set(packet.error); + this.version.set(this.version.get()+1); + console.log("set error", this.loadError.get()); + })(); + return; + } if (packet.model != null && (packet.index ?? 0) == 0) { let output = { model: packet.model, @@ -75,12 +84,17 @@ class OpenAIRendererModel { } if ((packet.index ?? 0) == 0) { mobx.action(() => { + let output = this.output.get(); + if (output == null) { + return; + } if (packet.finish_reason != null) { this.output.get().finish_reason = packet.finish_reason; } if (packet.text != null) { this.output.get().message += packet.text; } + this.version.set(this.version.get()+1); })(); } } @@ -111,12 +125,13 @@ class OpenAIRendererModel { mobx.action(() => { this.isDone.set(true); })(); - this.reload(0); + // this.reload(0); } reload(delayMs : number) : void { mobx.action(() => { this.loading.set(true); + this.loadError.set(null); })(); let rtnp = this.ptyDataSource(this.context); if (rtnp == null) { @@ -129,7 +144,6 @@ class OpenAIRendererModel { this.receiveData(ptydata.pos, ptydata.data, "reload"); mobx.action(() => { this.loading.set(false); - this.loadError.set(null); })(); }, delayMs); }).catch((e) => { @@ -165,6 +179,18 @@ class OpenAIRenderer extends React.Component<{model : OpenAIRendererModel}> { ); } + renderError() { + let model : OpenAIRendererModel = this.props.model; + return ( +
+ [error] +
+ {model.loadError.get()} +
+
+ ); + } + renderOutput(cmd : T.WebCmd) { let output = this.props.model.output.get(); let message = ""; @@ -194,9 +220,21 @@ class OpenAIRenderer extends React.Component<{model : OpenAIRendererModel}> { let model : OpenAIRendererModel = this.props.model; let cmd = model.rawCmd; let styleVal : Record = null; - if (model.loading.get() && model.savedHeight >= 0) { + if (model.loading.get() && model.savedHeight >= 0 && model.isDone) { styleVal = {height: model.savedHeight}; } + console.log("render again", mobx.toJS(model.output.get())); + let version = model.version.get(); + let loadError = model.loadError.get(); + console.log(model.context.lineNum, "got load error", loadError); + if (loadError != null) { + return ( +
+ {this.renderPrompt(cmd)} + {this.renderError()} +
+ ); + } return (
{this.renderPrompt(cmd)}