Skip to content

Commit

Permalink
Implement main AI store functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixarntz committed Aug 27, 2024
1 parent bc98106 commit 5326c6e
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 10 deletions.
131 changes: 131 additions & 0 deletions src/ai-store/generative-ai-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';

/**
* Service class.
*
* @since n.e.x.t
*/
export class GenerativeAiService {
/**
* Constructor.
*
* @since n.e.x.t
*
* @param {Object} service Service object.
* @param {string} service.slug Service slug.
* @param {string} service.name Service name.
* @param {Array} service.available_models Available models.
*/
constructor( { slug, name, available_models: models } ) {
if ( ! models || ! models.length ) {
throw new Error(
`No models available for the service ${ slug }. Is it available?`
);
}

this.slug = slug;
this.name = name;
this.models = models;
}

/**
* Gets the service slug.
*
* @since n.e.x.t
*
* @return {string} Service name.
*/
getServiceSlug() {
return this.slug;
}

/**
* Lists the available generative model slugs.
*
* @since n.e.x.t
*
* @return {string[]} The available model slugs.
*/
listModels() {
return this.models;
}

/**
* Generates content using the service.
*
* @since n.e.x.t
*
* @param {Object} args Arguments for generating content.
* @param {string|Object|Object[]} args.content Content data to pass to the model, including the prompt and optional history.
* @param {string} args.model Model slug.
* @param {Object} args.modelParams Model parameters.
* @return {Promise<Object[]>} Model response candidates with the generated content.
*/
async generateContent( { content, model, modelParams } ) {
// Do some very basic validation.
if ( ! content ) {
throw new Error(
__(
'The content argument is required to generate content.',
'wp-starter-plugin'
)
);
}
if ( ! Array.isArray( content ) ) {
if ( typeof content === 'object' ) {
if ( ! content.role || ! content.parts ) {
throw new Error(
__(
'The content object must have a role and parts properties.',
'wp-starter-plugin'
)
);
}
} else if ( typeof content !== 'string' ) {
throw new Error(
__(
'The content argument must be a string, an object, or an array of objects.',
'wp-starter-plugin'
)
);
}
}

return await apiFetch( {
path: `/wp-starter-plugin/v1/services/${ this.slug }:generate-content`,
method: 'POST',
data: {
content,
model: model || '',
modelParams: modelParams || {},
},
} );
}
}

const services = {};

/**
* Gets the generative AI service instance for the given service data.
*
* The service data must be an object received from the services REST endpoint.
*
* @since n.e.x.t
*
* @param {Object} serviceData Service data.
* @param {string} serviceData.slug Service slug.
* @param {string} serviceData.name Service name.
* @param {string[]} serviceData.available_models Available models.
* @return {GenerativeAiService} Generative AI service instance.
*/
export function getGenerativeAiService( serviceData ) {
if ( ! services[ serviceData.slug ] ) {
services[ serviceData.slug ] = new GenerativeAiService( serviceData );
}

return services[ serviceData.slug ];
}
70 changes: 60 additions & 10 deletions src/ai-store/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createRegistrySelector } from '@wordpress/data';
* Internal dependencies
*/
import { STORE_NAME } from './name';
import { getGenerativeAiService } from './generative-ai-service';

const RECEIVE_SERVICES = 'RECEIVE_SERVICES';

Expand Down Expand Up @@ -85,18 +86,67 @@ const selectors = {
return state.services;
},

getService: createRegistrySelector( ( select ) => ( state, slug ) => {
const services = select( STORE_NAME ).getServices();
if ( services === undefined ) {
return undefined;
isServiceRegistered: createRegistrySelector(
( select ) => ( state, slug ) => {
const services = select( STORE_NAME ).getServices();
if ( services === undefined ) {
return undefined;
}
return services[ slug ] !== undefined;
}
if ( services[ slug ] === undefined ) {
// eslint-disable-next-line no-console
console.error( `Invalid service ${ slug }.` );
return undefined;
),

isServiceAvailable: createRegistrySelector(
( select ) => ( state, slug ) => {
const services = select( STORE_NAME ).getServices();
if ( services === undefined ) {
return undefined;
}
return (
services[ slug ] !== undefined && services[ slug ].is_available
);
}
),

hasAvailableServices: createRegistrySelector(
( select ) => ( state, slugs ) => {
const services = select( STORE_NAME ).getServices();
if ( services === undefined ) {
return undefined;
}
if ( ! slugs ) {
slugs = Object.keys( services );
}
return slugs.some(
( slug ) =>
services[ slug ] !== undefined &&
services[ slug ].is_available
);
}
),

getAvailableService: createRegistrySelector(
( select ) => ( state, slugs ) => {
const services = select( STORE_NAME ).getServices();
if ( services === undefined ) {
return undefined;
}
if ( typeof slugs === 'string' ) {
slugs = [ slugs ];
} else if ( ! slugs ) {
slugs = Object.keys( services );
}
const availableSlug = slugs.find(
( slug ) =>
services[ slug ] !== undefined &&
services[ slug ].is_available
);
if ( ! availableSlug ) {
return null;
}
return getGenerativeAiService( services[ availableSlug ] );
}
return services[ slug ];
} ),
),
};

const storeConfig = {
Expand Down

0 comments on commit 5326c6e

Please sign in to comment.