Skip to content

Commit 8ea0e19

Browse files
authored
Merge pull request #845 from 10up/feature/localhost
Add Ollama as a Provider, allowing some Features to work with locally hosted LLMs
2 parents 355582b + bf9904b commit 8ea0e19

36 files changed

+6734
-3459
lines changed

.github/workflows/cypress.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
matrix:
1818
core:
1919
- {name: 'WP latest', version: 'latest'}
20-
- {name: 'WP minimum', version: 'WordPress/WordPress#6.5'}
20+
- {name: 'WP minimum', version: 'WordPress/WordPress#6.6'}
2121
- {name: 'WP trunk', version: 'WordPress/WordPress#master'}
2222

2323
steps:

README.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,22 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
1616

1717
## Features
1818

19-
* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
20-
* Generate key takeaways from post content and render at the top of a post using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat) or [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
21-
* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
22-
* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview)
19+
* Generate a summary of post content and store it as an excerpt using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview), [xAI's Grok](https://x.ai/) or locally using [Ollama](https://ollama.com/)
20+
* Generate key takeaways from post content and render at the top of a post using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or locally using [Ollama](https://ollama.com/)
21+
* Generate titles from post content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview), [xAI's Grok](https://x.ai/) or locally using [Ollama](https://ollama.com/)
22+
* Expand or condense text content using [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [Google's Gemini API](https://ai.google.dev/docs/gemini_api_overview), [xAI's Grok](https://x.ai/) or locally using [Ollama](https://ollama.com/)
2323
* Generate new images on demand to use in-content or as a featured image using [OpenAI's DALL·E 3 API](https://platform.openai.com/docs/guides/images)
2424
* Generate transcripts of audio files using [OpenAI's Whisper API](https://platform.openai.com/docs/guides/speech-to-text)
2525
* Moderate incoming comments for sensitive content using [OpenAI's Moderation API](https://platform.openai.com/docs/guides/moderation)
2626
* Convert text content into audio and output a "read-to-me" feature on the front-end to play this audio using [Microsoft Azure's Text to Speech API](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/text-to-speech), [Amazon Polly](https://aws.amazon.com/polly/) or [OpenAI's Text to Speech API](https://platform.openai.com/docs/guides/text-to-speech)
27-
* Classify post content using [IBM Watson's Natural Language Understanding API](https://www.ibm.com/watson/services/natural-language-understanding/), [OpenAI's Embedding API](https://platform.openai.com/docs/guides/embeddings) or [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
27+
* Classify post content using [IBM Watson's Natural Language Understanding API](https://www.ibm.com/watson/services/natural-language-understanding/), [OpenAI's Embedding API](https://platform.openai.com/docs/guides/embeddings), [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) or locally using [Ollama](https://ollama.com/)
2828
* Create a smart 404 page that has a recommended results section that suggests relevant content to the user based on the page URL they were trying to access using either [OpenAI's Embedding API](https://platform.openai.com/docs/guides/embeddings) or [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) in combination with [ElasticPress](https://github.com/10up/ElasticPress)
2929
* Find similar terms to merge together using either [OpenAI's Embedding API](https://platform.openai.com/docs/guides/embeddings) or [Microsoft Azure's OpenAI service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) in combination with [ElasticPress](https://github.com/10up/ElasticPress). Note this only compares top-level terms and if you merge a term that has children, these become top-level terms as per default WordPress behavior
3030
* BETA: Recommend content based on overall site traffic via [Microsoft Azure's AI Personalizer API](https://azure.microsoft.com/en-us/services/cognitive-services/personalizer/) *(note that this service has been [deprecated by Microsoft](https://learn.microsoft.com/en-us/azure/ai-services/personalizer/) and as such, will no longer work. We are looking to replace this with a new provider to maintain the same functionality (see [issue#392](https://github.com/10up/classifai/issues/392))*
31-
* Generate image alt text, image tags, and smartly crop images using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/)
32-
* Scan images and PDF files for embedded text and save for use in post meta using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/)
31+
* Generate image alt text using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/), [OpenAI's ChatGPT API](https://platform.openai.com/docs/guides/chat), [xAI's Grok](https://x.ai/) or locally using [Ollama](https://ollama.com/)
32+
* Generate image tags and extract text from images using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/) or locally using [Ollama](https://ollama.com/)
33+
* Smartly crop images using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/)
34+
* Scan PDF files for embedded text and save for use in post meta using [Microsoft Azure's AI Vision API](https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/)
3335
* Bulk classify content with [WP-CLI](https://wp-cli.org/)
3436

3537
### Language Processing
@@ -55,13 +57,14 @@ Tap into leading cloud-based services like [OpenAI](https://openai.com/), [Micro
5557
## Requirements
5658

5759
* PHP 7.4+
58-
* [WordPress](http://wordpress.org) 6.5+
60+
* [WordPress](http://wordpress.org) 6.6+
5961
* To utilize the NLU Language Processing functionality, you will need an active [IBM Watson](https://cloud.ibm.com/registration) account.
6062
* To utilize the ChatGPT, Embeddings, Text to Speech or Whisper Language Processing functionality or DALL·E Image Processing functionality, you will need an active [OpenAI](https://platform.openai.com/signup) account.
6163
* To utilize the Azure AI Vision Image Processing functionality or Text to Speech Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account.
6264
* To utilize the Azure OpenAI Language Processing functionality, you will need an active [Microsoft Azure](https://signup.azure.com/signup) account and you will need to [apply](https://aka.ms/oai/access) for OpenAI access.
6365
* To utilize the Google Gemini Language Processing functionality, you will need an active [Google Gemini](https://ai.google.dev/tutorials/setup) account.
6466
* To utilize the AWS Language Processing functionality, you will need an active [AWS](https://console.aws.amazon.com/) account.
67+
* To utilize the Ollama Language or Image Processing functionality, you will need to install [Ollama](https://ollama.com/) and download the appropriate models.
6568
* To utilize the Smart 404 feature, you will need an active [OpenAI](https://platform.openai.com/signup) account or [Microsoft Azure](https://signup.azure.com/signup) account with OpenAI access and you will need to use [ElasticPress](https://github.com/10up/ElasticPress) 5.0.0+ and [Elasticsearch](https://www.elastic.co/elasticsearch) 7.0+.
6669
* To utilize the Term Cleanup feature, you will need an active [OpenAI](https://platform.openai.com/signup) account or [Microsoft Azure](https://signup.azure.com/signup) account with OpenAI access. For better performance, you will need [ElasticPress](https://github.com/10up/ElasticPress) 5.0.0+ and [Elasticsearch](https://www.elastic.co/elasticsearch) 7.0+.
6770

@@ -709,6 +712,27 @@ For more information, see https://docs.microsoft.com/en-us/azure/cognitive-servi
709712

710713
### 3. Use "Recommended Content" block to display recommended content on your website
711714

715+
## Run locally hosted LLMs
716+
717+
Some of the Features in ClassifAI can be set up to use locally hosted LLMs. This has the benefit of complete privacy and data control, as well as being able to be run without any cost. The trade-offs here are performance isn't as great and results may also be less accurate.
718+
719+
Right now, this is powered by Ollama, a tool that allows you to host and run LLMs locally. To set this up, follow the steps below:
720+
721+
### 1. Install Ollama
722+
723+
* [Install Ollama](https://ollama.com/) on your local machine.
724+
* By default Ollama runs at `http://localhost:11434/`.
725+
726+
### 2. Install the model
727+
728+
* Decide which models you want to use. This will depend on the Feature you are setting up. For instance, if you want to use Image Processing Features, ensure you install a Vision model. If you want to use the Classification Feature, ensure you install an Embedding model. All other Features should work with standard models.
729+
* Install the model locally by running `ollama pull <model-name>` in your terminal.
730+
731+
### 3. Configure Provider
732+
733+
* Once Ollama is running and the model is installed, you can proceed to use it as a Provider for the desired Feature.
734+
* Note that when using locally hosted LLMs, performance may be slower than using cloud-based services, especially for initial requests. Results may also be less accurate but these are the trade-offs for privacy and data control.
735+
712736
## WP CLI Commands
713737

714738
- Check out the [ClassifAI docs](https://10up.github.io/classifai/).

classifai.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Update URI: https://classifaiplugin.com
66
* Description: Enhance your WordPress content with Artificial Intelligence and Machine Learning services.
77
* Version: 3.3.0-dev
8-
* Requires at least: 6.5
8+
* Requires at least: 6.6
99
* Requires PHP: 7.4
1010
* Author: 10up
1111
* Author URI: https://10up.com

includes/Classifai/Features/Classification.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Classifai\Providers\Watson\NLU;
77
use Classifai\Providers\OpenAI\Embeddings as OpenAIEmbeddings;
88
use Classifai\Providers\Azure\Embeddings as AzureEmbeddings;
9+
use Classifai\Providers\Localhost\OllamaEmbeddings;
910
use WP_REST_Server;
1011
use WP_REST_Request;
1112
use WP_Error;
@@ -43,6 +44,7 @@ public function __construct() {
4344
NLU::ID => __( 'IBM Watson NLU', 'classifai' ),
4445
OpenAIEmbeddings::ID => __( 'OpenAI Embeddings', 'classifai' ),
4546
AzureEmbeddings::ID => __( 'Azure OpenAI Embeddings', 'classifai' ),
47+
OllamaEmbeddings::ID => __( 'Ollama', 'classifai' ),
4648
];
4749
}
4850

@@ -250,6 +252,7 @@ public function save( int $post_id, array $results, bool $link = true ) {
250252
break;
251253
case AzureEmbeddings::ID:
252254
case OpenAIEmbeddings::ID:
255+
case OllamaEmbeddings::ID:
253256
$results = $provider_instance->set_terms( $post_id, $results, $link );
254257
break;
255258
}
@@ -782,7 +785,7 @@ public function add_custom_settings_fields() {
782785
);
783786

784787
// Embeddings only supports existing terms.
785-
if ( isset( $settings['provider'] ) && ( OpenAIEmbeddings::ID === $settings['provider'] || AzureEmbeddings::ID === $settings['provider'] ) ) {
788+
if ( isset( $settings['provider'] ) && ( OpenAIEmbeddings::ID === $settings['provider'] || AzureEmbeddings::ID === $settings['provider'] || OllamaEmbeddings::ID === $settings['provider'] ) ) {
786789
unset( $method_options['recommended_terms'] );
787790
$settings['classification_method'] = 'existing_terms';
788791
}
@@ -879,7 +882,7 @@ public function sanitize_default_feature_settings( array $new_settings ): array
879882
$new_settings['classification_method'] = sanitize_text_field( $new_settings['classification_method'] ?? $settings['classification_method'] );
880883

881884
// Embeddings only supports existing terms.
882-
if ( isset( $new_settings['provider'] ) && ( OpenAIEmbeddings::ID === $new_settings['provider'] || AzureEmbeddings::ID === $new_settings['provider'] ) ) {
885+
if ( isset( $new_settings['provider'] ) && ( OpenAIEmbeddings::ID === $new_settings['provider'] || AzureEmbeddings::ID === $new_settings['provider'] || OllamaEmbeddings::ID === $settings['provider'] ) ) {
883886
$new_settings['classification_method'] = 'existing_terms';
884887
}
885888

includes/Classifai/Features/ContentResizing.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Classifai\Providers\OpenAI\ChatGPT;
88
use Classifai\Providers\Browser\ChromeAI;
99
use Classifai\Providers\XAI\Grok;
10+
use Classifai\Providers\Localhost\Ollama;
1011
use Classifai\Services\LanguageProcessing;
1112
use WP_REST_Server;
1213
use WP_REST_Request;
@@ -56,6 +57,7 @@ public function __construct() {
5657
OpenAI::ID => __( 'Azure OpenAI', 'classifai' ),
5758
Grok::ID => __( 'xAI Grok', 'classifai' ),
5859
ChromeAI::ID => __( 'Chrome AI (experimental)', 'classifai' ),
60+
Ollama::ID => __( 'Ollama', 'classifai' ),
5961
];
6062
}
6163

includes/Classifai/Features/DescriptiveTextGenerator.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Classifai\Providers\Azure\ComputerVision;
66
use Classifai\Providers\OpenAI\ChatGPT;
77
use Classifai\Providers\XAI\Grok;
8+
use Classifai\Providers\Localhost\OllamaMultimodal as OllamaMM;
89
use Classifai\Services\ImageProcessing;
910
use WP_REST_Server;
1011
use WP_REST_Request;
@@ -44,6 +45,7 @@ public function __construct() {
4445
ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ),
4546
ChatGPT::ID => __( 'OpenAI', 'classifai' ),
4647
Grok::ID => __( 'xAI Grok', 'classifai' ),
48+
OllamaMM::ID => __( 'Ollama', 'classifai' ),
4749
];
4850
}
4951

@@ -420,6 +422,15 @@ public function get_settings( $index = false ) {
420422
}
421423
}
422424

425+
if ( $settings && ! empty( $settings[ OllamaMM::ID ]['prompt'] ) ) {
426+
foreach ( $settings[ OllamaMM::ID ]['prompt'] as $key => $prompt ) {
427+
if ( 1 === intval( $prompt['original'] ) ) {
428+
$settings[ OllamaMM::ID ]['prompt'][ $key ]['prompt'] = $this->prompt;
429+
break;
430+
}
431+
}
432+
}
433+
423434
return $settings;
424435
}
425436

includes/Classifai/Features/ExcerptGeneration.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Classifai\Providers\OpenAI\ChatGPT;
99
use Classifai\Providers\Azure\OpenAI;
1010
use Classifai\Providers\Browser\ChromeAI;
11+
use Classifai\Providers\Localhost\Ollama;
1112
use WP_REST_Server;
1213
use WP_REST_Request;
1314
use WP_Error;
@@ -49,6 +50,7 @@ public function __construct() {
4950
OpenAI::ID => __( 'Azure OpenAI', 'classifai' ),
5051
Grok::ID => __( 'xAI Grok', 'classifai' ),
5152
ChromeAI::ID => __( 'Chrome AI (experimental)', 'classifai' ),
53+
Ollama::ID => __( 'Ollama', 'classifai' ),
5254
];
5355
}
5456

includes/Classifai/Features/ImageTagsGenerator.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Classifai\Features;
44

55
use Classifai\Providers\Azure\ComputerVision;
6+
use Classifai\Providers\Localhost\OllamaMultimodal as OllamaMM;
67
use Classifai\Services\ImageProcessing;
78
use WP_REST_Server;
89
use WP_REST_Request;
@@ -22,6 +23,20 @@ class ImageTagsGenerator extends Feature {
2223
*/
2324
const ID = 'feature_image_tags_generator';
2425

26+
// phpcs:disable Squiz.PHP.Heredoc.NotAllowed
27+
/**
28+
* Prompt for generating tags.
29+
*
30+
* @var string
31+
*/
32+
public $prompt = <<<EOD
33+
You are an assistant that generates image tags. You will be provided with an image and will generate a list of tags that best represent the image. Ensure the tags are short. Return at most the best 5 tags and return these in the following format:
34+
- Tag
35+
- Another tag
36+
- ...
37+
EOD;
38+
// phpcs:enable Squiz.PHP.Heredoc.NotAllowed
39+
2540
/**
2641
* Constructor.
2742
*/
@@ -34,6 +49,7 @@ public function __construct() {
3449
// Contains just the providers this feature supports.
3550
$this->supported_providers = [
3651
ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ),
52+
OllamaMM::ID => __( 'Ollama', 'classifai' ),
3753
];
3854
}
3955

@@ -47,6 +63,28 @@ public function setup() {
4763
add_action( 'rest_api_init', [ $this, 'register_endpoints' ] );
4864
}
4965

66+
/**
67+
* Returns the settings for the feature.
68+
*
69+
* @param string $index The index of the setting to return.
70+
* @return array|mixed
71+
*/
72+
public function get_settings( $index = false ) {
73+
$settings = parent::get_settings( $index );
74+
75+
// Keep using the original prompt from the codebase to allow updates.
76+
if ( $settings && ! empty( $settings[ OllamaMM::ID ]['prompt'] ) ) {
77+
foreach ( $settings[ OllamaMM::ID ]['prompt'] as $key => $prompt ) {
78+
if ( 1 === intval( $prompt['original'] ) ) {
79+
$settings[ OllamaMM::ID ]['prompt'][ $key ]['prompt'] = $this->prompt;
80+
break;
81+
}
82+
}
83+
}
84+
85+
return $settings;
86+
}
87+
5088
/**
5189
* Set up necessary hooks.
5290
*/

includes/Classifai/Features/ImageTextExtraction.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Classifai\Features;
44

55
use Classifai\Providers\Azure\ComputerVision;
6+
use Classifai\Providers\Localhost\OllamaMultimodal as OllamaMM;
67
use Classifai\Services\ImageProcessing;
78
use WP_REST_Server;
89
use WP_REST_Request;
@@ -23,6 +24,13 @@ class ImageTextExtraction extends Feature {
2324
*/
2425
const ID = 'feature_image_to_text_generator';
2526

27+
/**
28+
* Prompt for extracting text.
29+
*
30+
* @var string
31+
*/
32+
public $prompt = 'You are an assistant that extracts text from images. You will be provided with an image and will return whatever text is in the image. Return only the text, nothing else. If there is no text present, return the word none.';
33+
2634
/**
2735
* Constructor.
2836
*/
@@ -35,6 +43,7 @@ public function __construct() {
3543
// Contains just the providers this feature supports.
3644
$this->supported_providers = [
3745
ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ),
46+
OllamaMM::ID => __( 'Ollama', 'classifai' ),
3847
];
3948
}
4049

@@ -48,6 +57,28 @@ public function setup() {
4857
add_action( 'rest_api_init', [ $this, 'register_endpoints' ] );
4958
}
5059

60+
/**
61+
* Returns the settings for the feature.
62+
*
63+
* @param string $index The index of the setting to return.
64+
* @return array|mixed
65+
*/
66+
public function get_settings( $index = false ) {
67+
$settings = parent::get_settings( $index );
68+
69+
// Keep using the original prompt from the codebase to allow updates.
70+
if ( $settings && ! empty( $settings[ OllamaMM::ID ]['prompt'] ) ) {
71+
foreach ( $settings[ OllamaMM::ID ]['prompt'] as $key => $prompt ) {
72+
if ( 1 === intval( $prompt['original'] ) ) {
73+
$settings[ OllamaMM::ID ]['prompt'][ $key ]['prompt'] = $this->prompt;
74+
break;
75+
}
76+
}
77+
}
78+
79+
return $settings;
80+
}
81+
5182
/**
5283
* Set up necessary hooks.
5384
*/

includes/Classifai/Features/KeyTakeaways.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
namespace Classifai\Features;
44

55
use Classifai\Services\LanguageProcessing;
6-
use Classifai\Providers\XAI\Grok;
7-
use Classifai\Providers\GoogleAI\GeminiAPI;
86
use Classifai\Providers\OpenAI\ChatGPT;
97
use Classifai\Providers\Azure\OpenAI;
10-
use Classifai\Providers\Browser\ChromeAI;
8+
use Classifai\Providers\Localhost\Ollama;
119
use WP_REST_Server;
1210
use WP_REST_Request;
1311
use WP_Error;
@@ -47,6 +45,7 @@ public function __construct() {
4745
$this->supported_providers = [
4846
ChatGPT::ID => __( 'OpenAI ChatGPT', 'classifai' ),
4947
OpenAI::ID => __( 'Azure OpenAI', 'classifai' ),
48+
Ollama::ID => __( 'Ollama', 'classifai' ),
5049
];
5150
}
5251

0 commit comments

Comments
 (0)