Skip to content

Commit

Permalink
chore: add proxy for content generation and usage in wp options
Browse files Browse the repository at this point in the history
  • Loading branch information
Soare-Robert-Daniel committed Aug 10, 2023
1 parent d9049e1 commit 44e4afb
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 19 deletions.
50 changes: 50 additions & 0 deletions inc/plugins/class-options-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,56 @@ function ( $item ) {
'default' => '',
)
);

register_setting(
'themeisle_blocks_settings',
'themeisle_otter_ai_usage',
array(
'type' => 'object',
'description' => __( 'Usage of Otter AI features.', 'otter-blocks' ),
'show_in_test' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'usage_count' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'key' => array(
'type' => 'string',
),
'value' => array(
'type' => 'string',
),
),
),
'default' => array(),
),
'prompts' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'key' => array(
'type' => 'string',
),
'values' => array(
'type' => 'array',
'items' => array(
'type' => 'string',
),
),
),
),
'default' => array(),
),
),
),
),
'default' => array(),
)
);
}

/**
Expand Down
134 changes: 134 additions & 0 deletions inc/server/class-prompt-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,140 @@ public function register_routes() {
)
);

register_rest_route(
$namespace,
'/generate',
array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'forward_prompt' ),
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
),
)
);
}

/**
* Forward the prompt to OpenAI API.
*
* @param \WP_REST_Request $request Request object.
* @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response
*/
public function forward_prompt( $request ) {
$open_ai_endpoint = 'https://api.openai.com/v1/chat/completions';

// Get the body from request and decode it.
$body = $request->get_body();
$body = json_decode( $body, true );

$api_key = get_option( 'themeisle_open_ai_api_key' );

// Extract the data from keys that start with 'otter_'.
$otter_data = array_filter(
$body,
function ( $key ) {
return 0 === strpos( $key, 'otter_' );
},
ARRAY_FILTER_USE_KEY
);

// Remove the values which keys start with 'otter_'.
$body = array_diff_key( $body, $otter_data );


if ( isset( $otter_data['otter_used_action'] ) && isset( $otter_data['otter_user_content'] ) ) {

$action = $otter_data['otter_used_action'];
$user_content = $otter_data['otter_user_content'];

$usage = get_option( 'themeisle_otter_ai_usage' );

if ( ! is_array( $usage ) ) {
$usage = array(
'usage_count' => array(),
'prompts' => array(),
);
}

if ( ! is_array( $usage['usage_count'] ) ) {
$usage['usage_count'] = array();
}

if ( ! is_array( $usage['prompts'] ) ) {
$usage['prompts'] = array();
}

$is_missing = true;

foreach ( $usage['usage_count'] as &$u ) {
if ( isset( $u['key'] ) && $u['key'] === $action ) {
$u['value']++;
$is_missing = false;
}
}

unset( $u );

if ( $is_missing ) {
$usage['usage_count'][] = array(
'key' => $action,
'value' => 1,
);
}

$is_missing = true;

foreach ( $usage['prompts'] as &$u ) {
if ( isset( $u['key'] ) && $u['key'] === $action ) {
$u['values'][] = $user_content;
$is_missing = false;

// Keep only the last 10 prompts.
if ( count( $u['values'] ) > 10 ) {
array_shift( $u['values'] );
}
}
}

unset( $u );

if ( $is_missing ) {
$usage['prompts'][] = array(
'key' => $action,
'values' => array( $user_content ),
);
}

update_option( 'themeisle_otter_ai_usage', $usage );
}


$response = wp_remote_post(
$open_ai_endpoint,
array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( $body ),
)
);

if ( is_wp_error( $response ) ) {
return $response;
}

$body = wp_remote_retrieve_body( $response );
$body = json_decode( $body, true );

if ( json_last_error() !== JSON_ERROR_NONE ) {
return new \WP_Error( 'rest_invalid_json', __( 'Invalid JSON body.', 'otter-blocks' ), array( 'status' => 400 ) );
}

return new \WP_REST_Response( $body, wp_remote_retrieve_response_code( $response ) );
}

/**
Expand Down
4 changes: 1 addition & 3 deletions src/blocks/blocks/form/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ import Inspector from './inspector.js';
import Placeholder from './placeholder.js';
import { useResponsiveAttributes } from '../../helpers/utility-hooks';
import { renderBoxOrNumWithUnit, _cssBlock, _px, findInnerBlocks } from '../../helpers/helper-functions';
import PromptPlaceholder from '../../components/prompt';
import { parseFormPromptResponseToBlocks, sendPromptToOpenAI } from '../../helpers/prompt';
import { aiGeneration, formAiGeneration } from '../../helpers/icons';
import { aiGeneration } from '../../helpers/icons';

const { attributes: defaultAttributes } = metadata;

Expand Down
4 changes: 4 additions & 0 deletions src/blocks/components/prompt/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
align-items: center;
justify-content: center;
margin-top: 10px;

:not(:last-child) {
margin-bottom: 10px;
}

.prompt-input__input__container {
display: flex;
Expand Down
8 changes: 6 additions & 2 deletions src/blocks/components/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ const PromptPlaceholder = ( props: PromptPlaceholderProps ) => {

// TODO: refactor this into a more reusable way
if ( 'textTransformation' === promptID ) {
embeddedPrompt = injectActionIntoPrompt( embeddedPrompt, 'otter_action_prompt' );
const action = embeddedPrompt?.['otter_action_prompt'] ?? '';
embeddedPrompt = injectActionIntoPrompt( embeddedPrompt, action );
}

if ( ! apiKey ) {
Expand All @@ -221,7 +222,10 @@ const PromptPlaceholder = ( props: PromptPlaceholderProps ) => {

const sendPrompt = regenerate ? sendPromptToOpenAIWithRegenerate : sendPromptToOpenAI;

sendPrompt?.( value, apiKey, embeddedPrompt ).then ( ( data ) => {
sendPrompt?.( value, apiKey, embeddedPrompt, {
'otter_used_action': 'textTransformation' === promptID ? 'textTransformation::otter_action_prompt' : ( promptID ?? '' ),
'otter_user_content': value
}).then ( ( data ) => {
if ( data?.error ) {
setGenerationStatus( 'error' );
setShowError( true );
Expand Down
18 changes: 6 additions & 12 deletions src/blocks/helpers/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ function promptRequestBuilder( settings?: OpenAiSettings ) {
stream: false
};

return async( prompt: string, apiKey: string, embeddedPrompt: PromptData ) => {
// TODO: remove the apiKey from the function definition.
return async( prompt: string, apiKey: string, embeddedPrompt: PromptData, metadata: Record<string, string> ) => {
const body = {
...embeddedPrompt,
messages: embeddedPrompt.messages.map( ( message ) => {
Expand All @@ -112,7 +113,6 @@ function promptRequestBuilder( settings?: OpenAiSettings ) {
})
};


function removeOtterKeys( obj ) {
for ( let key in obj ) {
if ( key.startsWith( 'otter_' ) ) {
Expand All @@ -123,23 +123,17 @@ function promptRequestBuilder( settings?: OpenAiSettings ) {
}

try {
const response = await fetch( 'https://api.openai.com/v1/chat/completions', {
const response = await apiFetch({
path: addQueryArgs( '/otter/v1/generate', {}),
method: 'POST',
headers: {
'Content-Type': 'application/json',

// The Authorization header contains your API key
Authorization: `Bearer ${apiKey}`
},
body: JSON.stringify({
...( metadata ?? {}),
...( removeOtterKeys( body ) ),
...settings
})
});

// TODO: Make a proxy in PHP

return await response.json() as ChatResponse;
return response as ChatResponse;
} catch ( e ) {
return {
error: {
Expand Down
12 changes: 10 additions & 2 deletions src/blocks/plugins/ai-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ const withConditions = createHigherOrderComponent( BlockEdit => {

if ( 'loaded' === status && ! apiKey ) {
if ( getOption( openAiAPIKeyName ) ) {
console.log( getOption( openAiAPIKeyName ) );
setApiKey( getOption( openAiAPIKeyName ) );
}
}
Expand Down Expand Up @@ -166,7 +165,11 @@ const withConditions = createHigherOrderComponent( BlockEdit => {
injectActionIntoPrompt(
embeddedPrompt,
action
)
),
{
'otter_used_action': `textTransformation::${ actionKey }`,
'otter_user_content': content
}
).then( ( response ) => {
if ( response.error ) {
setDisplayError( response.error );
Expand Down Expand Up @@ -269,6 +272,11 @@ const withConditions = createHigherOrderComponent( BlockEdit => {
{ __( 'Casual', 'otter-blocks' ) }
</ActionMenuItem>
</MenuGroup>
<MenuGroup>
<ActionMenuItem actionKey='otter_action_prompt' callback={onClose}>
{ __( 'Use as prompt', 'otter-blocks' ) }
</ActionMenuItem>
</MenuGroup>
<MenuGroup>
<ExternalLink className='o-menu-item-alignment' href="https://docs.themeisle.com/collection/1563-otter---page-builder-blocks-extensions" target="_blank" rel="noopener noreferrer">
{
Expand Down

0 comments on commit 44e4afb

Please sign in to comment.