Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): ai images #6506

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/backend/server/src/data/migrations/utils/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,43 @@ export const prompts: Prompt[] = [
},
],
},
{
name: 'Make it real',
action: 'Make it real',
model: 'gpt-4-vision-preview',
messages: [
{
role: 'system',
content: `You are an expert web developer who specializes in building working website prototypes from low-fidelity wireframes.
Your job is to accept low-fidelity wireframes, then create a working prototype using HTML, CSS, and JavaScript, and finally send back the results.
The results should be a single HTML file.
Use tailwind to style the website.
Put any additional CSS styles in a style tag and any JavaScript in a script tag.
Use unpkg or skypack to import any required dependencies.
Use Google fonts to pull in any open source fonts you require.
If you have any images, load them from Unsplash or use solid colored rectangles.

The wireframes may include flow charts, diagrams, labels, arrows, sticky notes, and other features that should inform your work.
If there are screenshots or images, use them to inform the colors, fonts, and layout of your website.
Use your best judgement to determine whether what you see should be part of the user interface, or else is just an annotation.

Use what you know about applications and user experience to fill in any implicit business logic in the wireframes. Flesh it out, make it real!

The user may also provide you with the html of a previous design that they want you to iterate from.
In the wireframe, the previous design's html will appear as a white rectangle.
Use their notes, together with the previous design, to inform your next result.

Sometimes it's hard for you to read the writing in the wireframes.
For this reason, all text from the wireframes will be provided to you as a list of strings, separated by newlines.
Use the provided list of text from the wireframes as a reference if any text is hard to read.

You love your designers and want them to be happy. Incorporating their feedback and notes and producing working websites makes them happy.

When sent new wireframes, respond ONLY with the contents of the html file.

{{image}}
`,
},
],
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class OpenAIProvider
.filter(url => SIMPLE_IMAGE_URL_REGEX.test(url))
.map(url => ({
type: 'image_url',
image_url: { url, detail: 'low' },
image_url: { url, detail: 'high' },
})),
];
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { assertExists } from '@blocksuite/global/utils';
import { AIProvider } from '@blocksuite/presets';

import { textToTextStream } from './request';
import { imageToTextStream, textToTextStream } from './request';

export function setupAIProvider() {
AIProvider.provideAction('chat', options => {
Expand Down Expand Up @@ -31,7 +31,10 @@ export function setupAIProvider() {

AIProvider.provideAction('translate', options => {
assertExists(options.stream);
const prompt = `Translate the following content to ${options.lang}: ${options.input}`;
const prompt = `Please translate the following content into ${options.lang} and return it to us, adhering to the original format of the content:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
Expand Down Expand Up @@ -111,11 +114,174 @@ export function setupAIProvider() {

AIProvider.provideAction('checkCodeErrors', options => {
assertExists(options.stream);
const prompt = `Check the code errors in the following content: ${options.input}`;
const prompt = `Check the code errors in the following content and provide the corrected version:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('explainCode', options => {
assertExists(options.stream);
const prompt = `Explain the code in the following content, focusing on the logic, functions, and expected outcomes:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writeArticle', options => {
assertExists(options.stream);
const prompt = `Write an article based on the following content, focusing on the main ideas, structure, and flow:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writeTwitterPost', options => {
assertExists(options.stream);
const prompt = `Write a Twitter post based on the following content, keeping it concise and engaging:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writePoem', options => {
assertExists(options.stream);
const prompt = `Write a poem based on the following content, focusing on the emotions, imagery, and rhythm:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writeOutline', options => {
assertExists(options.stream);
const prompt = `Write an outline from the following content in Markdown: ${options.input}`;

return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writeBlogPost', options => {
assertExists(options.stream);
const prompt = `Write a blog post based on the following content, focusing on the insights, analysis, and personal perspective:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('brainstorm', options => {
assertExists(options.stream);
const prompt = `Brainstorm ideas based on the following content, exploring different angles, perspectives, and approaches:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('findActions', options => {
assertExists(options.stream);
const prompt = `Find actions related to the following content and return content in markdown: ${options.input}`;

return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('writeOutline', options => {
assertExists(options.stream);
const prompt = `Write an outline based on the following content, organizing the main points, subtopics, and structure:

${options.input}
`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('brainstormMindmap', options => {
assertExists(options.stream);
const prompt = `Use the nested unordered list syntax without other extra text style in Markdown to create a structure similar to a mind map without any unnecessary plain text description. Analyze the following questions or topics: ${options.input}`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('explain', options => {
assertExists(options.stream);
const prompt = `Explain the following content in Markdown: ${options.input}`;

return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
});
});

AIProvider.provideAction('explainImage', options => {
assertExists(options.stream);
const prompt = `Describe the scene captured in this image, focusing on the details, colors, emotions, and any interactions between subjects or objects present.`;
return textToTextStream({
docId: options.docId,
workspaceId: options.workspaceId,
prompt,
attachments: options.attachments,
});
});

AIProvider.provideAction('makeItReal', options => {
assertExists(options.stream);
const promptName = 'Make it real';
return imageToTextStream({
promptName,
docId: options.docId,
workspaceId: options.workspaceId,
params: options.params,
attachments: options.attachments,
content:
options.content ||
'Here are the latest wireframes. Could you make a new website based on these wireframes and notes and send back just the html file?',
});
});
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,106 @@
import { getBaseUrl } from '@affine/graphql';
import { CopilotClient, toTextStream } from '@blocksuite/presets';

const TIMEOUT = 5000;
const TIMEOUT = 500000;

export function textToTextStream({
docId,
workspaceId,
prompt,
attachments,
params,
}: {
docId: string;
workspaceId: string;
prompt: string;
attachments?: string[];
params?: string;
}): BlockSuitePresets.TextStream {
const client = new CopilotClient(getBaseUrl());
return {
[Symbol.asyncIterator]: async function* () {
const hasAttachments = attachments && attachments.length > 0;
const session = await client.createSession({
workspaceId,
docId,
promptName: 'Summary', // placeholder
promptName: hasAttachments ? 'debug:action:vision4' : 'Summary',
});
const eventSource = client.textToTextStream(prompt, session);
if (hasAttachments) {
const messageId = await client.createMessage({
sessionId: session,
content: prompt,
attachments,
params,
});
const eventSource = client.textStream(messageId, session);
yield* toTextStream(eventSource, { timeout: TIMEOUT });
} else {
const eventSource = client.textToTextStream(prompt, session);
yield* toTextStream(eventSource, { timeout: TIMEOUT });
}
},
};
}

// Image to text(html)
export function imageToTextStream({
docId,
workspaceId,
promptName,
...options
}: {
docId: string;
workspaceId: string;
promptName: string;
params?: string;
content: string;
attachments?: string[];
}) {
const client = new CopilotClient(getBaseUrl());
return {
[Symbol.asyncIterator]: async function* () {
const sessionId = await client.createSession({
workspaceId,
docId,
promptName,
});
const messageId = await client.createMessage({
sessionId,
...options,
});
const eventSource = client.textStream(messageId, sessionId);
yield* toTextStream(eventSource, { timeout: TIMEOUT });
},
};
}

// Image to images
export function imageToImagesStream({
docId,
workspaceId,
promptName,
...options
}: {
docId: string;
workspaceId: string;
promptName: string;
content: string;
params?: string;
attachments?: string[];
}) {
const client = new CopilotClient(getBaseUrl());
return {
[Symbol.asyncIterator]: async function* () {
const sessionId = await client.createSession({
workspaceId,
docId,
promptName,
});
const messageId = await client.createMessage({
sessionId,
...options,
});
const eventSource = client.imagesStream(messageId, sessionId);
yield* toTextStream(eventSource, { timeout: TIMEOUT });
},
};
Expand Down