prompt-completion.ts•5.65 kB
import { straicoAuth } from '../../index';
import { createAction, Property } from '@activepieces/pieces-framework';
import {
  AuthenticationType,
  HttpMethod,
  httpClient,
  propsValidation,
} from '@activepieces/pieces-common';
import { z } from 'zod';
import { baseUrlv1 } from '../common/common';
export const promptCompletion = createAction({
  auth: straicoAuth,
  name: 'prompt_completion',
  displayName: 'Ask AI',
  description:
    'Enables users to generate prompt completion based on a specified model.',
  props: {
    model: Property.Dropdown({
      displayName: 'Model',
      required: true,
      description:
        'The model which will generate the completion. Some models are suitable for natural language tasks, others specialize in code.',
      refreshers: [],
      defaultValue: 'openai/gpt-4o-mini',
      options: async ({ auth }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'Enter your API key first',
            options: [],
          };
        }
        try {
          const models = await httpClient.sendRequest<{
            data: {
              chat: Array<{
                name: string;
                model: string;
              }>;
            };
          }>({
            url: `${baseUrlv1}/models`,
            method: HttpMethod.GET,
            authentication: {
              type: AuthenticationType.BEARER_TOKEN,
              token: auth as string,
            },
          });
          return {
            disabled: false,
            options:
              models.body?.data?.chat?.map((model) => {
                return {
                  label: model.name,
                  value: model.model,
                };
              }) || [],
          };
        } catch (error) {
          return {
            disabled: true,
            options: [],
            placeholder: "Couldn't load models, API key is invalid",
          };
        }
      },
    }),
    prompt: Property.LongText({
      displayName: 'Prompt',
      required: true,
      description: 'The prompt text for which completions are requested',
    }),
    fileUrls: Property.Array({
      displayName: 'File URLs',
      required: false,
      description: 'URLs of files to be processed by the model (maximum 4 URLs), previously uploaded via the File Upload endpoint',
    }),
    youtubeUrls: Property.Array({
      displayName: 'YouTube URLs',
      required: false,
      description: 'URLs of YouTube videos to be processed by the model (maximum 4 URLs)',
    }),
    imageUrls: Property.Array({
      displayName: 'Image URLs',
      required: false,
      description: 'URLs of images to be processed by the model, previously uploaded via the File Upload endpoint',
    }),
    displayTranscripts: Property.Checkbox({
      displayName: 'Display Transcripts',
      required: false,
      description: 'If true, returns transcripts of the files. Note: Either File URLs or YouTube URLs are required when this is enabled',
      defaultValue: false,
    }),
    temperature: Property.Number({
      displayName: 'Temperature',
      required: false,
      description: 'This setting influences the variety in the model\'s responses (0-2)',
    }),
    maxTokens: Property.Number({
      displayName: 'Max Tokens',
      required: false,
      description: 'Set the limit for the number of tokens the model can generate in response',
    }),
  },
  async run({ auth, propsValue }) {
    // Validate URLs length and displayTranscripts requirements
    await propsValidation.validateZod(propsValue, {
      fileUrls: z.array(z.string()).max(4, 'Maximum 4 file URLs allowed'),
      youtubeUrls: z.array(z.string()).max(4, 'Maximum 4 YouTube URLs allowed'),
    });
    
    // Validate that displayTranscripts is only true when fileUrls or youtubeUrls are provided
    if (propsValue.displayTranscripts === true && 
        (!propsValue.fileUrls?.length && !propsValue.youtubeUrls?.length)) {
      throw new Error('Either File URLs or YouTube URLs are required when Display Transcripts is enabled');
    }
    const response = await httpClient.sendRequest<{
      data: {
        completions: {
          [key: string]: {
            completion: {
              choices: Array<{
                message: {
                  content: string;
                };
              }>;
            };
          };
        };
        transcripts: Array<{ text: string }>;
      };
    }>({
      url: `${baseUrlv1}/prompt/completion`,
      method: HttpMethod.POST,
      authentication: {
        type: AuthenticationType.BEARER_TOKEN,
        token: auth as string,
      },
      body: {
        models: [propsValue.model],
        message: propsValue.prompt,
        ...(propsValue.fileUrls?.length ? { file_urls: propsValue.fileUrls } : {}),
        ...(propsValue.youtubeUrls?.length ? { youtube_urls: propsValue.youtubeUrls } : {}),
        ...(propsValue.imageUrls?.length ? { images: propsValue.imageUrls } : {}),
        ...((propsValue.displayTranscripts !== undefined && (propsValue.fileUrls?.length || propsValue.youtubeUrls?.length)) ? 
            { display_transcripts: propsValue.displayTranscripts } : {}),
        ...(propsValue.temperature !== undefined ? { temperature: propsValue.temperature } : {}),
        ...(propsValue.maxTokens !== undefined ? { max_tokens: propsValue.maxTokens } : {}),
      },
    });
    const modelResponse = response.body.data.completions[propsValue.model];
    return {
      content: modelResponse.completion.choices[0].message.content,
      transcripts: response.body.data?.transcripts || []
    };
  },
});