holy shit

This commit is contained in:
Orion Reed 2024-07-20 01:20:47 +02:00
parent 76fc86b35f
commit 344bd352ad
7 changed files with 445 additions and 2 deletions

View File

@ -13,6 +13,7 @@
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "1.3.0",
"openai": "^4.52.7",
"partykit": "0.0.27",
"rbush": "^4.0.0",
"react": "^18.2.0",

View File

@ -7,9 +7,11 @@ import { SocialShapeTool } from "./SocialShapeTool";
import { CustomToolbar, overrides } from "./ui";
// import { getDocumentMeta, getUserId, getUsersInRoom, setDocumentMeta } from "./storeUtils";
import { registerDefaultPropagators } from "./propagators/ScopedPropagators";
import { PromptShape } from "./PromptShape";
import { PromptShapeTool } from "./PromptTool";
const shapeUtils = [SocialShapeUtil];
const tools = [SocialShapeTool];
const shapeUtils = [SocialShapeUtil, PromptShape];
const tools = [SocialShapeTool, PromptShapeTool];
const HOST_URL = import.meta.env.DEV

169
src/PromptShape.tsx Normal file
View File

@ -0,0 +1,169 @@
import {
BaseBoxShapeUtil,
HTMLContainer,
// TLArrowShape,
TLBaseShape,
TLGeoShape,
TLOnResizeHandler,
TLShape,
} from "tldraw"
import { getEdge } from "./propagators/tlgraph"
import { llm } from "./llm"
import { isShapeOfType } from "./propagators/utils"
import { ISocialShape } from "./SocialShapeUtil"
// import TextInput from "react-autocomplete-input"
// import "react-autocomplete-input/dist/bundle.css"
type IPrompt = TLBaseShape<
"prompt",
{
w: number
h: number
prompt: string
output: string
agentBinding: string | null
}
>
export class PromptShape extends BaseBoxShapeUtil<IPrompt> {
static override type = "prompt" as const
FIXED_HEIGHT = 50 as const
MIN_WIDTH = 150 as const
PADDING = 4 as const
getDefaultProps(): IPrompt["props"] {
return {
w: 300,
h: 50,
prompt: "",
output: "",
agentBinding: null,
}
}
override onResize: TLOnResizeHandler<IPrompt> = (
shape,
{ scaleX, initialShape },
) => {
const { x, y } = shape
const w = initialShape.props.w * scaleX
return {
x,
y,
props: {
...shape.props,
w: Math.max(Math.abs(w), this.MIN_WIDTH),
h: this.FIXED_HEIGHT,
},
}
}
component(shape: IPrompt) {
const arrowBindings = this.editor.getBindingsInvolvingShape(
shape.id,
"arrow",
)
const arrows = arrowBindings
.map((binding) => this.editor.getShape(binding.fromId))
const inputMap = arrows.reduce((acc, arrow) => {
const edge = getEdge(arrow, this.editor);
if (edge) {
const sourceShape = this.editor.getShape(edge.from);
if (sourceShape && edge.text) {
acc[edge.text] = sourceShape;
}
}
return acc;
}, {} as Record<string, TLShape>);
const generateText = async (prompt: string) => {
await llm('', prompt, (partial: string, done: boolean) => {
console.log("DONE??", done)
this.editor.updateShape<IPrompt>({
id: shape.id,
type: "prompt",
props: { output: partial, agentBinding: done ? null : 'someone' },
})
})
}
const handlePrompt = () => {
let processedPrompt = shape.props.prompt;
for (const [key, sourceShape] of Object.entries(inputMap)) {
const pattern = `{${key}}`;
if (processedPrompt.includes(pattern)) {
if (isShapeOfType<TLGeoShape>(sourceShape, 'geo')) {
processedPrompt = processedPrompt.replace(pattern, sourceShape.props.text);
} else if (isShapeOfType<ISocialShape>(sourceShape, 'social')) {
processedPrompt = processedPrompt.replace(pattern, sourceShape.props.value.toString());
}
}
}
console.log(processedPrompt);
generateText(processedPrompt)
};
return (
<HTMLContainer
style={{
borderRadius: 6,
border: "1px solid lightgrey",
padding: this.PADDING,
height: this.FIXED_HEIGHT,
width: shape.props.w,
pointerEvents: "all",
backgroundColor: "#efefef",
overflow: "visible",
display: "flex",
justifyContent: "center",
alignItems: "center",
outline: shape.props.agentBinding ? "2px solid orange" : "none",
}}
>
<input
style={{
width: "100%",
height: "100%",
overflow: "visible",
backgroundColor: "rgba(0, 0, 0, 0.05)",
border: "1px solid rgba(0, 0, 0, 0.05)",
borderRadius: 6 - this.PADDING,
fontSize: 16,
}}
type="text"
placeholder="Enter prompt..."
value={shape.props.prompt}
onChange={(text) => {
this.editor.updateShape<IPrompt>({
id: shape.id,
type: "prompt",
props: { prompt: text.target.value },
})
}}
/>
<button
style={{
width: 100,
height: "100%",
marginLeft: 5,
pointerEvents: "all",
}}
onPointerDown={(e) => {
e.stopPropagation()
}}
type="button"
onClick={handlePrompt}
>
Prompt
</button>
</HTMLContainer>
)
}
// [5]
indicator(shape: IPrompt) {
return <rect width={shape.props.w} height={shape.props.h} rx={5} />
}
}

9
src/PromptTool.ts Normal file
View File

@ -0,0 +1,9 @@
import { BaseBoxShapeTool } from 'tldraw'
export class PromptShapeTool extends BaseBoxShapeTool {
static override id = 'prompt'
static override initial = 'idle'
override shapeType = 'prompt'
}

31
src/llm.ts Normal file
View File

@ -0,0 +1,31 @@
import OpenAI from "openai";
const openai = new OpenAI({
apiKey: "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",
// apiKey: import.meta.env.VITE_OPENAI_API_KEY,
dangerouslyAllowBrowser: true,
});
export async function llm(
systemPrompt: string,
userPrompt: string,
onToken: (partialResponse: string, done: boolean) => void,
) {
console.log("System Prompt:", systemPrompt);
console.log("User Prompt:", userPrompt);
let partial = "";
const stream = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: 'You are a helpful assistant.' },
{ role: "user", content: userPrompt },
],
stream: true,
});
for await (const chunk of stream) {
partial += chunk.choices[0]?.delta?.content || "";
onToken(partial, false);
}
console.log("Generated:", partial);
onToken(partial, true);
}

View File

@ -22,6 +22,16 @@ export const overrides: TLUiOverrides = {
editor.setCurrentTool("social")
},
},
prompt: {
id: "prompt",
name: "Prompt",
icon: "code",
kbd: "p",
label: "Prompt",
onSelect: () => {
editor.setCurrentTool("prompt")
},
},
}
},
}

221
yarn.lock
View File

@ -1922,6 +1922,34 @@ __metadata:
languageName: node
linkType: hard
"@types/node-fetch@npm:^2.6.4":
version: 2.6.11
resolution: "@types/node-fetch@npm:2.6.11"
dependencies:
"@types/node": "npm:*"
form-data: "npm:^4.0.0"
checksum: 5283d4e0bcc37a5b6d8e629aee880a4ffcfb33e089f4b903b2981b19c623972d1e64af7c3f9540ab990f0f5c89b9b5dda19c5bcb37a8e177079e93683bfd2f49
languageName: node
linkType: hard
"@types/node@npm:*":
version: 20.14.11
resolution: "@types/node@npm:20.14.11"
dependencies:
undici-types: "npm:~5.26.4"
checksum: 5306becc0ff41d81b1e31524bd376e958d0741d1ce892dffd586b9ae0cb6553c62b0d62abd16da8bea6b9a2c17572d360450535d7c073794b0cef9cb4e39691e
languageName: node
linkType: hard
"@types/node@npm:^18.11.18":
version: 18.19.41
resolution: "@types/node@npm:18.19.41"
dependencies:
undici-types: "npm:~5.26.4"
checksum: fc078939cdec05ca60c557bff55d8d96c8e5695e925ee144d8c67efcd9717ed1ec1e18a476257f2dcc8382d1158dae22b24a423ef3cb833ff9f441a80e80c0e6
languageName: node
linkType: hard
"@types/prop-types@npm:*":
version: 15.7.5
resolution: "@types/prop-types@npm:15.7.5"
@ -2002,6 +2030,15 @@ __metadata:
languageName: node
linkType: hard
"abort-controller@npm:^3.0.0":
version: 3.0.0
resolution: "abort-controller@npm:3.0.0"
dependencies:
event-target-shim: "npm:^5.0.0"
checksum: 90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5
languageName: node
linkType: hard
"abstract-leveldown@npm:^6.2.1":
version: 6.3.0
resolution: "abstract-leveldown@npm:6.3.0"
@ -2053,6 +2090,15 @@ __metadata:
languageName: node
linkType: hard
"agentkeepalive@npm:^4.2.1":
version: 4.5.0
resolution: "agentkeepalive@npm:4.5.0"
dependencies:
humanize-ms: "npm:^1.2.1"
checksum: 394ea19f9710f230722996e156607f48fdf3a345133b0b1823244b7989426c16019a428b56c82d3eabef616e938812981d9009f4792ecc66bd6a59e991c62612
languageName: node
linkType: hard
"aggregate-error@npm:^3.0.0":
version: 3.1.0
resolution: "aggregate-error@npm:3.1.0"
@ -2134,6 +2180,13 @@ __metadata:
languageName: node
linkType: hard
"asynckit@npm:^0.4.0":
version: 0.4.0
resolution: "asynckit@npm:0.4.0"
checksum: d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d
languageName: node
linkType: hard
"balanced-match@npm:^1.0.0":
version: 1.0.2
resolution: "balanced-match@npm:1.0.2"
@ -2328,6 +2381,15 @@ __metadata:
languageName: node
linkType: hard
"combined-stream@npm:^1.0.8":
version: 1.0.8
resolution: "combined-stream@npm:1.0.8"
dependencies:
delayed-stream: "npm:~1.0.0"
checksum: 0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5
languageName: node
linkType: hard
"concurrently@npm:^8.2.0":
version: 8.2.0
resolution: "concurrently@npm:8.2.0"
@ -2425,6 +2487,13 @@ __metadata:
languageName: node
linkType: hard
"delayed-stream@npm:~1.0.0":
version: 1.0.0
resolution: "delayed-stream@npm:1.0.0"
checksum: d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19
languageName: node
linkType: hard
"detect-node-es@npm:^1.1.0":
version: 1.1.0
resolution: "detect-node-es@npm:1.1.0"
@ -2674,6 +2743,13 @@ __metadata:
languageName: node
linkType: hard
"event-target-shim@npm:^5.0.0":
version: 5.0.1
resolution: "event-target-shim@npm:5.0.1"
checksum: 0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b
languageName: node
linkType: hard
"eventemitter3@npm:^4.0.7":
version: 4.0.7
resolution: "eventemitter3@npm:4.0.7"
@ -2722,6 +2798,34 @@ __metadata:
languageName: node
linkType: hard
"form-data-encoder@npm:1.7.2":
version: 1.7.2
resolution: "form-data-encoder@npm:1.7.2"
checksum: 56553768037b6d55d9de524f97fe70555f0e415e781cb56fc457a68263de3d40fadea2304d4beef2d40b1a851269bd7854e42c362107071892cb5238debe9464
languageName: node
linkType: hard
"form-data@npm:^4.0.0":
version: 4.0.0
resolution: "form-data@npm:4.0.0"
dependencies:
asynckit: "npm:^0.4.0"
combined-stream: "npm:^1.0.8"
mime-types: "npm:^2.1.12"
checksum: cb6f3ac49180be03ff07ba3ff125f9eba2ff0b277fb33c7fc47569fc5e616882c5b1c69b9904c4c4187e97dd0419dd03b134174756f296dec62041e6527e2c6e
languageName: node
linkType: hard
"formdata-node@npm:^4.3.2":
version: 4.4.1
resolution: "formdata-node@npm:4.4.1"
dependencies:
node-domexception: "npm:1.0.0"
web-streams-polyfill: "npm:4.0.0-beta.3"
checksum: 74151e7b228ffb33b565cec69182694ad07cc3fdd9126a8240468bb70a8ba66e97e097072b60bcb08729b24c7ce3fd3e0bd7f1f80df6f9f662b9656786e76f6a
languageName: node
linkType: hard
"fs-minipass@npm:^2.0.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
@ -2828,6 +2932,7 @@ __metadata:
"@types/react-dom": "npm:^18.2.7"
"@vitejs/plugin-react": "npm:^4.0.3"
concurrently: "npm:^8.2.0"
openai: "npm:^4.52.7"
partykit: "npm:0.0.27"
rbush: "npm:^4.0.0"
react: "npm:^18.2.0"
@ -2935,6 +3040,15 @@ __metadata:
languageName: node
linkType: hard
"humanize-ms@npm:^1.2.1":
version: 1.2.1
resolution: "humanize-ms@npm:1.2.1"
dependencies:
ms: "npm:^2.0.0"
checksum: f34a2c20161d02303c2807badec2f3b49cbfbbb409abd4f95a07377ae01cfe6b59e3d15ac609cffcd8f2521f0eb37b7e1091acf65da99aa2a4f1ad63c21e7e7a
languageName: node
linkType: hard
"iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
@ -3360,6 +3474,22 @@ __metadata:
languageName: node
linkType: hard
"mime-db@npm:1.52.0":
version: 1.52.0
resolution: "mime-db@npm:1.52.0"
checksum: 0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa
languageName: node
linkType: hard
"mime-types@npm:^2.1.12":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
mime-db: "npm:1.52.0"
checksum: 82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2
languageName: node
linkType: hard
"mimic-fn@npm:^2.1.0":
version: 2.1.0
resolution: "mimic-fn@npm:2.1.0"
@ -3496,6 +3626,13 @@ __metadata:
languageName: node
linkType: hard
"ms@npm:^2.0.0":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
languageName: node
linkType: hard
"mustache@npm:^4.2.0":
version: 4.2.0
resolution: "mustache@npm:4.2.0"
@ -3537,6 +3674,27 @@ __metadata:
languageName: node
linkType: hard
"node-domexception@npm:1.0.0":
version: 1.0.0
resolution: "node-domexception@npm:1.0.0"
checksum: 5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b
languageName: node
linkType: hard
"node-fetch@npm:^2.6.7":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
whatwg-url: "npm:^5.0.0"
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
checksum: b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
languageName: node
linkType: hard
"node-gyp-build@npm:~4.1.0":
version: 4.1.1
resolution: "node-gyp-build@npm:4.1.1"
@ -3604,6 +3762,24 @@ __metadata:
languageName: node
linkType: hard
"openai@npm:^4.52.7":
version: 4.52.7
resolution: "openai@npm:4.52.7"
dependencies:
"@types/node": "npm:^18.11.18"
"@types/node-fetch": "npm:^2.6.4"
abort-controller: "npm:^3.0.0"
agentkeepalive: "npm:^4.2.1"
form-data-encoder: "npm:1.7.2"
formdata-node: "npm:^4.3.2"
node-fetch: "npm:^2.6.7"
web-streams-polyfill: "npm:^3.2.1"
bin:
openai: bin/cli
checksum: 9689b08ee26bad4834d5b8fb57d05a5a956450b699f0b78d56fc3c89304ef6ac621c3458fa269fb059c33e871faaaecd1f4899287dfbc1ac1039228b247260cd
languageName: node
linkType: hard
"p-map@npm:^4.0.0":
version: 4.0.0
resolution: "p-map@npm:4.0.0"
@ -4144,6 +4320,13 @@ __metadata:
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
languageName: node
linkType: hard
"tree-kill@npm:^1.2.2":
version: 1.2.2
resolution: "tree-kill@npm:1.2.2"
@ -4187,6 +4370,13 @@ __metadata:
languageName: node
linkType: hard
"undici-types@npm:~5.26.4":
version: 5.26.5
resolution: "undici-types@npm:5.26.5"
checksum: bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501
languageName: node
linkType: hard
"undici@npm:^5.22.1":
version: 5.28.3
resolution: "undici@npm:5.28.3"
@ -4337,6 +4527,37 @@ __metadata:
languageName: node
linkType: hard
"web-streams-polyfill@npm:4.0.0-beta.3":
version: 4.0.0-beta.3
resolution: "web-streams-polyfill@npm:4.0.0-beta.3"
checksum: a9596779db2766990117ed3a158e0b0e9f69b887a6d6ba0779940259e95f99dc3922e534acc3e5a117b5f5905300f527d6fbf8a9f0957faf1d8e585ce3452e8e
languageName: node
linkType: hard
"web-streams-polyfill@npm:^3.2.1":
version: 3.3.3
resolution: "web-streams-polyfill@npm:3.3.3"
checksum: 64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f
languageName: node
linkType: hard
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: 5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
languageName: node
linkType: hard
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: "npm:~0.0.3"
webidl-conversions: "npm:^3.0.0"
checksum: 1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
languageName: node
linkType: hard
"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"