Skip to content

Instantly share code, notes, and snippets.

@johnlindquist
Created December 5, 2023 05:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnlindquist/c72a5dfa3d6c6ee1c7a2e3a59c54c4c1 to your computer and use it in GitHub Desktop.
Save johnlindquist/c72a5dfa3d6c6ee1c7a2e3a59c54c4c1 to your computer and use it in GitHub Desktop.
import "@johnlindquist/kit"
// Name: Bulk Tip Draft Generator
// Author: Taylor Bell
// Description:
import escape from "escape-string-regexp"
import { ChatOpenAI } from "langchain/chat_models/openai"
import { ConversationChain } from "langchain/chains"
import { CallbackManager } from "langchain/callbacks"
import {
AIMessagePromptTemplate,
ChatPromptTemplate,
HumanMessagePromptTemplate,
SystemMessagePromptTemplate,
} from "langchain/prompts"
let openAIApiKey = await env("OPENAI_API_KEY", {
hint: `Grab a key from <a href="https://platform.openai.com/account/api-keys">here</a>`,
})
let subDirs = ["Documents", "skillrecordings", "Epic Web (Kent)", "Workshop Recordings", "tipIdeas"]
let exampleFilesForGPT4Dir = projectPath("examples")
let exampleTip1 = await readFile(`${exampleFilesForGPT4Dir}/colt-exampletip1.md`, "utf-8")
let exampleTip2 = await readFile(`${exampleFilesForGPT4Dir}/colt-exampletip2.md`, "utf-8")
let editorPromise = editor({
ignoreBlur: true,
onSubmit: async (input, state) => {
if (state.value === "open") {
await revealInFinder(outDir())
return preventSubmit
}
},
})
async function makeQuery(text, exampleTip1, exampleTip2) {
setLoading(true)
debugger
let prompt = ChatPromptTemplate.fromMessages([
SystemMessagePromptTemplate.fromTemplate(
`You are an experienced web developer and writer with deep knowledge and understanding of complex JavaScript, React, and associated patterns. You value clear and precise instructional writing and are able to extensively break down and explain advanced coding concepts. Your tone is informative and occasionally casual, imbuing your technical guidance with the sense that you're conversing with your reader, rather than delivering a lecture.`.trim()
),
HumanMessagePromptTemplate.fromTemplate(
`
Your task is to write a succinct markdown formatted article on the topic of Making Requests to OpenAI's GPT APIs using JavaScript.`.trim()
),
AIMessagePromptTemplate.fromTemplate(`${exampleTip1.trim().replaceAll("{", "{{").replaceAll("}", "}}")}`),
HumanMessagePromptTemplate.fromTemplate(
`Please write a succinct markdown formatted article on the topic of Object Destructuring in JavaScript`.trim()
),
AIMessagePromptTemplate.fromTemplate(`${exampleTip2.trim().replaceAll("{", "{{").replaceAll("}", "}}")}`),
HumanMessagePromptTemplate.fromTemplate(`${text}`.trim()),
])
debugger
let llm = new ChatOpenAI({
// modelName: "gpt-4-0314",
modelName: "gpt-3.5-turbo",
temperature: 0.69,
maxTokens: 2048,
openAIApiKey,
streaming: true,
callbackManager: CallbackManager.fromHandlers({
handleLLMNewToken: async token => {
await editor.append(token)
},
}),
})
let chain = new ConversationChain({
llm,
prompt,
})
let compiledResponse = await chain.call({ input: [""] })
let aiResult = compiledResponse.response
debugger
setLoading(false)
return aiResult
}
let topicIdeas = [
`## useFetcher with Multiple Submit Buttons Tip Idea \[03:33:37\]
A question is raised about the efficiency of using "useFetcher" with multiple submit buttons, and it is clarified that the difference between using a form and "useFetcher" is whether a redirect is planned at the end of the action\. If a redirect is planned, a form should be used, and if not, "useFetcher" and a Fetcher form can be used\.`,
`## Handling Conditional Field Validation with Zod \[03:37:40\]
\x2d The participant raises a question about handling conditional field validation in forms\.
\x2d They describe a scenario where certain fields should only be mandatory if specific values are selected in a dropdown\.
\x2d They are interested in how to define a Zod schema that represents these conditional requirements\.
\x2d The host acknowledges the participant's question and mentions the Zod library\.
\x2d They suggest that the participant research the ZodUnion type, which can be used to define a set of fields or another set of fields\.
\x2d The Conform function in Zod also supports unions\.`,
`## Handling Nested Component Form Structure with Conform Tip Idea
\x2d A question is raised about how Conform handles deeply nested components\.
\x2d The current setup involves attaching the Conform input directly to the input field\.
\x2d The question asks if there is another component or utility provided by Conform that can pass the desired attachment to a deeply nested child component\.
`,
`## Question about Conform and Handling Form Structure \[01:54:53 \x2d 01:57:19\]
\x2d The question is whether Conform has any solutions for handling form structure\.
\x2d The host doesn't believe Conform has a solution for this and hopes it doesn't because there are already existing solutions\.
\x2d Passing things as props is suggested as one solution, which is done later in the exercises for passing fields config as props\.
\x2d Composition using slots or the child prop is mentioned as another solution, but there isn't enough time to discuss it in detail\.
\x2d As a last resort, context can be used to pass things through if needed\.
\x2d The example of using style components and rendering the actual component inside it is mentioned\.
\x2d It is recommended that those using styled components should migrate away from it\.
\x2d The absence of the name attribute in the form is noticed, and most form utilities use the name attribute for submissions\.
\x2d It is confirmed that the structure of the form comes from the schema defined\.
\x2d The host gives an example of using nested structure in form submissions with React Hook Form and asks if Conform handles that as well\.
`,
`## Indexing Foreign Keys Tip Idea \[00:21:02\]
Speaker 1 is discussing the necessity of indexing foreign keys in database models\.
\x2d They mention that putting an index on every foreign key may not be necessary because some foreign keys are already unique\.
\x2d For example, in the "file" model, adding an additional index would be unnecessary because there is already an existing index\.
\x2d However, the database doesn't automatically recognize duplicate indexes, so an extra index may still be created\.
\x2d Speaker 1 explains that the optimizer will only use one of the duplicate indexes when running queries\.
\x2d The duplicate index may not significantly impact read performance, but it can negatively impact write performance without providing any benefit to read performance\.
\x2d Speaker 1 discusses the potential benefits of compound indexes\.
\x2d In the case of a unique index combined with another field, there may be some overlap in indexes\.
\x2d Having overlapping indexes can be beneficial as long as they provide added value\.
\x2d However, it's important to note that having overlapping indexes doesn't necessarily hurt the database performance\.
\x2d Speaker 1 discusses the importance of adding comments to index files, explaining the purpose of each index\.
\x2d For a specific index called "combined index," Speaker 1 acknowledges the clear benefit it provides and mentions the need to update the description to include information about unique indexes automatically created by Prisma\.
\x2d The rule mentioned is to create an index for every foreign key that is not unique\.
`,
`## Why Avoid Auto\x2dIncrementing IDs Tip Idea \[00:36:05\]
\x2d A question is asked about why auto\x2dincrement ID is considered silly and how to explain the benefits of UUID, GUID, or QUID to DBAs in organizations\.
\x2d The host explains that one issue with auto\x2dincrement IDs is the lack of consistency when multiple instances of an app are running simultaneously, potentially resulting in the creation of the same ID\.
\x2d Auto\x2dincrement IDs can be easily guessed, making it a security concern\.
\x2d The host expresses their view that auto\x2dincrement IDs don't make sense to them and invites others to share their thoughts on explaining this to DBAs\.`,
`## Dealing with Deleted Files Tip Idea \[01:39:07\]
\x2d A question is raised about the possibility of deleting an image and having it cascade to the corresponding file deletion\.
\x2d The possibility of representing and establishing a relationship between the image and the file, so that if the image is deleted, the file is also deleted, is discussed\.
\x2d The host mentions that they are not sure if there is a built\x2din way to achieve this and suggest asking Tyler for more information\.
\x2d It is mentioned that if an image is deleted, it would cascade up to the user, resulting in the user not having an image anymore\.
\x2d The possibility of an image belonging to both a note and a user is discussed\.
\x2d The idea of having separate tables for note images and user images is considered as a potential solution\.
\x2d The host expresses openness to feedback and mentions that it would be nice to have impossible states be truly impossible\.`,
// "Understanding JavaScript: The Key Difference Between `null` and `undefined`",
]
let total = topicIdeas.length
let count = 0
let outDir = createPathResolver(home(...subDirs))
for (let t of topicIdeas) {
setInput("")
let gptResult = ""
count++
let random11DigitNumber = Math.floor(Math.random() * 1000000000000)
let outFilePath = outDir(`${random11DigitNumber}-tip-draft.md`)
await ensureDir(outDir())
setPreview(
md(`# Making ${count} of ${total}
Making query from:
~~~
${t}
~~~
Writing to:
${outDir()}
[Open in Finder](submit:open)
`)
)
gptResult = await makeQuery(escape(t.replaceAll("{", "{{").replaceAll("}", "}}")), exampleTip1, exampleTip2)
if (gptResult !== "") {
await writeFile(outFilePath, gptResult)
}
}
await editorPromise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment