Skip to content

Commit

Permalink
feat: prompt cell output can be saved to variables
Browse files Browse the repository at this point in the history
  • Loading branch information
BroKun authored and sunshinesmilelk committed Jan 22, 2024
1 parent 05e113b commit 7a8b983
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/libro-prompt-cell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@
"@difizen/libro-codemirror": "^0.1.14",
"@difizen/libro-common": "^0.1.14",
"@difizen/libro-core": "^0.1.14",
"@ant-design/icons": "^5.1.0",
"@difizen/mana-l10n": "latest",
"@difizen/mana-app": "latest",
"classnames": "^2.3.2",
"highlight.js": "^11.8.0",
"marked": "^5.1.1",
"marked-highlight": "^2.0.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/libro-prompt-cell/src/index.less
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.libro-prompt-cell-header {
display: flex;
align-items: center;
}

.libro-llm-hljs {
overflow-x: auto;
white-space: pre-wrap !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { singleton } from '@difizen/mana-app';

export interface PromptDecodedFormatter extends DefaultDecodedFormatter {
modelType?: string;
variableName?: string;
}

@singleton({ contrib: FormatterContribution })
Expand All @@ -29,6 +30,7 @@ export class FormatterPromptMagicContribution
const promptObj = {
model_name: source.modelType || 'chatgpt',
prompt: source.value,
variable_name: source.variableName,
};
const encodeValue = `%%prompt \n${JSON.stringify(promptObj)}`;
return {
Expand All @@ -41,14 +43,15 @@ export class FormatterPromptMagicContribution

decode = (formatterValue: DefaultEncodedFormatter) => {
const value = concatMultilineString(formatterValue.source);

if (value.startsWith('%%prompt \n')) {
const run = value.split('%%prompt \n')[1];
const runValue = JSON.parse(run);
const codeValue = runValue.prompt;
const modelType = runValue.model_name;
const variableName = runValue.variable_name;
return {
value: codeValue,
variableName,
modelType,
};
}
Expand Down
4 changes: 4 additions & 0 deletions packages/libro-prompt-cell/src/prompt-cell-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export class LibroPromptCellModel
kernelExecuting = false;

modelType: string;

@prop()
variableName: string;

@prop()
executing: boolean;
@prop()
Expand Down
52 changes: 42 additions & 10 deletions packages/libro-prompt-cell/src/prompt-cell-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ import { Deferred } from '@difizen/mana-app';
import { Select, Tag } from 'antd';
import React, { useEffect, useState } from 'react';

import type { LibroPromptCellModel } from './prompt-cell-model.js';
import { LibroPromptCellModel } from './prompt-cell-model.js';
import { PromptScript } from './prompt-cell-script.js';
import { VariableNameInput } from './variable-handler/index.js';
import './index.less';

export interface ChatItem {
name: string;
Expand Down Expand Up @@ -99,7 +101,7 @@ const SelectionItemLabel: React.FC<{ item: ChatItem }> = (props: {
</span>
);
};
const CellEditor: React.FC = () => {
const CellEditorRaw: React.FC = () => {
const instance = useInject<LibroPromptCellView>(ViewInstance);
useEffect(() => {
if (instance.editorView?.editor) {
Expand All @@ -109,13 +111,15 @@ const CellEditor: React.FC = () => {
return <>{instance.editorView && <ViewRender view={instance.editorView} />}</>;
};

export const CellEditorMemo = React.memo(CellEditor);
export const CellEditor = React.memo(CellEditorRaw);

const PropmtEditorViewComponent = React.forwardRef<HTMLDivElement>(
function MaxPropmtEditorViewComponent(props, ref) {
const instance = useInject<LibroPromptCellView>(ViewInstance);
const [selectedModel, setSelectedModel] = useState<string>('暂无内置模型');
useEffect(() => {
// TODO: Data initialization should not depend on view initialization, which causes limitations in usage scenarios and multiple renderings.
instance.model.variableName = instance.model.decodeObject['variableName'];
instance
.updateChatList()
.then(() => {
Expand All @@ -138,11 +142,7 @@ const PropmtEditorViewComponent = React.forwardRef<HTMLDivElement>(
}, []);

const handleChange = (value: string) => {
instance.model.modelType = value;
instance.model.decodeObject = {
...instance.model.decodeObject,
modelType: value,
};
instance.handleModelNameChange(value);
setSelectedModel(value);
};

Expand All @@ -166,8 +166,13 @@ const PropmtEditorViewComponent = React.forwardRef<HTMLDivElement>(
}}
/>
</div>
<VariableNameInput
value={instance.model.variableName}
checkVariableNameAvailable={instance.checkVariableNameAvailable}
handleVariableNameChange={instance.handleVariableNameChange}
/>
</div>
<CellEditorMemo />
<CellEditor />
</div>
);
},
Expand Down Expand Up @@ -464,7 +469,10 @@ export class LibroPromptCellView extends LibroExecutableCellView {

updateChatList = async () => {
return this.fetch(
{ code: this.promptScript.toList, store_history: false },
{
code: this.promptScript.toList,
store_history: false,
},
this.handleQueryResponse,
);
};
Expand Down Expand Up @@ -505,4 +513,28 @@ export class LibroPromptCellView extends LibroExecutableCellView {
break;
}
};

checkVariableNameAvailable = (variableName: string) => {
return (
this.parent.model.cells.findIndex(
(cell) =>
cell.model instanceof LibroPromptCellModel &&
cell.model.variableName === variableName,
) > -1
);
};
handleModelNameChange = (key: string) => {
this.model.decodeObject = {
...this.model.decodeObject,
modelType: key,
};
this.model.modelType = key;
};
handleVariableNameChange = (variableName: string) => {
this.model.decodeObject = {
...this.model.decodeObject,
variableName: variableName,
};
this.model.variableName = variableName;
};
}
46 changes: 46 additions & 0 deletions packages/libro-prompt-cell/src/variable-handler/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.libro-variable-name-input {
border-left: 1px solid var(--mana-color-border);

svg {
margin-left: 4px;
}

&-label {
padding-left: 16px;
color: var(--mana-libro-cell-header-title);
font-weight: 400;
font-size: 14px;
font-family: SFProText;
line-height: 36px;
letter-spacing: 0;
}

&-input {
width: 120px;
height: 32px;
border: 1px solid #d6d8da;
border-radius: 6px;
box-shadow: unset;
}

&-popover {
vertical-align: middle;
}

&-warning-text {
margin-top: 4px;
color: #faad14;
}

&-actions {
padding: 2px 0;

span + span {
margin-left: 8px;
}

span {
color: #1890ff;
}
}
}
1 change: 1 addition & 0 deletions packages/libro-prompt-cell/src/variable-handler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './variable-name-input.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { EditFilled } from '@ant-design/icons';
import { LirboContextKey } from '@difizen/libro-core';
import { useInject } from '@difizen/mana-app';
import { l10n } from '@difizen/mana-l10n';
import { Input, Popover } from 'antd';
import classNames from 'classnames';
import type { FC } from 'react';
import { useEffect } from 'react';
import { useRef } from 'react';
import { useCallback, useState } from 'react';
import './index.less';

interface VariableNameInputPopoverContentProps {
value: string;
handleVariableNameChange: (variableName: string) => void;
checkVariableNameAvailable: (variableName: string) => boolean;
cancel: () => void;
}

export const VariableNameInputPopoverContent: FC<
VariableNameInputPopoverContentProps
> = (props: VariableNameInputPopoverContentProps) => {
const { value, handleVariableNameChange, checkVariableNameAvailable, cancel } = props;
const [variableNameAvailable, setVariableNameAvailable] = useState(true);
const [variableName, setVariableName] = useState(value);

useEffect(() => {
setVariableName(value);
}, [value]);

const handleValueChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
if (checkVariableNameAvailable(e.target.value)) {
setVariableNameAvailable(false);
} else {
setVariableNameAvailable(true);
}
setVariableName(e.target.value);
},
[checkVariableNameAvailable],
);

const handValueSave = useCallback(() => {
handleVariableNameChange(variableName);
cancel();
}, [variableName, handleVariableNameChange, cancel]);

return (
<>
<Input
status={`${variableNameAvailable ? '' : 'warning'}`}
className="libro-variable-name-input-component"
onChange={handleValueChange}
value={variableName}
/>

{!variableNameAvailable && (
<span className="libro-variable-name-input-warning-text">
{l10n.t('当前变量名已存在')}
</span>
)}

<div className="libro-variable-name-input-actions">
<span onClick={cancel}>{l10n.t('取消')}</span>
<span onClick={handValueSave}>{l10n.t('保存')}</span>
</div>
</>
);
};

interface VariableNameInputProps {
value: string;
handleVariableNameChange: (variableName: string) => void;
checkVariableNameAvailable: (variableName: string) => boolean;
classname?: string;
}
const variableNameInputCls = 'libro-variable-name-input';
export const VariableNameInput: FC<VariableNameInputProps> = (
props: VariableNameInputProps,
) => {
const { value } = props;
const [popoverVisible, setPopoverVisible] = useState(false);
const contextKey = useInject(LirboContextKey);
const ref = useRef<HTMLDivElement>(null);
return (
<div className={classNames(variableNameInputCls, props.classname)} ref={ref}>
<span className="libro-variable-name-input-label">Save: </span>
<span className="libro-variable-name-input-value">{value || '...'}</span>
<span className="libro-variable-name-input-popover">
<Popover
content={
<VariableNameInputPopoverContent
{...props}
cancel={() => {
setPopoverVisible(false);
}}
/>
}
placement="bottomLeft"
open={popoverVisible}
onOpenChange={(visible) => {
if (visible) {
contextKey.disableCommandMode();
} else {
contextKey.enableCommandMode();
}
setPopoverVisible(visible);
}}
getPopupContainer={() => {
return ref.current?.getElementsByClassName(
variableNameInputCls,
)[0] as HTMLElement;
}}
trigger="click"
>
<EditFilled />
</Popover>
</span>
</div>
);
};

0 comments on commit 7a8b983

Please sign in to comment.