From 089a7e7975fd16076904d39fb4fd530b9c1c1f8c Mon Sep 17 00:00:00 2001 From: Chip Wasson Date: Fri, 25 Apr 2025 10:45:29 -0600 Subject: [PATCH] Add claude module --- package.json | 1 + src/app.module.ts | 2 + src/claude/claude.controller.ts | 63 +++++++++++++++++++++++++++++ src/claude/claude.module.ts | 7 ++++ yarn.lock | 72 ++++++++++++++++++++++++++++++++- 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/claude/claude.controller.ts create mode 100644 src/claude/claude.module.ts diff --git a/package.json b/package.json index c462a7f..637b148 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@anthropic-ai/sdk": "^0.40.0", "@liaoliaots/nestjs-redis": "^9.0.5", "@nestjs/bull": "^10.0.1", "@nestjs/cache-manager": "^2.1.1", diff --git a/src/app.module.ts b/src/app.module.ts index fb1b811..a2c05b7 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -32,6 +32,7 @@ import { ContactModule } from './contact/contact.module'; import { HoardingModule } from './hoarding/hoarding.module'; import { NamesModule } from './names/names.module'; import { AssemblyAiModule } from './assembly-ai/assembly-ai.module'; +import { ClaudeModule } from './claude/claude.module'; @Module({ imports: [ @@ -110,6 +111,7 @@ import { AssemblyAiModule } from './assembly-ai/assembly-ai.module'; HoardingModule, NamesModule, AssemblyAiModule, + ClaudeModule, ], controllers: [AppController], providers: [ diff --git a/src/claude/claude.controller.ts b/src/claude/claude.controller.ts new file mode 100644 index 0000000..59acae1 --- /dev/null +++ b/src/claude/claude.controller.ts @@ -0,0 +1,63 @@ +import Anthropic from '@anthropic-ai/sdk'; +import { Body, Controller, Logger, Post, Res, Headers } from '@nestjs/common'; +import { ApiBody, ApiConsumes, ApiResponse } from '@nestjs/swagger'; +import { Response } from 'express'; + +@Controller('claude') +export class ClaudeController { + private readonly logger = new Logger(ClaudeController.name); + private readonly claude: Anthropic; + + constructor() { + this.claude = new Anthropic({ + apiKey: process.env.ANTHROPIC_API_KEY, + }); + } + + @Post('script-to-meeting-summary') + @ApiResponse({ + status: 200, + description: 'Converts a script to a meeting summary', + type: String, + }) + @ApiBody({ + description: 'Meeting transcript in script format', + type: String, + }) + @ApiConsumes('text/plain') + async scriptToMeetingSummary( + @Body() body: string, + @Res() res: Response, + @Headers('x-api-key') apiKey: string, + ) { + if (apiKey !== process.env.CLAUDE_ENDPOINT_KEY) { + this.logger.warn('Unauthorized access attempt'); + return res.status(401).send('Unauthorized'); + } + + const instructionPrefix = `You are being contacted by an API which is making this call. +Do not respond with anything other than the text that should be returned to the user. +The meeting transcript follows the line separating these instructions which is a line of = symbols. +Return a two paragraph or less summary, bullet points of the topics, and bullet points of any action items. + +========== +`; + + this.logger.log(`Received script to summarize: ${body.length} characters`); + + const response = await this.claude.messages.create({ + model: 'claude-3-7-sonnet-20250219', + messages: [ + { + role: 'user', + content: `${instructionPrefix}${body}`, + }, + ], + max_tokens: 1000, + }); + + return res + .header('Content-Type', 'text/plain; charset=utf-8') + .send((response.content[0] as any).text); + } +} diff --git a/src/claude/claude.module.ts b/src/claude/claude.module.ts new file mode 100644 index 0000000..4bafb92 --- /dev/null +++ b/src/claude/claude.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { ClaudeController } from './claude.controller'; + +@Module({ + controllers: [ClaudeController] +}) +export class ClaudeModule {} diff --git a/yarn.lock b/yarn.lock index ab00dbc..ceeef4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,6 +45,19 @@ ora "5.4.1" rxjs "7.8.1" +"@anthropic-ai/sdk@^0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.40.0.tgz#7a98cf38ec59ccb68aa23d098ffa97525fd265ae" + integrity sha512-NkIgtZxa4nWBzXtDuYe6Wumu1+cgEuEQtol8b1bVSjtS7Dvqex6iNdxaddV8PkRC8Z8mhbNw2m7j+9AD9uRRKw== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.25.7.tgz#438f2c524071531d643c6f0188e1e28f130cebc7" @@ -1171,6 +1184,14 @@ dependencies: "@types/express" "*" +"@types/node-fetch@^2.6.4": + version "2.6.12" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" + integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node@*": version "22.7.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" @@ -1183,6 +1204,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== +"@types/node@^18.11.18": + version "18.19.87" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.87.tgz#690f000cc51e3c7f48bc00f7e86fac6eb550b709" + integrity sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A== + dependencies: + undici-types "~5.26.4" + "@types/node@^20.3.1": version "20.16.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.11.tgz#9b544c3e716b1577ac12e70f9145193f32750b33" @@ -1568,6 +1596,13 @@ agent-base@6: dependencies: debug "4" +agentkeepalive@^4.2.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" + integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== + dependencies: + humanize-ms "^1.2.1" + airtable@^0.12.2: version "0.12.2" resolved "https://registry.yarnpkg.com/airtable/-/airtable-0.12.2.tgz#e53e66db86744f9bc684faa58881d6c9c12f0e6f" @@ -3086,6 +3121,11 @@ fork-ts-checker-webpack-plugin@9.0.2: semver "^7.3.5" tapable "^2.2.1" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + form-data@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" @@ -3095,6 +3135,14 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + formidable@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" @@ -3416,6 +3464,13 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4548,7 +4603,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.3, ms@^2.1.1, ms@^2.1.3: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4622,6 +4677,11 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-emoji@1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" @@ -5861,6 +5921,11 @@ uid@2.0.2: dependencies: "@lukeed/csprng" "^1.0.0" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" @@ -5989,6 +6054,11 @@ web-encoding@^1.1.5: optionalDependencies: "@zxing/text-encoding" "0.9.0" +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"