Skip to content

Commit

Permalink
Fix scrolling UI and improve prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianMachado committed Dec 15, 2023
1 parent e07a87e commit bb36089
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 90 deletions.
48 changes: 27 additions & 21 deletions apps/api/src/routes/ai-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async function getOpenAiResponse(
const response = await getOpenAIClient()?.chat.completions.create({
model: "gpt-3.5-turbo-1106",
messages,
temperature: 0.5,
temperature: 0,
max_tokens: 1000,
top_p: 1,
frequency_penalty: 0,
Expand Down Expand Up @@ -183,53 +183,59 @@ export const aiFixRoute: FastifyPluginAsync = async function (server) {
const { path: issuePath } = issue;
// We want to get the property that the issue is referring to
let issueProperty = openApiSpec;
let prevPathFragment = "";
for (const pathFragment of issuePath) {
for (let i = 0; i < issuePath.length; i++) {
const pathFragment = issuePath[i];
if (issueProperty == null || typeof issueProperty !== "object") {
break;
}
issueProperty = (issueProperty as Record<string, unknown>)[
pathFragment as string
];
if (prevPathFragment === "paths") {
if (i >= 1 && issuePath[i - 1] === "paths") {
// We want the the AI to have the full context of the path
issueProperty = {
[pathFragment]: issueProperty,
};
break;
}
if (prevPathFragment === "components") {
if (i >= 2 && issuePath[i - 2] === "components") {
// We preserve the component name as it is relevant for certain rules
issueProperty = {
[pathFragment]: issueProperty,
};
break;
}
prevPathFragment = pathFragment as string;
}

// We parse out the values of all references to give the AI more context
const references = getAllReferences(
issueProperty as Record<string, unknown>,
).map((ref) => resolveAllReferences(ref, openApiSpec));
)
.map((ref) => resolveAllReferences(ref, openApiSpec))
.map((reference) => JSON.stringify(reference, null, 2))
.join(", ");

const prompt = `Given the following OpenAPI spec sample, and an issue found with that sample, please provide a suggested fix for the issue. The Sample: ${JSON.stringify(
issueProperty,
null,
2,
)}\n\n The Issue: ${JSON.stringify(
issue,
null,
2,
)}. If your suggestion is a change to the OpenAPI spec, the suggestion should be in ${
isJson ? "JSON" : "YAML"
} format. Leave inline comments explaining the changes you made. Any code blocks should use the markdown codeblock syntax. If the issue has to do with a component being orphaned, you should suggest deleting that component from the spec. ${
references
? `Here are some components that are referenced within the OpenAPI spec sample: ${references}`
: ""
}
`;

const response = await getOpenAiResponse([
{
role: "user",
content: `Given the following OpenAPI spec sample, and an issue found with that sample, please provide a suggested fix for the issue.\n\n The Sample: ${JSON.stringify(
issueProperty,
null,
2,
)}\n\n The Issue: ${JSON.stringify(
issue,
null,
2,
)}. If your suggestion is a change to the OpenAPI spec, the suggestion should be in ${
isJson ? "JSON" : "YAML"
} format. Any code blocks should use the markdown codeblock syntax. If the issue has to do with a component being orphaned, you should suggest deleting that component from the spec. Here are some components that are referenced within the OpenAPI spec sample: ${references.join(
", ",
)}
)}`,
content: prompt,
},
]);

Expand Down
123 changes: 54 additions & 69 deletions apps/web/src/components/IssueModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import useWindowSize from "@/utils/use-window-size";
import { Dialog, Transition } from "@headlessui/react";
import { ArrowUpRightIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { Editor, OnMount } from "@monaco-editor/react";
import { Editor, Monaco, OnMount } from "@monaco-editor/react";
import { Fragment, useState } from "react";
import { getRuleData } from "../../utils/issue-utils";
import { Issue } from "../DetailedScoreSection";
Expand Down Expand Up @@ -45,32 +45,6 @@ const getGlyphBackgroundClassName = (severity: number) => {
}
};

const getPingClassName = (severity: number) => {
switch (severity) {
case 0:
return "bg-red-500";
case 1:
return "bg-yellow-500";
case 2:
return "bg-blue-500";
case 3:
return "bg-green-500";
}
};

const getPingAnimationClassName = (severity: number) => {
switch (severity) {
case 0:
return "bg-red-400";
case 1:
return "bg-yellow-400";
case 2:
return "bg-blue-400";
case 3:
return "bg-green-400";
}
};

const IssueModal = ({
issue,
openapi,
Expand Down Expand Up @@ -224,7 +198,11 @@ const IssueModal = ({
)}
</div>
</div>
<div className="flex h-full w-full flex-col">
<div
className={`flex ${
selectedTab === "AI" ? "h-fit" : "h-full"
} w-full flex-col`}
>
<div className="flex w-fit rounded-md rounded-b-none border">
<div
onClick={() => setSelectedTab("CODE")}
Expand All @@ -243,30 +221,22 @@ const IssueModal = ({
AI Suggestion{" "}
{aiTabVisited ? null : (
<span className="absolute -right-1 -top-1 flex h-3 w-3">
<span
className={`absolute inline-flex h-full w-full animate-ping rounded-full ${getPingAnimationClassName(
issue.severity,
)} opacity-75`}
></span>
<span
className={`relative inline-flex h-3 w-3 rounded-full ${getPingClassName(
issue.severity,
)}`}
></span>
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#f7a1e1] opacity-75"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-[#FF00BD]"></span>
</span>
)}
</div>
</div>
{selectedTab === "AI" ? (
<div className="mt-4 flex w-full flex-col">
<div className="mt-4 flex h-fit w-full flex-col">
<div className="w-full rounded-sm border border-amber-400 bg-amber-100 px-2 py-3 font-medium text-gray-600">
Note: AI Suggestions are experimental. Implement with
care.
</div>
{aiSuggestion ? (
<ReactMarkdown
className={
"prose prose-code:before:content-none prose-code:after:content-none font-[helvetica, -apple-system, BlinkMacSystemFont, Roboto, Ubuntu, sans-serif] mt-4 h-full"
"prose prose-code:before:content-none prose-code:after:content-none font-[helvetica, -apple-system, BlinkMacSystemFont, Roboto, Ubuntu, sans-serif] mt-4"
}
components={{
p({ node, children, ...props }) {
Expand All @@ -291,6 +261,13 @@ const IssueModal = ({
const match = /language-(\w+)/.exec(
className || "",
);
const onMount: OnMount = (editor) => {
const height = editor.getContentHeight();
editor.layout({
height,
width: 100,
});
};
return !match ? (
<code
className="font-light font-[ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace] rounded-[0.25rem] bg-[#f7fafc] p-[0.25rem] text-sm text-[#2a2f45]"
Expand All @@ -300,21 +277,26 @@ const IssueModal = ({
</code>
) : (
<Editor
className="my-2"
height="300px"
className="my-2 h-full"
width="100%"
onMount={onMount}
language={
fileExtension === "json" ? "json" : "yaml"
}
value={
children as string
} /* We expect the code to be a string */
options={{
automaticLayout: true,
readOnly: true,
selectionHighlight: true,
renderLineHighlight: "line",
scrollBeyondLastLine: false,
glyphMargin: true,
scrollbar: {
vertical: "hidden",
alwaysConsumeMouseWheel: false,
},
minimap: windowSize.isMobile
? {
enabled: false,
Expand Down Expand Up @@ -347,33 +329,36 @@ const IssueModal = ({
</div>
) : null}
{selectedTab === "CODE" ? (
<Editor
height="100%"
width="100%"
language={fileExtension === "json" ? "json" : "yaml"}
value={openapi}
options={{
readOnly: true,
selectionHighlight: true,
renderLineHighlight: "line",
scrollBeyondLastLine: false,
glyphMargin: true,
minimap: windowSize.isMobile
? {
enabled: false,
}
: undefined,
fontSize: windowSize.isMobile ? 10 : undefined,
lineNumbers: windowSize.isMobile ? "off" : undefined,
// @ts-ignore - this is a valid option, but the types don't know about it
renderIndentGuides: windowSize.isMobile
? false
: undefined,
folding: windowSize.isMobile ? false : undefined,
}}
onMount={onEditorDidMount}
line={issue.range.start.line}
/>
<div className="flex h-full w-full">
<Editor
className="my-2 h-full"
width="100%"
language={fileExtension === "json" ? "json" : "yaml"}
value={openapi}
options={{
automaticLayout: true,
readOnly: true,
selectionHighlight: true,
renderLineHighlight: "line",
scrollBeyondLastLine: false,
glyphMargin: true,
minimap: windowSize.isMobile
? {
enabled: false,
}
: undefined,
fontSize: windowSize.isMobile ? 10 : undefined,
lineNumbers: windowSize.isMobile ? "off" : undefined,
// @ts-ignore - this is a valid option, but the types don't know about it
renderIndentGuides: windowSize.isMobile
? false
: undefined,
folding: windowSize.isMobile ? false : undefined,
}}
onMount={onEditorDidMount}
line={issue.range.start.line}
/>
</div>
) : null}
</div>
</div>
Expand Down

0 comments on commit bb36089

Please sign in to comment.