From 8bdb1481acc7d0a0d290b01728820c98cf681d7d Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Tue, 21 Jan 2025 16:26:52 +0000 Subject: [PATCH] feat(anthropic): add anthropic app with send message action --- .../src/apps/anthropic/actions/index.js | 3 + .../anthropic/actions/send-message/index.js | 124 ++++++++++++++++++ .../src/apps/anthropic/assets/favicon.svg | 8 ++ .../backend/src/apps/anthropic/auth/index.js | 34 +++++ .../apps/anthropic/auth/is-still-verified.js | 7 + .../apps/anthropic/auth/verify-credentials.js | 5 + .../common/add-anthropic-version-header.js | 7 + .../apps/anthropic/common/add-auth-header.js | 9 ++ .../src/apps/anthropic/dynamic-data/index.js | 3 + .../dynamic-data/list-models/index.js | 31 +++++ packages/backend/src/apps/anthropic/index.js | 21 +++ .../src/models/__snapshots__/app.test.js.snap | 1 + packages/docs/pages/.vitepress/config.js | 9 ++ packages/docs/pages/apps/anthropic/actions.md | 12 ++ .../docs/pages/apps/anthropic/connection.md | 8 ++ packages/docs/pages/guide/available-apps.md | 1 + .../docs/pages/public/favicons/anthropic.svg | 8 ++ 17 files changed, 291 insertions(+) create mode 100644 packages/backend/src/apps/anthropic/actions/index.js create mode 100644 packages/backend/src/apps/anthropic/actions/send-message/index.js create mode 100644 packages/backend/src/apps/anthropic/assets/favicon.svg create mode 100644 packages/backend/src/apps/anthropic/auth/index.js create mode 100644 packages/backend/src/apps/anthropic/auth/is-still-verified.js create mode 100644 packages/backend/src/apps/anthropic/auth/verify-credentials.js create mode 100644 packages/backend/src/apps/anthropic/common/add-anthropic-version-header.js create mode 100644 packages/backend/src/apps/anthropic/common/add-auth-header.js create mode 100644 packages/backend/src/apps/anthropic/dynamic-data/index.js create mode 100644 packages/backend/src/apps/anthropic/dynamic-data/list-models/index.js create mode 100644 packages/backend/src/apps/anthropic/index.js create mode 100644 packages/docs/pages/apps/anthropic/actions.md create mode 100644 packages/docs/pages/apps/anthropic/connection.md create mode 100644 packages/docs/pages/public/favicons/anthropic.svg diff --git a/packages/backend/src/apps/anthropic/actions/index.js b/packages/backend/src/apps/anthropic/actions/index.js new file mode 100644 index 00000000..92d67c2c --- /dev/null +++ b/packages/backend/src/apps/anthropic/actions/index.js @@ -0,0 +1,3 @@ +import sendMessage from './send-message/index.js'; + +export default [sendMessage]; diff --git a/packages/backend/src/apps/anthropic/actions/send-message/index.js b/packages/backend/src/apps/anthropic/actions/send-message/index.js new file mode 100644 index 00000000..3215bafb --- /dev/null +++ b/packages/backend/src/apps/anthropic/actions/send-message/index.js @@ -0,0 +1,124 @@ +import defineAction from '../../../../helpers/define-action.js'; + +const castFloatOrUndefined = (value) => { + return value === '' ? undefined : parseFloat(value); +}; + +export default defineAction({ + name: 'Send message', + key: 'send Message', + description: + 'Sends a structured list of input messages with text content, and the model will generate the next message in the conversation.', + arguments: [ + { + label: 'Model', + key: 'model', + type: 'dropdown', + required: true, + variables: true, + description: 'The model that will complete your prompt.', + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listModels', + }, + ], + }, + }, + { + label: 'Messages', + key: 'messages', + type: 'dynamic', + required: true, + description: 'Add or remove messages as needed', + value: [{ role: 'assistant', body: '' }], + fields: [ + { + label: 'Role', + key: 'role', + type: 'dropdown', + required: true, + options: [ + { + label: 'Assistant', + value: 'assistant', + }, + { + label: 'User', + value: 'user', + }, + ], + }, + { + label: 'Content', + key: 'content', + type: 'string', + required: true, + variables: true, + }, + ], + }, + { + label: 'Maximum tokens', + key: 'maxTokens', + type: 'string', + required: true, + variables: true, + description: 'The maximum number of tokens to generate before stopping.', + }, + { + label: 'Temperature', + key: 'temperature', + type: 'string', + required: false, + variables: true, + value: '1.0', + description: + 'Amount of randomness injected into the response. Defaults to 1.0. Ranges from 0.0 to 1.0. Use temperature closer to 0.0 for analytical / multiple choice, and closer to 1.0 for creative and generative tasks.', + }, + { + label: 'Stop sequences', + key: 'stopSequences', + type: 'dynamic', + required: false, + variables: true, + description: + 'Custom text sequences that will cause the model to stop generating.', + fields: [ + { + label: 'Stop sequence', + key: 'stopSequence', + type: 'string', + required: false, + variables: true, + }, + ], + }, + ], + + async run($) { + const nonEmptyStopSequences = $.step.parameters.stopSequences + .filter(({ stopSequence }) => stopSequence) + .map(({ stopSequence }) => stopSequence); + + const payload = { + model: $.step.parameters.model, + temperature: castFloatOrUndefined($.step.parameters.temperature), + max_tokens: castFloatOrUndefined($.step.parameters.maxTokens), + stop_sequences: nonEmptyStopSequences, + messages: $.step.parameters.messages.map((message) => ({ + role: message.role, + content: message.content, + })), + }; + + const { data } = await $.http.post('/v1/messages', payload); + + $.setActionItem({ + raw: data, + }); + }, +}); diff --git a/packages/backend/src/apps/anthropic/assets/favicon.svg b/packages/backend/src/apps/anthropic/assets/favicon.svg new file mode 100644 index 00000000..affdadef --- /dev/null +++ b/packages/backend/src/apps/anthropic/assets/favicon.svg @@ -0,0 +1,8 @@ + + + Anthropic + + + + + \ No newline at end of file diff --git a/packages/backend/src/apps/anthropic/auth/index.js b/packages/backend/src/apps/anthropic/auth/index.js new file mode 100644 index 00000000..947c8f8a --- /dev/null +++ b/packages/backend/src/apps/anthropic/auth/index.js @@ -0,0 +1,34 @@ +import verifyCredentials from './verify-credentials.js'; +import isStillVerified from './is-still-verified.js'; + +export default { + fields: [ + { + key: 'screenName', + label: 'Screen Name', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: + 'Screen name of your connection to be used on Automatisch UI.', + clickToCopy: false, + }, + { + key: 'apiKey', + label: 'API Key', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: 'Anthropic AI API key of your account.', + docUrl: 'https://automatisch.io/docs/anthropic#api-key', + clickToCopy: false, + }, + ], + + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/anthropic/auth/is-still-verified.js b/packages/backend/src/apps/anthropic/auth/is-still-verified.js new file mode 100644 index 00000000..531fc23a --- /dev/null +++ b/packages/backend/src/apps/anthropic/auth/is-still-verified.js @@ -0,0 +1,7 @@ +const isStillVerified = async ($) => { + await $.http.get('/v1/models'); + + return true; +}; + +export default isStillVerified; diff --git a/packages/backend/src/apps/anthropic/auth/verify-credentials.js b/packages/backend/src/apps/anthropic/auth/verify-credentials.js new file mode 100644 index 00000000..7f43f884 --- /dev/null +++ b/packages/backend/src/apps/anthropic/auth/verify-credentials.js @@ -0,0 +1,5 @@ +const verifyCredentials = async ($) => { + await $.http.get('/v1/models'); +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/anthropic/common/add-anthropic-version-header.js b/packages/backend/src/apps/anthropic/common/add-anthropic-version-header.js new file mode 100644 index 00000000..9ff91ce2 --- /dev/null +++ b/packages/backend/src/apps/anthropic/common/add-anthropic-version-header.js @@ -0,0 +1,7 @@ +const addAuthHeader = ($, requestConfig) => { + requestConfig.headers['anthropic-version'] = '2023-06-01'; + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/anthropic/common/add-auth-header.js b/packages/backend/src/apps/anthropic/common/add-auth-header.js new file mode 100644 index 00000000..01bcae10 --- /dev/null +++ b/packages/backend/src/apps/anthropic/common/add-auth-header.js @@ -0,0 +1,9 @@ +const addAuthHeader = ($, requestConfig) => { + if ($.auth.data?.apiKey) { + requestConfig.headers['x-api-key'] = $.auth.data.apiKey; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/anthropic/dynamic-data/index.js b/packages/backend/src/apps/anthropic/dynamic-data/index.js new file mode 100644 index 00000000..6db48046 --- /dev/null +++ b/packages/backend/src/apps/anthropic/dynamic-data/index.js @@ -0,0 +1,3 @@ +import listModels from './list-models/index.js'; + +export default [listModels]; diff --git a/packages/backend/src/apps/anthropic/dynamic-data/list-models/index.js b/packages/backend/src/apps/anthropic/dynamic-data/list-models/index.js new file mode 100644 index 00000000..f47f5fb9 --- /dev/null +++ b/packages/backend/src/apps/anthropic/dynamic-data/list-models/index.js @@ -0,0 +1,31 @@ +export default { + name: 'List models', + key: 'listModels', + + async run($) { + const models = { + data: [], + }; + + const params = { + limit: 999, + }; + + let hasMore = false; + + do { + const { data } = await $.http.get('/v1/models', { params }); + params.after_id = data.last_id; + hasMore = data.has_more; + + for (const base of data.data) { + models.data.push({ + value: base.id, + name: base.display_name, + }); + } + } while (hasMore); + + return models; + }, +}; diff --git a/packages/backend/src/apps/anthropic/index.js b/packages/backend/src/apps/anthropic/index.js new file mode 100644 index 00000000..65f29ba2 --- /dev/null +++ b/packages/backend/src/apps/anthropic/index.js @@ -0,0 +1,21 @@ +import defineApp from '../../helpers/define-app.js'; +import addAuthHeader from './common/add-auth-header.js'; +import addAnthropicVersionHeader from './common/add-anthropic-version-header.js'; +import auth from './auth/index.js'; +import actions from './actions/index.js'; +import dynamicData from './dynamic-data/index.js'; + +export default defineApp({ + name: 'Anthropic', + key: 'anthropic', + baseUrl: 'https://anthropic.com', + apiBaseUrl: 'https://api.anthropic.com', + iconUrl: '{BASE_URL}/apps/anthropic/assets/favicon.svg', + authDocUrl: '{DOCS_URL}/apps/anthropic/connection', + primaryColor: '#181818', + supportsConnections: true, + beforeRequest: [addAuthHeader, addAnthropicVersionHeader], + auth, + actions, + dynamicData, +}); diff --git a/packages/backend/src/models/__snapshots__/app.test.js.snap b/packages/backend/src/models/__snapshots__/app.test.js.snap index 38324be3..7fb9a297 100644 --- a/packages/backend/src/models/__snapshots__/app.test.js.snap +++ b/packages/backend/src/models/__snapshots__/app.test.js.snap @@ -3,6 +3,7 @@ exports[`App model > list should have list of applications keys 1`] = ` [ "airtable", + "anthropic", "appwrite", "azure-openai", "carbone", diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 0fd2511b..1508b069 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -41,6 +41,15 @@ export default defineConfig({ { text: 'Connection', link: '/apps/airtable/connection' }, ], }, + { + text: 'Anthropic', + collapsible: true, + collapsed: true, + items: [ + { text: 'Actions', link: '/apps/anthropic/actions' }, + { text: 'Connection', link: '/apps/anthropic/connection' }, + ], + }, { text: 'Appwrite', collapsible: true, diff --git a/packages/docs/pages/apps/anthropic/actions.md b/packages/docs/pages/apps/anthropic/actions.md new file mode 100644 index 00000000..6e8d4cf7 --- /dev/null +++ b/packages/docs/pages/apps/anthropic/actions.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/anthropic.svg +items: + - name: Send message + desc: Sends a structured list of input messages with text content, and the model will generate the next message in the conversation. +--- + + + + diff --git a/packages/docs/pages/apps/anthropic/connection.md b/packages/docs/pages/apps/anthropic/connection.md new file mode 100644 index 00000000..92330b8b --- /dev/null +++ b/packages/docs/pages/apps/anthropic/connection.md @@ -0,0 +1,8 @@ +# Anthropic + +1. Go to [API Keys page](https://console.anthropic.com/settings/keys) on Anthropic. +2. Create a new key. +3. Paste the key into the `API Key` field in Automatisch. +4. Write any screen name to be displayed in Automatisch. +5. Click `Save`. +6. Start using Anthropic integration with Automatisch! diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index 4be6447b..6f010074 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -3,6 +3,7 @@ The following integrations are currently supported by Automatisch. - [Airtable](/apps/airtable/actions) +- [Anthropic](/apps/anthropic/actions) - [Appwrite](/apps/appwrite/triggers) - [Carbone](/apps/carbone/actions) - [ClickUp](/apps/clickup/triggers) diff --git a/packages/docs/pages/public/favicons/anthropic.svg b/packages/docs/pages/public/favicons/anthropic.svg new file mode 100644 index 00000000..affdadef --- /dev/null +++ b/packages/docs/pages/public/favicons/anthropic.svg @@ -0,0 +1,8 @@ + + + Anthropic + + + + + \ No newline at end of file