Skip to content

Commit

Permalink
Replaced globalThis.fetch with node-fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
dsebastien committed Aug 18, 2024
1 parent d6fffe0 commit 6f56fd7
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 58 deletions.
3 changes: 2 additions & 1 deletion apps/plugin/src/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export const MSG_API_KEY_CONFIGURATION_REQUIRED =
export const MSG_IMAGE_GENERATION_MODEL_CONFIGURATION_REQUIRED =
'Please configure the Replicate plugin to provide an image generation model';

export const MSG_IMAGE_GENERATION_ERROR = 'Error while generating image(s) using Replicate.com';
export const MSG_IMAGE_GENERATION_ERROR =
'Error while generating image(s) using Replicate.com';
5 changes: 3 additions & 2 deletions apps/plugin/src/app/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from './constants';
import { generateImages } from './utils/generate-images.fn';
import { PromptModal } from './modals/prompt-modal';
import {isApiKeyConfigured} from "./utils/is-api-key-configured.fn";
import { isApiKeyConfigured } from './utils/is-api-key-configured.fn';

export class ReplicatePlugin extends Plugin {
/**
Expand Down Expand Up @@ -110,7 +110,8 @@ export class ReplicatePlugin extends Plugin {
}

if (loadedSettings.imageGenerationConfiguration) {
draft.imageGenerationConfiguration = loadedSettings.imageGenerationConfiguration;
draft.imageGenerationConfiguration =
loadedSettings.imageGenerationConfiguration;
} else {
log(
'The loaded settings miss the [imageGenerationConfiguration] property',
Expand Down
37 changes: 25 additions & 12 deletions apps/plugin/src/app/settingTab/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {
App, Notice,
App,
Notice,
PluginSettingTab,
Setting,
ToggleComponent,
} from 'obsidian';
import { ReplicatePlugin } from '../plugin';
import { Draft, produce } from 'immer';
import {
PluginSettings,
} from '../types/plugin-settings.intf';
import { PluginSettings } from '../types/plugin-settings.intf';
import { log } from '../utils/log';
import {NOTICE_TIMEOUT} from "../constants";
import { NOTICE_TIMEOUT } from '../constants';

/* eslint-disable @typescript-eslint/no-non-null-assertion */

Expand Down Expand Up @@ -139,20 +138,34 @@ export class SettingsTab extends PluginSettingTab {
.addTextArea((text) => {
text
.setPlaceholder('Valid JSON object')
.setValue(JSON.stringify(this.plugin.settings.imageGenerationConfiguration))
.setValue(
JSON.stringify(this.plugin.settings.imageGenerationConfiguration)
)
.onChange(async (newValue) => {
log(`Image generation model configuration set to: `, 'debug', newValue);
log(
`Image generation model configuration set to: `,
'debug',
newValue
);
let imageGenerationModelConfiguration: object = {};
try{
try {
imageGenerationModelConfiguration = JSON.parse(newValue);
} catch(error) {
log('Invalid JSON for image generation model configuration', 'warn', error);
new Notice('The Replicate.com image generation model configuration is not a valid JSON object. Please correct it.', NOTICE_TIMEOUT);
} catch (error) {
log(
'Invalid JSON for image generation model configuration',
'warn',
error
);
new Notice(
'The Replicate.com image generation model configuration is not a valid JSON object. Please correct it.',
NOTICE_TIMEOUT
);
}
this.plugin.settings = produce(
this.plugin.settings,
(draft: Draft<PluginSettings>) => {
draft.imageGenerationConfiguration = imageGenerationModelConfiguration
draft.imageGenerationConfiguration =
imageGenerationModelConfiguration;
}
);
await this.plugin.saveSettings();
Expand Down
8 changes: 4 additions & 4 deletions apps/plugin/src/app/types/plugin-settings.intf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

export interface PluginSettings {
// General
apiKey: string;
Expand All @@ -24,9 +23,10 @@ export const DEFAULT_SETTINGS: PluginSettings = {
// Reference for this default example: https://replicate.com/black-forest-labs/flux-dev
imageGenerationConfiguration: {
// Prompt for generated image
prompt: "obsidian rock in the forest spelling out the words \"Obsidian\", canon pro photography, dynamic shot, 50mm",
prompt:
'obsidian rock in the forest spelling out the words "Obsidian", canon pro photography, dynamic shot, 50mm',
// Aspect ratio for the generated image
aspect_ratio: "1:1", // 1:1, 4:3, 16:9 9:16 3:4 4:3 2:3 3:2 4:5 5:4
aspect_ratio: '1:1', // 1:1, 4:3, 16:9 9:16 3:4 4:3 2:3 3:2 4:5 5:4
// Input image for image to image mode. The aspect ratio of your output will match this image
// image: ...
// Prompt strength when using img2img. 1.0 corresponds to full destruction of information in image
Expand All @@ -40,7 +40,7 @@ export const DEFAULT_SETTINGS: PluginSettings = {
// Random seed. Set for reproducible generation
// seed: ...
// Format of the output images
output_format: "webp", // webp, jpg, png
output_format: 'webp', // webp, jpg, png
// Quality when saving the output images, from 0 to 100. 100 is best quality, 0 is lowest quality. Not relevant for .png outputs
output_quality: 80, // 0-100
// Disable safety checker for generated images
Expand Down
93 changes: 64 additions & 29 deletions apps/plugin/src/app/utils/generate-images.fn.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
import {PluginSettings} from '../types/plugin-settings.intf';
import {log} from './log';
import {Notice} from "obsidian";
import { PluginSettings } from '../types/plugin-settings.intf';
import { log } from './log';
import { Notice } from 'obsidian';
import {
MSG_API_KEY_CONFIGURATION_REQUIRED,
MSG_IMAGE_GENERATION_ERROR,
MSG_IMAGE_GENERATION_MODEL_CONFIGURATION_REQUIRED,
NOTICE_TIMEOUT
} from "../constants";
import {isApiKeyConfigured} from "./is-api-key-configured.fn";
import {isImageGenerationModelConfigured} from "./is-image-generation-model-configured.fn";
import {ReplicateCreatePrediction} from "./replicate-create-prediction-input.intf";
import {getReplicateClient} from "./get-replicate-client.fn";
NOTICE_TIMEOUT,
} from '../constants';
import { isApiKeyConfigured } from './is-api-key-configured.fn';
import { isImageGenerationModelConfigured } from './is-image-generation-model-configured.fn';
import { ReplicateCreatePrediction } from './replicate-create-prediction-input.intf';
import { getReplicateClient } from './get-replicate-client.fn';

export const generateImages = async (
prompt: string | undefined,
settings: PluginSettings
): Promise<void> => {
if (!isApiKeyConfigured(settings)) {
log('Cannot generate images because the Replicate.com API Key has not been configured', 'warn');
log(
'Cannot generate images because the Replicate.com API Key has not been configured',
'warn'
);
new Notice(MSG_API_KEY_CONFIGURATION_REQUIRED, NOTICE_TIMEOUT);
return;
}

if (!isImageGenerationModelConfigured) {
log('Cannot generate images because no image generation model has been configured', 'warn');
new Notice(MSG_IMAGE_GENERATION_MODEL_CONFIGURATION_REQUIRED, NOTICE_TIMEOUT);
log(
'Cannot generate images because no image generation model has been configured',
'warn'
);
new Notice(
MSG_IMAGE_GENERATION_MODEL_CONFIGURATION_REQUIRED,
NOTICE_TIMEOUT
);
return;
}

const replicate = getReplicateClient(settings.apiKey);

log('Generating images for prompt: ', 'debug', prompt);
new Notice(`Generating image(s) for with following prompt: [${prompt}] using the following model [${settings.imageGenerationModel}]`, NOTICE_TIMEOUT);
new Notice(
`Generating image(s) for with following prompt: [${prompt}] using the following model [${settings.imageGenerationModel}]`,
NOTICE_TIMEOUT
);

try {
const replicateCreatePredictionConfiguration: ReplicateCreatePrediction = {
Expand All @@ -41,32 +53,54 @@ export const generateImages = async (
...settings.imageGenerationConfiguration,
prompt, // FIXME ensure that the prompt is the one we expect in the request
},
}
};

if(settings.imageGenerationModelVersion && "" !== settings.imageGenerationModelVersion!.trim()) {
replicateCreatePredictionConfiguration.version = settings.imageGenerationModelVersion!;
if (settings.imageGenerationModelVersion) {
if ('' !== settings.imageGenerationModelVersion.trim()) {
replicateCreatePredictionConfiguration.version =
settings.imageGenerationModelVersion;
}
}

log('Sending image generation request to Replicate.com', 'debug', replicateCreatePredictionConfiguration);
log(
'Sending image generation request to Replicate.com',
'debug',
replicateCreatePredictionConfiguration
);

let predictionResult = await replicate.predictions.create(replicateCreatePredictionConfiguration);
let predictionResult = await replicate.predictions.create(
replicateCreatePredictionConfiguration
);

if (predictionResult.error) {
log("Error received from Replicate.com", 'warn', predictionResult.error);
new Notice(`${MSG_IMAGE_GENERATION_ERROR}: [${predictionResult.error}]`, NOTICE_TIMEOUT);
log('Error received from Replicate.com', 'warn', predictionResult.error);
new Notice(
`${MSG_IMAGE_GENERATION_ERROR}: [${predictionResult.error}]`,
NOTICE_TIMEOUT
);
return;
}

while (predictionResult.status !== 'succeeded' && predictionResult.status !== 'failed') {
while (
predictionResult.status !== 'succeeded' &&
predictionResult.status !== 'failed'
) {
await sleep(1000);

log("Loading the image generation results from Replicate.com", "debug");
log('Loading the image generation results from Replicate.com', 'debug');
predictionResult = await replicate.predictions.get(predictionResult.id);
log("Received response from Replicate.com", "debug", predictionResult);
log('Received response from Replicate.com', 'debug', predictionResult);

if (predictionResult?.error) {
log('Error received from Replicate', 'warn', predictionResult.error.detail);
new Notice(`${MSG_IMAGE_GENERATION_ERROR}: [${predictionResult.error.detail}]`, NOTICE_TIMEOUT);
log(
'Error received from Replicate',
'warn',
predictionResult.error.detail
);
new Notice(
`${MSG_IMAGE_GENERATION_ERROR}: [${predictionResult.error.detail}]`,
NOTICE_TIMEOUT
);
return;
}

Expand All @@ -77,24 +111,25 @@ export const generateImages = async (
}

if (predictionResult.status === 'succeeded') {
log('Successfully loaded the results from Replicate', 'info', predictionResult);
log(
'Successfully loaded the results from Replicate',
'info',
predictionResult
);

// TODO HANDLE SUCCESS
}

}
} catch (error) {
log('Error while generating image(s) using Replicate.com', 'warn', error);
new Notice(`${MSG_IMAGE_GENERATION_ERROR}: [${error}]`, NOTICE_TIMEOUT);
return;
}


//await navigator.clipboard.writeText(prompt);
// display notice once completed (mention if copied to clipboard)
};


// if (predictionResult.error) {
// console.warn('Error: ', predictionResult.error);
// setLoading(false);
Expand Down
7 changes: 3 additions & 4 deletions apps/plugin/src/app/utils/get-replicate-client.fn.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {log} from "./log";
import Replicate from "replicate";
import { log } from './log';
import Replicate from 'replicate';

export const getReplicateClient = (apiKey: string): Replicate => {
log('Creating Replicate.com API client', 'debug');
return new Replicate({
auth: apiKey,
userAgent: 'Obsidian Replicate',
});
}

};
4 changes: 2 additions & 2 deletions apps/plugin/src/app/utils/is-api-key-configured.fn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {PluginSettings} from "../types/plugin-settings.intf";
import { PluginSettings } from '../types/plugin-settings.intf';

export const isApiKeyConfigured = (settings: PluginSettings): boolean => {
return '' !== settings.apiKey.trim();
}
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {PluginSettings} from "../types/plugin-settings.intf";
import { PluginSettings } from '../types/plugin-settings.intf';

export const isImageGenerationModelConfigured = (settings: PluginSettings): boolean => {
export const isImageGenerationModelConfigured = (
settings: PluginSettings
): boolean => {
return '' !== settings.imageGenerationModel.trim();
}
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
type WebhookEventType = "start" | "output" | "logs" | "completed";
type WebhookEventType = 'start' | 'output' | 'logs' | 'completed';

export interface ReplicateCreatePrediction {
model: string;
Expand Down
13 changes: 13 additions & 0 deletions apps/plugin/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
import { ReplicatePlugin } from './app/plugin';
import nfetch, { Headers, Request, Response } from 'node-fetch';

// Replace the browser's fetch (which uses CORS) with node-fetch, which is compatible, but has no CORS restrictions
// Reference: https://forum.obsidian.md/t/make-http-requests-from-plugins/15461/25?page=2

// @ts-expect-error - globalThis is not defined in the Node.js environment
globalThis.fetch = nfetch;
// @ts-expect-error - globalThis is not defined in the Node.js environment
globalThis.Headers = Headers;
// @ts-expect-error - globalThis is not defined in the Node.js environment
globalThis.Request = Request;
// @ts-expect-error - globalThis is not defined in the Node.js environment
globalThis.Response = Response;

// noinspection JSUnusedGlobalSymbols
export default ReplicatePlugin;

0 comments on commit 6f56fd7

Please sign in to comment.